diff options
| author | Loris Cro <kappaloris@gmail.com> | 2023-06-18 09:06:40 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-06-18 09:06:40 +0200 |
| commit | 216ef10dc471e4db60a30208be178d6c59efeaaf (patch) | |
| tree | 8c239dab283ae9cb3b7fe099bae240bcc53f894e /src/Sema.zig | |
| parent | 0fc1d396495c1ab482197021dedac8bea3f9401c (diff) | |
| parent | 729a051e9e38674233190aea23c0ac8c134f2d67 (diff) | |
| download | zig-216ef10dc471e4db60a30208be178d6c59efeaaf.tar.gz zig-216ef10dc471e4db60a30208be178d6c59efeaaf.zip | |
Merge branch 'master' into autodoc-searchkey
Diffstat (limited to 'src/Sema.zig')
| -rw-r--r-- | src/Sema.zig | 20623 |
1 files changed, 11533 insertions, 9090 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index df199be97c..36fe5a6ee8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11,13 +11,9 @@ gpa: Allocator, /// Points to the temporary arena allocator of the Sema. /// This arena will be cleared when the sema is destroyed. arena: Allocator, -/// Points to the arena allocator for the owner_decl. -/// This arena will persist until the decl is invalidated. -perm_arena: Allocator, code: Zir, air_instructions: std.MultiArrayList(Air.Inst) = .{}, air_extra: std.ArrayListUnmanaged(u32) = .{}, -air_values: std.ArrayListUnmanaged(Value) = .{}, /// Maps ZIR to AIR. inst_map: InstMap = .{}, /// When analyzing an inline function call, owner_decl is the Decl of the caller @@ -28,10 +24,12 @@ owner_decl_index: Decl.Index, /// For an inline or comptime function call, this will be the root parent function /// which contains the callsite. Corresponds to `owner_decl`. owner_func: ?*Module.Fn, +owner_func_index: Module.Fn.OptionalIndex, /// The function this ZIR code is the body of, according to the source code. /// This starts out the same as `owner_func` and then diverges in the case of /// an inline or comptime function call. func: ?*Module.Fn, +func_index: Module.Fn.OptionalIndex, /// Used to restore the error return trace when returning a non-error from a function. error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none, /// When semantic analysis needs to know the return type of the function whose body @@ -65,12 +63,15 @@ comptime_args_fn_inst: Zir.Inst.Index = 0, /// to use this instead of allocating a fresh one. This avoids an unnecessary /// extra hash table lookup in the `monomorphed_funcs` set. /// Sema will set this to null when it takes ownership. -preallocated_new_func: ?*Module.Fn = null, -/// The key is `constant` AIR instructions to types that must be fully resolved -/// after the current function body analysis is done. -/// TODO: after upgrading to use InternPool change the key here to be an -/// InternPool value index. -types_to_resolve: std.ArrayListUnmanaged(Air.Inst.Ref) = .{}, +preallocated_new_func: Module.Fn.OptionalIndex = .none, +/// The key is types that must be fully resolved prior to machine code +/// generation pass. Types are added to this set when resolving them +/// immediately could cause a dependency loop, but they do need to be resolved +/// before machine code generation passes process the AIR. +/// It would work fine if this were an array list instead of an array hash map. +/// I chose array hash map with the intention to save time by omitting +/// duplicates. +types_to_resolve: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}, /// These are lazily created runtime blocks from block_inline instructions. /// They are created when an break_inline passes through a runtime condition, because /// Sema must convert comptime control flow to runtime control flow, which means @@ -84,12 +85,22 @@ is_generic_instantiation: bool = false, /// function types will emit generic poison instead of a partial type. no_partial_func_ty: bool = false, -unresolved_inferred_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{}, +/// The temporary arena is used for the memory of the `InferredAlloc` values +/// here so the values can be dropped without any cleanup. +unresolved_inferred_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, InferredAlloc) = .{}, + +/// Indices of comptime-mutable decls created by this Sema. These decls' values +/// should be interned after analysis completes, as they may refer to memory in +/// the Sema arena. +/// TODO: this is a workaround for memory bugs triggered by the removal of +/// Decl.value_arena. A better solution needs to be found. Probably this will +/// involve transitioning comptime-mutable memory away from using Decls at all. +comptime_mutable_decls: *std.ArrayList(Decl.Index), const std = @import("std"); const math = std.math; const mem = std.mem; -const Allocator = std.mem.Allocator; +const Allocator = mem.Allocator; const assert = std.debug.assert; const log = std.log.scoped(.sema); @@ -114,6 +125,7 @@ const Package = @import("Package.zig"); const crash_report = @import("crash_report.zig"); const build_options = @import("build_options"); const Compilation = @import("Compilation.zig"); +const InternPool = @import("InternPool.zig"); pub const default_branch_quota = 1000; pub const default_reference_trace_len = 2; @@ -226,7 +238,7 @@ pub const Block = struct { sema: *Sema, /// The namespace to use for lookups from this source block /// When analyzing fields, this is different from src_decl.src_namespace. - namespace: *Namespace, + namespace: Namespace.Index, /// The AIR instructions generated for this block. instructions: std.ArrayListUnmanaged(Air.Inst.Index), // `param` instructions are collected here to be used by the `func` instruction. @@ -252,7 +264,6 @@ pub const Block = struct { // TODO is_comptime and comptime_reason should probably be merged together. is_comptime: bool, is_typeof: bool = false, - is_coerce_result_ptr: bool = false, /// Keep track of the active error return trace index around blocks so that we can correctly /// pop the error trace upon block exit. @@ -266,12 +277,6 @@ pub const Block = struct { c_import_buf: ?*std.ArrayList(u8) = null, - /// type of `err` in `else => |err|` - switch_else_err_ty: ?Type = null, - - /// Value for switch_capture in an inline case - inline_case_capture: Air.Inst.Ref = .none, - const ComptimeReason = union(enum) { c_import: struct { block: *Block, @@ -286,6 +291,7 @@ pub const Block = struct { fn explain(cr: ComptimeReason, sema: *Sema, msg: ?*Module.ErrorMsg) !void { const parent = msg orelse return; + const mod = sema.mod; const prefix = "expression is evaluated at comptime because "; switch (cr) { .c_import => |ci| { @@ -293,23 +299,23 @@ pub const Block = struct { }, .comptime_ret_ty => |rt| { const src_loc = if (try sema.funcDeclSrc(rt.func)) |fn_decl| blk: { - var src_loc = fn_decl.srcLoc(); + var src_loc = fn_decl.srcLoc(mod); src_loc.lazy = .{ .node_offset_fn_type_ret_ty = 0 }; break :blk src_loc; } else blk: { - const src_decl = sema.mod.declPtr(rt.block.src_decl); - break :blk rt.func_src.toSrcLoc(src_decl); + const src_decl = mod.declPtr(rt.block.src_decl); + break :blk rt.func_src.toSrcLoc(src_decl, mod); }; - if (rt.return_ty.tag() == .generic_poison) { - return sema.mod.errNoteNonLazy(src_loc, parent, prefix ++ "the generic function was instantiated with a comptime-only return type", .{}); + if (rt.return_ty.isGenericPoison()) { + return mod.errNoteNonLazy(src_loc, parent, prefix ++ "the generic function was instantiated with a comptime-only return type", .{}); } - try sema.mod.errNoteNonLazy( + try mod.errNoteNonLazy( src_loc, parent, prefix ++ "the function returns a comptime-only type '{}'", - .{rt.return_ty.fmt(sema.mod)}, + .{rt.return_ty.fmt(mod)}, ); - try sema.explainWhyTypeIsComptime(rt.block, rt.func_src, parent, src_loc, rt.return_ty); + try sema.explainWhyTypeIsComptime(parent, src_loc, rt.return_ty); }, } } @@ -385,7 +391,6 @@ pub const Block = struct { .want_safety = parent.want_safety, .float_mode = parent.float_mode, .c_import_buf = parent.c_import_buf, - .switch_else_err_ty = parent.switch_else_err_ty, .error_return_trace_index = parent.error_return_trace_index, }; } @@ -399,8 +404,8 @@ pub const Block = struct { }; } - pub fn getFileScope(block: *Block) *Module.File { - return block.namespace.file_scope; + pub fn getFileScope(block: *Block, mod: *Module) *Module.File { + return mod.namespacePtr(block.namespace).file_scope; } fn addTy( @@ -585,13 +590,18 @@ pub const Block = struct { } fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref { + const sema = block.sema; + const mod = sema.mod; return block.addInst(.{ .tag = if (block.float_mode == .Optimized) .cmp_vector_optimized else .cmp_vector, .data = .{ .ty_pl = .{ - .ty = try block.sema.addType( - try Type.vector(block.sema.arena, block.sema.typeOf(lhs).vectorLen(), Type.bool), + .ty = try sema.addType( + try mod.vectorType(.{ + .len = sema.typeOf(lhs).vectorLen(mod), + .child = .bool_type, + }), ), - .payload = try block.sema.addExtra(Air.VectorCmp{ + .payload = try sema.addExtra(Air.VectorCmp{ .lhs = lhs, .rhs = rhs, .op = Air.VectorCmp.encodeOp(cmp_op), @@ -685,29 +695,20 @@ pub const Block = struct { pub fn startAnonDecl(block: *Block) !WipAnonDecl { return WipAnonDecl{ .block = block, - .new_decl_arena = std.heap.ArenaAllocator.init(block.sema.gpa), .finished = false, }; } pub const WipAnonDecl = struct { block: *Block, - new_decl_arena: std.heap.ArenaAllocator, finished: bool, - pub fn arena(wad: *WipAnonDecl) Allocator { - return wad.new_decl_arena.allocator(); - } - pub fn deinit(wad: *WipAnonDecl) void { - if (!wad.finished) { - wad.new_decl_arena.deinit(); - } wad.* = undefined; } /// `alignment` value of 0 means to use ABI alignment. - pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: u32) !Decl.Index { + pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: u64) !Decl.Index { const sema = wad.block.sema; // Do this ahead of time because `createAnonymousDecl` depends on calling // `type.hasRuntimeBits()`. @@ -717,10 +718,11 @@ pub const Block = struct { .val = val, }); const new_decl = sema.mod.declPtr(new_decl_index); - new_decl.@"align" = alignment; + // TODO: migrate Decl alignment to use `InternPool.Alignment` + new_decl.@"align" = @intCast(u32, alignment); errdefer sema.mod.abortAnonDecl(new_decl_index); - try new_decl.finalizeNewArena(&wad.new_decl_arena); wad.finished = true; + try sema.mod.finalizeAnonDecl(new_decl_index); return new_decl_index; } }; @@ -737,11 +739,27 @@ const LabeledBlock = struct { } }; +/// The value stored in the inferred allocation. This will go into +/// peer type resolution. This is stored in a separate list so that +/// the items are contiguous in memory and thus can be passed to +/// `Module.resolvePeerTypes`. +const InferredAlloc = struct { + prongs: std.MultiArrayList(struct { + /// The dummy instruction used as a peer to resolve the type. + /// Although this has a redundant type with placeholder, this is + /// needed in addition because it may be a constant value, which + /// affects peer type resolution. + stored_inst: Air.Inst.Ref, + /// The bitcast instruction used as a placeholder when the + /// new result pointer type is not yet known. + placeholder: Air.Inst.Index, + }) = .{}, +}; + pub fn deinit(sema: *Sema) void { const gpa = sema.gpa; sema.air_instructions.deinit(gpa); sema.air_extra.deinit(gpa); - sema.air_values.deinit(gpa); sema.inst_map.deinit(gpa); sema.decl_val_table.deinit(gpa); sema.types_to_resolve.deinit(gpa); @@ -824,7 +842,7 @@ pub fn analyzeBodyBreak( else => |e| return e, }; if (block.instructions.items.len != 0 and - sema.typeOf(Air.indexToRef(block.instructions.items[block.instructions.items.len - 1])).isNoReturn()) + sema.isNoReturn(Air.indexToRef(block.instructions.items[block.instructions.items.len - 1]))) return null; const break_data = sema.code.instructions.items(.data)[break_inst].@"break"; const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; @@ -859,18 +877,20 @@ fn analyzeBodyInner( try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body); + // Most of the time, we don't need to construct a new capture scope for a + // block. However, successive iterations of comptime loops can capture + // different values for the same Zir.Inst.Index, so in those cases, we will + // have to create nested capture scopes; see the `.repeat` case below. const parent_capture_scope = block.wip_capture_scope; - - var wip_captures = WipCaptureScope{ - .finalized = true, + parent_capture_scope.incRef(); + var wip_captures: WipCaptureScope = .{ .scope = parent_capture_scope, - .perm_arena = sema.perm_arena, .gpa = sema.gpa, + .finalized = true, // don't finalize the parent scope }; - defer if (wip_captures.scope != parent_capture_scope) { - wip_captures.deinit(); - }; + defer wip_captures.deinit(); + const mod = sema.mod; const map = &sema.inst_map; const tags = sema.code.instructions.items(.tag); const datas = sema.code.instructions.items(.data); @@ -883,7 +903,7 @@ fn analyzeBodyInner( var dbg_block_begins: u32 = 0; - // We use a while(true) loop here to avoid a redundant way of breaking out of + // We use a while (true) loop here to avoid a redundant way of breaking out of // the loop. The only way to break out of the loop is with a `noreturn` // instruction. var i: usize = 0; @@ -891,15 +911,15 @@ fn analyzeBodyInner( crash_info.setBodyIndex(i); const inst = body[i]; std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{ - sema.mod.declPtr(block.src_decl).src_namespace.file_scope.sub_file_path, inst, + mod.namespacePtr(mod.declPtr(block.src_decl).src_namespace).file_scope.sub_file_path, inst, }); const air_inst: Air.Inst.Ref = switch (tags[inst]) { // zig fmt: off .alloc => try sema.zirAlloc(block, inst), - .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)), - .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)), - .alloc_inferred_comptime => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_const)), - .alloc_inferred_comptime_mut => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_mut)), + .alloc_inferred => try sema.zirAllocInferred(block, inst, true), + .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, false), + .alloc_inferred_comptime => try sema.zirAllocInferredComptime(inst, true), + .alloc_inferred_comptime_mut => try sema.zirAllocInferredComptime(inst, false), .alloc_mut => try sema.zirAllocMut(block, inst), .alloc_comptime_mut => try sema.zirAllocComptime(block, inst), .make_ptr_const => try sema.zirMakePtrConst(block, inst), @@ -921,7 +941,8 @@ fn analyzeBodyInner( .bool_br_and => try sema.zirBoolBr(block, inst, false), .bool_br_or => try sema.zirBoolBr(block, inst, true), .c_import => try sema.zirCImport(block, inst), - .call => try sema.zirCall(block, inst), + .call => try sema.zirCall(block, inst, .direct), + .field_call => try sema.zirCall(block, inst, .field), .closure_get => try sema.zirClosureGet(block, inst), .cmp_lt => try sema.zirCmp(block, inst, .lt), .cmp_lte => try sema.zirCmp(block, inst, .lte), @@ -953,7 +974,6 @@ fn analyzeBodyInner( .field_ptr_named => try sema.zirFieldPtrNamed(block, inst), .field_val => try sema.zirFieldVal(block, inst), .field_val_named => try sema.zirFieldValNamed(block, inst), - .field_call_bind => try sema.zirFieldCallBind(block, inst), .func => try sema.zirFunc(block, inst, false), .func_inferred => try sema.zirFunc(block, inst, true), .func_fancy => try sema.zirFuncFancy(block, inst), @@ -963,7 +983,7 @@ fn analyzeBodyInner( .int_big => try sema.zirIntBig(block, inst), .float => try sema.zirFloat(block, inst), .float128 => try sema.zirFloat128(block, inst), - .int_type => try sema.zirIntType(block, inst), + .int_type => try sema.zirIntType(inst), .is_non_err => try sema.zirIsNonErr(block, inst), .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), .ret_is_non_err => try sema.zirRetIsNonErr(block, inst), @@ -985,15 +1005,10 @@ fn analyzeBodyInner( .slice_end => try sema.zirSliceEnd(block, inst), .slice_sentinel => try sema.zirSliceSentinel(block, inst), .slice_start => try sema.zirSliceStart(block, inst), + .slice_length => try sema.zirSliceLength(block, inst), .str => try sema.zirStr(block, inst), - .switch_block => try sema.zirSwitchBlock(block, inst), - .switch_cond => try sema.zirSwitchCond(block, inst, false), - .switch_cond_ref => try sema.zirSwitchCond(block, inst, true), - .switch_capture => try sema.zirSwitchCapture(block, inst, false, false), - .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true), - .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false), - .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true), - .switch_capture_tag => try sema.zirSwitchCaptureTag(block, inst), + .switch_block => try sema.zirSwitchBlock(block, inst, false), + .switch_block_ref => try sema.zirSwitchBlock(block, inst, true), .type_info => try sema.zirTypeInfo(block, inst), .size_of => try sema.zirSizeOf(block, inst), .bit_size_of => try sema.zirBitSizeOf(block, inst), @@ -1137,6 +1152,8 @@ fn analyzeBodyInner( .asm_expr => try sema.zirAsm( block, extended, true), .typeof_peer => try sema.zirTypeofPeer( block, extended), .compile_log => try sema.zirCompileLog( extended), + .min_multi => try sema.zirMinMaxMulti( block, extended, .min), + .max_multi => try sema.zirMinMaxMulti( block, extended, .max), .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), @@ -1147,7 +1164,6 @@ fn analyzeBodyInner( .wasm_memory_size => try sema.zirWasmMemorySize( block, extended), .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), .prefetch => try sema.zirPrefetch( block, extended), - .field_call_bind_named => try sema.zirFieldCallBindNamed(block, extended), .err_set_cast => try sema.zirErrSetCast( block, extended), .await_nosuspend => try sema.zirAwaitNosuspend( block, extended), .select => try sema.zirSelect( block, extended), @@ -1196,6 +1212,7 @@ fn analyzeBodyInner( i += 1; continue; }, + .value_placeholder => unreachable, // never appears in a body }; }, @@ -1418,6 +1435,11 @@ fn analyzeBodyInner( const src = LazySrcLoc.nodeOffset(datas[inst].node); try sema.emitBackwardBranch(block, src); if (wip_captures.scope.captures.count() != orig_captures) { + // We need to construct new capture scopes for the next loop iteration so it + // can capture values without clobbering the earlier iteration's captures. + // At first, we reused the parent capture scope as an optimization, but for + // successive scopes we have to create new ones as children of the parent + // scope. try wip_captures.reset(parent_capture_scope); block.wip_capture_scope = wip_captures.scope; orig_captures = 0; @@ -1433,6 +1455,11 @@ fn analyzeBodyInner( const src = LazySrcLoc.nodeOffset(datas[inst].node); try sema.emitBackwardBranch(block, src); if (wip_captures.scope.captures.count() != orig_captures) { + // We need to construct new capture scopes for the next loop iteration so it + // can capture values without clobbering the earlier iteration's captures. + // At first, we reused the parent capture scope as an optimization, but for + // successive scopes we have to create new ones as children of the parent + // scope. try wip_captures.reset(parent_capture_scope); block.wip_capture_scope = wip_captures.scope; orig_captures = 0; @@ -1619,18 +1646,18 @@ fn analyzeBodyInner( const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; const err_union = try sema.resolveInst(extra.data.operand); const err_union_ty = sema.typeOf(err_union); - if (err_union_ty.zigTypeTag() != .ErrorUnion) { + if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ - err_union_ty.fmt(sema.mod), + err_union_ty.fmt(mod), }); } const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); assert(is_non_err != .none); - const is_non_err_tv = sema.resolveInstConst(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| { + const is_non_err_val = sema.resolveConstValue(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| { if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err); return err; }; - if (is_non_err_tv.val.toBool()) { + if (is_non_err_val.toBool()) { break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); } const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse @@ -1652,11 +1679,11 @@ fn analyzeBodyInner( const err_union = try sema.analyzeLoad(block, src, operand, operand_src); const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); assert(is_non_err != .none); - const is_non_err_tv = sema.resolveInstConst(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| { + const is_non_err_val = sema.resolveConstValue(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| { if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err); return err; }; - if (is_non_err_tv.val.toBool()) { + if (is_non_err_val.toBool()) { break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); } const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse @@ -1682,7 +1709,7 @@ fn analyzeBodyInner( const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data; const defer_body = sema.code.extra[extra.index..][0..extra.len]; const err_code = try sema.resolveInst(inst_data.err_code); - sema.inst_map.putAssumeCapacity(extra.remapped_err_code, err_code); + map.putAssumeCapacity(extra.remapped_err_code, err_code); const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) { error.ComptimeBreak => sema.comptime_break_inst, else => |e| return e, @@ -1691,8 +1718,12 @@ fn analyzeBodyInner( break :blk Air.Inst.Ref.void_value; }, }; - if (sema.typeOf(air_inst).isNoReturn()) + if (sema.isNoReturn(air_inst)) { + // We're going to assume that the body itself is noreturn, so let's ensure that now + assert(block.instructions.items.len > 0); + assert(sema.isNoReturn(Air.indexToRef(block.instructions.items[block.instructions.items.len - 1]))); break always_noreturn; + } map.putAssumeCapacity(inst, air_inst); i += 1; }; @@ -1701,7 +1732,7 @@ fn analyzeBodyInner( const noreturn_inst = block.instructions.popOrNull(); while (dbg_block_begins > 0) { dbg_block_begins -= 1; - if (block.is_comptime or sema.mod.comp.bin_file.options.strip) continue; + if (block.is_comptime or mod.comp.bin_file.options.strip) continue; _ = try block.addInst(.{ .tag = .dbg_block_end, @@ -1711,6 +1742,8 @@ fn analyzeBodyInner( if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some); if (!wip_captures.finalized) { + // We've updated the capture scope due to a `repeat` instruction where + // the body had a capture; finalize our child scope and reset try wip_captures.finalize(); block.wip_capture_scope = parent_capture_scope; } @@ -1718,20 +1751,23 @@ fn analyzeBodyInner( return result; } -pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { - var i: usize = @enumToInt(zir_ref); - - // First section of indexes correspond to a set number of constant values. - if (i < Zir.Inst.Ref.typed_value_map.len) { - // We intentionally map the same indexes to the same values between ZIR and AIR. - return zir_ref; +pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { + if (zir_ref == .none) { + return .none; + } else { + return resolveInst(sema, zir_ref); } - i -= Zir.Inst.Ref.typed_value_map.len; +} - // Finally, the last section of indexes refers to the map of ZIR=>AIR. - const inst = sema.inst_map.get(@intCast(u32, i)).?; - const ty = sema.typeOf(inst); - if (ty.tag() == .generic_poison) return error.GenericPoison; +pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { + assert(zir_ref != .none); + const i = @enumToInt(zir_ref); + // First section of indexes correspond to a set number of constant values. + // We intentionally map the same indexes to the same values between ZIR and AIR. + if (i < InternPool.static_len) return @intToEnum(Air.Inst.Ref, i); + // The last section of indexes refers to the map of ZIR => AIR. + const inst = sema.inst_map.get(i - InternPool.static_len).?; + if (inst == .generic_poison) return error.GenericPoison; return inst; } @@ -1757,16 +1793,31 @@ pub fn resolveConstString( reason: []const u8, ) ![]u8 { const air_inst = try sema.resolveInst(zir_ref); - const wanted_type = Type.initTag(.const_slice_u8); + const wanted_type = Type.slice_const_u8; const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); const val = try sema.resolveConstValue(block, src, coerced_inst, reason); return val.toAllocatedBytes(wanted_type, sema.arena, sema.mod); } +pub fn resolveConstStringIntern( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + zir_ref: Zir.Inst.Ref, + reason: []const u8, +) !InternPool.NullTerminatedString { + const air_inst = try sema.resolveInst(zir_ref); + const wanted_type = Type.slice_const_u8; + const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); + const val = try sema.resolveConstValue(block, src, coerced_inst, reason); + return val.toIpString(wanted_type, sema.mod); +} + pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { const air_inst = try sema.resolveInst(zir_ref); + assert(air_inst != .var_args_param_type); const ty = try sema.analyzeAsType(block, src, air_inst); - if (ty.tag() == .generic_poison) return error.GenericPoison; + if (ty.isGenericPoison()) return error.GenericPoison; return ty; } @@ -1776,45 +1827,48 @@ fn analyzeAsType( src: LazySrcLoc, air_inst: Air.Inst.Ref, ) !Type { - const wanted_type = Type.initTag(.type); + const wanted_type = Type.type; const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); const val = try sema.resolveConstValue(block, src, coerced_inst, "types must be comptime-known"); - var buffer: Value.ToTypeBuffer = undefined; - const ty = val.toType(&buffer); - return ty.copy(sema.arena); + return val.toType(); } pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void { - if (!sema.mod.backendSupportsFeature(.error_return_trace)) return; + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; + if (!mod.backendSupportsFeature(.error_return_trace)) return; assert(!block.is_comptime); var err_trace_block = block.makeSubBlock(); - defer err_trace_block.instructions.deinit(sema.gpa); + defer err_trace_block.instructions.deinit(gpa); const src: LazySrcLoc = .unneeded; // var addrs: [err_return_trace_addr_count]usize = undefined; const err_return_trace_addr_count = 32; - const addr_arr_ty = try Type.array(sema.arena, err_return_trace_addr_count, null, Type.usize, sema.mod); - const addrs_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, addr_arr_ty)); + const addr_arr_ty = try Type.array(sema.arena, err_return_trace_addr_count, null, Type.usize, mod); + const addrs_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(addr_arr_ty)); // var st: StackTrace = undefined; const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); - const st_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty)); + const st_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(stack_trace_ty)); // st.instruction_addresses = &addrs; - const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "instruction_addresses", src, true); + const instruction_addresses_field_name = try ip.getOrPutString(gpa, "instruction_addresses"); + const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true); try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store); // st.index = 0; - const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src, true); + const index_field_name = try ip.getOrPutString(gpa, "index"); + const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true); try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store); // @errorReturnTrace() = &st; _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr); - try block.instructions.insertSlice(sema.gpa, last_arg_index, err_trace_block.instructions.items); + try block.instructions.insertSlice(gpa, last_arg_index, err_trace_block.instructions.items); } /// May return Value Tags: `variable`, `undef`. @@ -1828,7 +1882,7 @@ fn resolveValue( reason: []const u8, ) CompileError!Value { if (try sema.resolveMaybeUndefValAllowVariables(air_ref)) |val| { - if (val.tag() == .generic_poison) return error.GenericPoison; + if (val.isGenericPoison()) return error.GenericPoison; return val; } return sema.failWithNeededComptime(block, src, reason); @@ -1844,10 +1898,12 @@ fn resolveConstMaybeUndefVal( reason: []const u8, ) CompileError!Value { if (try sema.resolveMaybeUndefValAllowVariables(inst)) |val| { - switch (val.tag()) { - .variable => return sema.failWithNeededComptime(block, src, reason), + switch (val.toIntern()) { .generic_poison => return error.GenericPoison, - else => return val, + else => switch (sema.mod.intern_pool.indexToKey(val.toIntern())) { + .variable => return sema.failWithNeededComptime(block, src, reason), + else => return val, + }, } } return sema.failWithNeededComptime(block, src, reason); @@ -1863,16 +1919,31 @@ fn resolveConstValue( reason: []const u8, ) CompileError!Value { if (try sema.resolveMaybeUndefValAllowVariables(air_ref)) |val| { - switch (val.tag()) { - .undef => return sema.failWithUseOfUndef(block, src), - .variable => return sema.failWithNeededComptime(block, src, reason), + switch (val.toIntern()) { .generic_poison => return error.GenericPoison, - else => return val, + .undef => return sema.failWithUseOfUndef(block, src), + else => switch (sema.mod.intern_pool.indexToKey(val.toIntern())) { + .undef => return sema.failWithUseOfUndef(block, src), + .variable => return sema.failWithNeededComptime(block, src, reason), + else => return val, + }, } } return sema.failWithNeededComptime(block, src, reason); } +/// Will not return Value Tags: `variable`, `undef`. Instead they will emit compile errors. +/// Lazy values are recursively resolved. +fn resolveConstLazyValue( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + air_ref: Air.Inst.Ref, + reason: []const u8, +) CompileError!Value { + return sema.resolveLazyValue(try sema.resolveConstValue(block, src, air_ref, reason)); +} + /// Value Tag `variable` causes this function to return `null`. /// Value Tag `undef` causes this function to return a compile error. fn resolveDefinedValue( @@ -1881,8 +1952,9 @@ fn resolveDefinedValue( src: LazySrcLoc, air_ref: Air.Inst.Ref, ) CompileError!?Value { + const mod = sema.mod; if (try sema.resolveMaybeUndefVal(air_ref)) |val| { - if (val.isUndef()) { + if (val.isUndef(mod)) { if (block.is_typeof) return null; return sema.failWithUseOfUndef(block, src); } @@ -1899,34 +1971,53 @@ fn resolveMaybeUndefVal( inst: Air.Inst.Ref, ) CompileError!?Value { const val = (try sema.resolveMaybeUndefValAllowVariables(inst)) orelse return null; - switch (val.tag()) { - .variable => return null, + switch (val.ip_index) { .generic_poison => return error.GenericPoison, - else => return val, + .none => return val, + else => switch (sema.mod.intern_pool.indexToKey(val.toIntern())) { + .variable => return null, + else => return val, + }, } } +/// Value Tag `variable` causes this function to return `null`. +/// Value Tag `undef` causes this function to return the Value. +/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned. +/// Lazy values are recursively resolved. +fn resolveMaybeUndefLazyVal( + sema: *Sema, + inst: Air.Inst.Ref, +) CompileError!?Value { + return try sema.resolveLazyValue((try sema.resolveMaybeUndefVal(inst)) orelse return null); +} + /// Value Tag `variable` results in `null`. /// Value Tag `undef` results in the Value. /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned. /// Value Tag `decl_ref` and `decl_ref_mut` or any nested such value results in `null`. +/// Lazy values are recursively resolved. fn resolveMaybeUndefValIntable( sema: *Sema, inst: Air.Inst.Ref, ) CompileError!?Value { const val = (try sema.resolveMaybeUndefValAllowVariables(inst)) orelse return null; var check = val; - while (true) switch (check.tag()) { - .variable, .decl_ref, .decl_ref_mut, .comptime_field_ptr => return null, - .field_ptr => check = check.castTag(.field_ptr).?.data.container_ptr, - .elem_ptr => check = check.castTag(.elem_ptr).?.data.array_ptr, - .eu_payload_ptr, .opt_payload_ptr => check = check.cast(Value.Payload.PayloadPtr).?.data.container_ptr, + while (true) switch (check.ip_index) { .generic_poison => return error.GenericPoison, - else => { - try sema.resolveLazyValue(val); - return val; + .none => break, + else => switch (sema.mod.intern_pool.indexToKey(check.toIntern())) { + .variable => return null, + .ptr => |ptr| switch (ptr.addr) { + .decl, .mut_decl, .comptime_field => return null, + .int => break, + .eu_payload, .opt_payload => |base| check = base.toValue(), + .elem, .field => |base_index| check = base_index.base.toValue(), + }, + else => break, }, }; + return try sema.resolveLazyValue(val); } /// Returns all Value tags including `variable` and `undef`. @@ -1945,35 +2036,33 @@ fn resolveMaybeUndefValAllowVariablesMaybeRuntime( inst: Air.Inst.Ref, make_runtime: *bool, ) CompileError!?Value { + assert(inst != .none); // First section of indexes correspond to a set number of constant values. - var i: usize = @enumToInt(inst); - if (i < Air.Inst.Ref.typed_value_map.len) { - return Air.Inst.Ref.typed_value_map[i].val; + const int = @enumToInt(inst); + if (int < InternPool.static_len) { + return @intToEnum(InternPool.Index, int).toValue(); } - i -= Air.Inst.Ref.typed_value_map.len; + const i = int - InternPool.static_len; const air_tags = sema.air_instructions.items(.tag); if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| { - if (air_tags[i] == .constant) { - const ty_pl = sema.air_instructions.items(.data)[i].ty_pl; - const val = sema.air_values.items[ty_pl.payload]; - if (val.tag() == .variable) return val; + if (air_tags[i] == .interned) { + const interned = sema.air_instructions.items(.data)[i].interned; + const val = interned.toValue(); + if (val.getVariable(sema.mod) != null) return val; } return opv; } - switch (air_tags[i]) { - .constant => { - const ty_pl = sema.air_instructions.items(.data)[i].ty_pl; - const val = sema.air_values.items[ty_pl.payload]; - if (val.tag() == .runtime_value) make_runtime.* = true; - if (val.isPtrToThreadLocal(sema.mod)) make_runtime.* = true; - return val; - }, - .const_ty => { - return try sema.air_instructions.items(.data)[i].ty.toValue(sema.arena); - }, + const air_datas = sema.air_instructions.items(.data); + const val = switch (air_tags[i]) { + .inferred_alloc => unreachable, + .inferred_alloc_comptime => unreachable, + .interned => air_datas[i].interned.toValue(), else => return null, - } + }; + if (val.isRuntimeValue(sema.mod)) make_runtime.* = true; + if (val.isPtrToThreadLocal(sema.mod)) make_runtime.* = true; + return val; } fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: []const u8) CompileError { @@ -2006,13 +2095,14 @@ fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, opt } fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { + const mod = sema.mod; const msg = msg: { const msg = try sema.errMsg(block, src, "type '{}' does not support array initialization syntax", .{ - ty.fmt(sema.mod), + ty.fmt(mod), }); errdefer msg.destroy(sema.gpa); - if (ty.isSlice()) { - try sema.errNote(block, src, msg, "inferred array length is specified with an underscore: '[_]{}'", .{ty.elemType2().fmt(sema.mod)}); + if (ty.isSlice(mod)) { + try sema.errNote(block, src, msg, "inferred array length is specified with an underscore: '[_]{}'", .{ty.elemType2(mod).fmt(mod)}); } break :msg msg; }; @@ -2038,7 +2128,8 @@ fn failWithErrorSetCodeMissing( } fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: usize) CompileError { - if (int_ty.zigTypeTag() == .Vector) { + const mod = sema.mod; + if (int_ty.zigTypeTag(mod) == .Vector) { const msg = msg: { const msg = try sema.errMsg(block, src, "overflow of vector type '{}' with value '{}'", .{ int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod), @@ -2055,16 +2146,17 @@ fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: } fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazySrcLoc, container_ty: Type, field_index: usize) CompileError { + const mod = sema.mod; const msg = msg: { const msg = try sema.errMsg(block, init_src, "value stored in comptime field does not match the default value of the field", .{}); errdefer msg.destroy(sema.gpa); - const struct_ty = container_ty.castTag(.@"struct") orelse break :msg msg; - const default_value_src = struct_ty.data.fieldSrcLoc(sema.mod, .{ + const struct_ty = mod.typeToStruct(container_ty) orelse break :msg msg; + const default_value_src = mod.fieldSrcLoc(struct_ty.owner_decl, .{ .index = field_index, .range = .value, }); - try sema.mod.errNoteNonLazy(default_value_src, msg, "default value set here", .{}); + try mod.errNoteNonLazy(default_value_src, msg, "default value set here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -2079,13 +2171,19 @@ fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError return sema.failWithOwnedErrorMsg(msg); } -fn failWithInvalidFieldAccess(sema: *Sema, block: *Block, src: LazySrcLoc, object_ty: Type, field_name: []const u8) CompileError { - const inner_ty = if (object_ty.isSinglePointer()) object_ty.childType() else object_ty; +fn failWithInvalidFieldAccess( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + object_ty: Type, + field_name: InternPool.NullTerminatedString, +) CompileError { + const mod = sema.mod; + const inner_ty = if (object_ty.isSinglePointer(mod)) object_ty.childType(mod) else object_ty; - if (inner_ty.zigTypeTag() == .Optional) opt: { - var buf: Type.Payload.ElemType = undefined; - const child_ty = inner_ty.optionalChild(&buf); - if (!typeSupportsFieldAccess(child_ty, field_name)) break :opt; + if (inner_ty.zigTypeTag(mod) == .Optional) opt: { + const child_ty = inner_ty.optionalChild(mod); + if (!typeSupportsFieldAccess(mod, child_ty, field_name)) break :opt; const msg = msg: { const msg = try sema.errMsg(block, src, "optional type '{}' does not support field access", .{object_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -2093,9 +2191,9 @@ fn failWithInvalidFieldAccess(sema: *Sema, block: *Block, src: LazySrcLoc, objec break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } else if (inner_ty.zigTypeTag() == .ErrorUnion) err: { - const child_ty = inner_ty.errorUnionPayload(); - if (!typeSupportsFieldAccess(child_ty, field_name)) break :err; + } else if (inner_ty.zigTypeTag(mod) == .ErrorUnion) err: { + const child_ty = inner_ty.errorUnionPayload(mod); + if (!typeSupportsFieldAccess(mod, child_ty, field_name)) break :err; const msg = msg: { const msg = try sema.errMsg(block, src, "error union type '{}' does not support field access", .{object_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -2107,15 +2205,16 @@ fn failWithInvalidFieldAccess(sema: *Sema, block: *Block, src: LazySrcLoc, objec return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)}); } -fn typeSupportsFieldAccess(ty: Type, field_name: []const u8) bool { - switch (ty.zigTypeTag()) { - .Array => return mem.eql(u8, field_name, "len"), +fn typeSupportsFieldAccess(mod: *const Module, ty: Type, field_name: InternPool.NullTerminatedString) bool { + const ip = &mod.intern_pool; + switch (ty.zigTypeTag(mod)) { + .Array => return ip.stringEqlSlice(field_name, "len"), .Pointer => { - const ptr_info = ty.ptrInfo().data; + const ptr_info = ty.ptrInfo(mod); if (ptr_info.size == .Slice) { - return mem.eql(u8, field_name, "ptr") or mem.eql(u8, field_name, "len"); - } else if (ptr_info.pointee_type.zigTypeTag() == .Array) { - return mem.eql(u8, field_name, "len"); + return ip.stringEqlSlice(field_name, "ptr") or ip.stringEqlSlice(field_name, "len"); + } else if (ptr_info.pointee_type.zigTypeTag(mod) == .Array) { + return ip.stringEqlSlice(field_name, "len"); } else return false; }, .Type, .Struct, .Union => return true, @@ -2135,7 +2234,7 @@ fn errNote( ) error{OutOfMemory}!void { const mod = sema.mod; const src_decl = mod.declPtr(block.src_decl); - return mod.errNoteNonLazy(src.toSrcLoc(src_decl), parent, format, args); + return mod.errNoteNonLazy(src.toSrcLoc(src_decl, mod), parent, format, args); } fn addFieldErrNote( @@ -2148,19 +2247,19 @@ fn addFieldErrNote( ) !void { @setCold(true); const mod = sema.mod; - const decl_index = container_ty.getOwnerDecl(); + const decl_index = container_ty.getOwnerDecl(mod); const decl = mod.declPtr(decl_index); const field_src = blk: { - const tree = decl.getFileScope().getTree(sema.gpa) catch |err| { + const tree = decl.getFileScope(mod).getTree(sema.gpa) catch |err| { log.err("unable to load AST to report compile error: {s}", .{@errorName(err)}); - break :blk decl.srcLoc(); + break :blk decl.srcLoc(mod); }; const container_node = decl.relativeToNodeIndex(0); const node_tags = tree.nodes.items(.tag); var buf: [2]std.zig.Ast.Node.Index = undefined; - const container_decl = tree.fullContainerDecl(&buf, container_node) orelse break :blk decl.srcLoc(); + const container_decl = tree.fullContainerDecl(&buf, container_node) orelse break :blk decl.srcLoc(mod); var it_index: usize = 0; for (container_decl.ast.members) |member_node| { @@ -2170,7 +2269,7 @@ fn addFieldErrNote( .container_field, => { if (it_index == field_index) { - break :blk decl.nodeOffsetSrcLoc(decl.nodeIndexToRelative(member_node)); + break :blk decl.nodeOffsetSrcLoc(decl.nodeIndexToRelative(member_node), mod); } it_index += 1; }, @@ -2191,7 +2290,7 @@ fn errMsg( ) error{OutOfMemory}!*Module.ErrorMsg { const mod = sema.mod; const src_decl = mod.declPtr(block.src_decl); - return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl), format, args); + return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl, mod), format, args); } pub fn fail( @@ -2208,19 +2307,19 @@ pub fn fail( fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { @setCold(true); const gpa = sema.gpa; + const mod = sema.mod; - if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) { + if (crash_report.is_enabled and mod.comp.debug_compile_errors) { if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation; var wip_errors: std.zig.ErrorBundle.Wip = undefined; wip_errors.init(gpa) catch unreachable; - Compilation.addModuleErrorMsg(&wip_errors, err_msg.*) catch unreachable; + Compilation.addModuleErrorMsg(mod, &wip_errors, err_msg.*) catch unreachable; std.debug.print("compile error during Sema:\n", .{}); var error_bundle = wip_errors.toOwnedBundle("") catch unreachable; error_bundle.renderToStdErr(.{ .ttyconf = .no_color }); crash_report.compilerPanic("unexpected compile error occurred", null, null); } - const mod = sema.mod; ref: { errdefer err_msg.destroy(gpa); if (err_msg.src_loc.lazy == .unneeded) { @@ -2230,9 +2329,9 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { try mod.failed_files.ensureUnusedCapacity(gpa, 1); const max_references = blk: { - if (sema.mod.comp.reference_trace) |num| break :blk num; + if (mod.comp.reference_trace) |num| break :blk num; // Do not add multiple traces without explicit request. - if (sema.mod.failed_decls.count() != 0) break :ref; + if (mod.failed_decls.count() != 0) break :ref; break :blk default_reference_trace_len; }; @@ -2241,7 +2340,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { defer reference_stack.deinit(); // Avoid infinite loops. - var seen = std.AutoHashMap(Module.Decl.Index, void).init(gpa); + var seen = std.AutoHashMap(Decl.Index, void).init(gpa); defer seen.deinit(); var cur_reference_trace: u32 = 0; @@ -2250,13 +2349,16 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { if (gop.found_existing) break; if (cur_reference_trace < max_references) { const decl = sema.mod.declPtr(ref.referencer); - try reference_stack.append(.{ .decl = decl.name, .src_loc = ref.src.toSrcLoc(decl) }); + try reference_stack.append(.{ + .decl = decl.name.toOptional(), + .src_loc = ref.src.toSrcLoc(decl, mod), + }); } referenced_by = ref.referencer; } if (sema.mod.comp.reference_trace == null and cur_reference_trace > 0) { try reference_stack.append(.{ - .decl = null, + .decl = .none, .src_loc = undefined, .hidden = 0, }); @@ -2290,6 +2392,34 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { return error.AnalysisFail; } +/// Given an ErrorMsg, modify its message and source location to the given values, turning the +/// original message into a note. Notes on the original message are preserved as further notes. +/// Reference trace is preserved. +fn reparentOwnedErrorMsg( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + msg: *Module.ErrorMsg, + comptime format: []const u8, + args: anytype, +) !void { + const mod = sema.mod; + const src_decl = mod.declPtr(block.src_decl); + const resolved_src = src.toSrcLoc(src_decl, mod); + const msg_str = try std.fmt.allocPrint(mod.gpa, format, args); + + const orig_notes = msg.notes.len; + msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1); + std.mem.copyBackwards(Module.ErrorMsg, msg.notes[1..], msg.notes[0..orig_notes]); + msg.notes[0] = .{ + .src_loc = msg.src_loc, + .msg = msg.msg, + }; + + msg.src_loc = resolved_src; + msg.msg = msg_str; +} + const align_ty = Type.u29; fn analyzeAsAlign( @@ -2348,10 +2478,10 @@ fn analyzeAsInt( dest_ty: Type, reason: []const u8, ) !u64 { + const mod = sema.mod; const coerced = try sema.coerce(block, dest_ty, air_ref, src); const val = try sema.resolveConstValue(block, src, coerced, reason); - const target = sema.mod.getTarget(); - return (try val.getUnsignedIntAdvanced(target, sema)).?; + return (try val.getUnsignedIntAdvanced(mod, sema)).?; } // Returns a compile error if the value has tag `variable`. See `resolveInstValue` for @@ -2392,72 +2522,77 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const pointee_ty = try sema.resolveType(block, src, extra.lhs); const ptr = try sema.resolveInst(extra.rhs); - const target = sema.mod.getTarget(); + const target = mod.getTarget(); const addr_space = target_util.defaultAddressSpace(target, .local); if (Air.refToIndex(ptr)) |ptr_inst| { - if (sema.air_instructions.items(.tag)[ptr_inst] == .constant) { - const air_datas = sema.air_instructions.items(.data); - const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload]; - switch (ptr_val.tag()) { - .inferred_alloc => { - const inferred_alloc = &ptr_val.castTag(.inferred_alloc).?.data; - // Add the stored instruction to the set we will use to resolve peer types - // for the inferred allocation. - // This instruction will not make it to codegen; it is only to participate - // in the `stored_inst_list` of the `inferred_alloc`. - var trash_block = block.makeSubBlock(); - defer trash_block.instructions.deinit(sema.gpa); - const operand = try trash_block.addBitCast(pointee_ty, .void_value); - - const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = pointee_ty, - .@"align" = inferred_alloc.alignment, - .@"addrspace" = addr_space, - }); - const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr); + switch (sema.air_instructions.items(.tag)[ptr_inst]) { + .inferred_alloc => { + const ia1 = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc; + const ia2 = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?; + // Add the stored instruction to the set we will use to resolve peer types + // for the inferred allocation. + // This instruction will not make it to codegen; it is only to participate + // in the `stored_inst_list` of the `inferred_alloc`. + var trash_block = block.makeSubBlock(); + defer trash_block.instructions.deinit(sema.gpa); + const operand = try trash_block.addBitCast(pointee_ty, .void_value); + + const ptr_ty = try mod.ptrType(.{ + .child = pointee_ty.toIntern(), + .flags = .{ + .alignment = ia1.alignment, + .address_space = addr_space, + }, + }); + const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr); - try inferred_alloc.prongs.append(sema.arena, .{ - .stored_inst = operand, - .placeholder = Air.refToIndex(bitcasted_ptr).?, - }); + try ia2.prongs.append(sema.arena, .{ + .stored_inst = operand, + .placeholder = Air.refToIndex(bitcasted_ptr).?, + }); - return bitcasted_ptr; - }, - .inferred_alloc_comptime => { - const iac = ptr_val.castTag(.inferred_alloc_comptime).?; - // There will be only one coerce_result_ptr because we are running at comptime. - // The alloc will turn into a Decl. - var anon_decl = try block.startAnonDecl(); - defer anon_decl.deinit(); - iac.data.decl_index = try anon_decl.finish( - try pointee_ty.copy(anon_decl.arena()), - Value.undef, - iac.data.alignment, - ); - if (iac.data.alignment != 0) { - try sema.resolveTypeLayout(pointee_ty); - } - const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = pointee_ty, - .@"align" = iac.data.alignment, - .@"addrspace" = addr_space, - }); - return sema.addConstant( - ptr_ty, - try Value.Tag.decl_ref_mut.create(sema.arena, .{ - .decl_index = iac.data.decl_index, - .runtime_index = block.runtime_index, - }), - ); - }, - else => {}, - } + return bitcasted_ptr; + }, + .inferred_alloc_comptime => { + const alignment = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime.alignment; + // There will be only one coerce_result_ptr because we are running at comptime. + // The alloc will turn into a Decl. + var anon_decl = try block.startAnonDecl(); + defer anon_decl.deinit(); + const decl_index = try anon_decl.finish( + pointee_ty, + (try mod.intern(.{ .undef = pointee_ty.toIntern() })).toValue(), + alignment.toByteUnits(0), + ); + sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime.decl_index = decl_index; + if (alignment != .none) { + try sema.resolveTypeLayout(pointee_ty); + } + const ptr_ty = try mod.ptrType(.{ + .child = pointee_ty.toIntern(), + .flags = .{ + .alignment = alignment, + .address_space = addr_space, + }, + }); + try sema.maybeQueueFuncBodyAnalysis(decl_index); + try sema.comptime_mutable_decls.append(decl_index); + return sema.addConstant(ptr_ty, (try mod.intern(.{ .ptr = .{ + .ty = ptr_ty.toIntern(), + .addr = .{ .mut_decl = .{ + .decl = decl_index, + .runtime_index = block.runtime_index, + } }, + } })).toValue()); + }, + else => {}, } } @@ -2466,7 +2601,6 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE // kind of transformations to make on the result pointer. var trash_block = block.makeSubBlock(); trash_block.is_comptime = false; - trash_block.is_coerce_result_ptr = true; defer trash_block.instructions.deinit(sema.gpa); const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr)); @@ -2483,6 +2617,7 @@ fn coerceResultPtr( dummy_operand: Air.Inst.Ref, trash_block: *Block, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const target = sema.mod.getTarget(); const addr_space = target_util.defaultAddressSpace(target, .local); const pointee_ty = sema.typeOf(dummy_operand); @@ -2526,7 +2661,7 @@ fn coerceResultPtr( return sema.addConstant(ptr_ty, ptr_val); } if (pointee_ty.eql(Type.null, sema.mod)) { - const opt_ty = sema.typeOf(new_ptr).childType(); + const opt_ty = sema.typeOf(new_ptr).childType(mod); const null_inst = try sema.addConstant(opt_ty, Value.null); _ = try block.addBinOp(.store, new_ptr, null_inst); return Air.Inst.Ref.void_value; @@ -2559,7 +2694,7 @@ fn coerceResultPtr( .@"addrspace" = addr_space, }); if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| { - new_ptr = try sema.addConstant(ptr_operand_ty, ptr_val); + new_ptr = try sema.addConstant(ptr_operand_ty, try mod.getCoerced(ptr_val, ptr_operand_ty)); } else { new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src, null); } @@ -2576,6 +2711,9 @@ fn coerceResultPtr( .array_to_slice => { return sema.fail(block, src, "TODO coerce_result_ptr array_to_slice", .{}); }, + .get_union_tag => { + return sema.fail(block, src, "TODO coerce_result_ptr get_union_tag", .{}); + }, else => { if (std.debug.runtime_safety) { std.debug.panic("unexpected AIR tag for coerce_result_ptr: {}", .{ @@ -2593,8 +2731,10 @@ pub fn analyzeStructDecl( sema: *Sema, new_decl: *Decl, inst: Zir.Inst.Index, - struct_obj: *Module.Struct, + struct_index: Module.Struct.Index, ) SemaError!void { + const mod = sema.mod; + const struct_obj = mod.structPtr(struct_index); const extended = sema.code.instructions.items(.data)[inst].extended; assert(extended.opcode == .struct_decl); const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); @@ -2623,7 +2763,7 @@ pub fn analyzeStructDecl( } } - _ = try sema.mod.scanNamespace(&struct_obj.namespace, extra_index, decls_len, new_decl); + _ = try mod.scanNamespace(struct_obj.namespace, extra_index, decls_len, new_decl); } fn zirStructDecl( @@ -2632,28 +2772,35 @@ fn zirStructDecl( extended: Zir.Inst.Extended.InstData, inst: Zir.Inst.Index, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const gpa = sema.gpa; const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); const src: LazySrcLoc = if (small.has_src_node) blk: { const node_offset = @bitCast(i32, sema.code.extra[extended.operand]); break :blk LazySrcLoc.nodeOffset(node_offset); } else sema.src; - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); + // Because these three things each reference each other, `undefined` + // placeholders are used before being set after the struct type gains an + // InternPool index. - const mod = sema.mod; - const struct_obj = try new_decl_arena_allocator.create(Module.Struct); - const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); - const struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.type, - .val = struct_val, + .ty = Type.noreturn, + .val = Value.@"unreachable", }, small.name_strategy, "struct", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); - struct_obj.* = .{ + + const new_namespace_index = try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .ty = undefined, + .file_scope = block.getFileScope(mod), + }); + const new_namespace = mod.namespacePtr(new_namespace_index); + errdefer mod.destroyNamespace(new_namespace_index); + + const struct_index = try mod.createStruct(.{ .owner_decl = new_decl_index, .fields = .{}, .zir_index = inst, @@ -2661,18 +2808,25 @@ fn zirStructDecl( .status = .none, .known_non_opv = undefined, .is_tuple = small.is_tuple, - .namespace = .{ - .parent = block.namespace, - .ty = struct_ty, - .file_scope = block.getFileScope(), - }, - }; - std.log.scoped(.module).debug("create struct {*} owned by {*} ({s})", .{ - &struct_obj.namespace, new_decl, new_decl.name, + .namespace = new_namespace_index, }); - try sema.analyzeStructDecl(new_decl, inst, struct_obj); - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + errdefer mod.destroyStruct(struct_index); + + const struct_ty = try mod.intern_pool.get(gpa, .{ .struct_type = .{ + .index = struct_index.toOptional(), + .namespace = new_namespace_index.toOptional(), + } }); + // TODO: figure out InternPool removals for incremental compilation + //errdefer mod.intern_pool.remove(struct_ty); + + new_decl.ty = Type.type; + new_decl.val = struct_ty.toValue(); + new_namespace.ty = struct_ty.toType(); + + try sema.analyzeStructDecl(new_decl, inst, struct_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; } fn createAnonymousDeclTypeNamed( @@ -2685,6 +2839,7 @@ fn createAnonymousDeclTypeNamed( inst: ?Zir.Inst.Index, ) !Decl.Index { const mod = sema.mod; + const gpa = sema.gpa; const namespace = block.namespace; const src_scope = block.wip_capture_scope; const src_decl = mod.declPtr(block.src_decl); @@ -2700,16 +2855,15 @@ fn createAnonymousDeclTypeNamed( // semantically analyzed. // This name is also used as the key in the parent namespace so it cannot be // renamed. - const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__{s}_{d}", .{ - src_decl.name, anon_prefix, @enumToInt(new_decl_index), - }); - errdefer sema.gpa.free(name); + + const name = mod.intern_pool.getOrPutStringFmt(gpa, "{}__{s}_{d}", .{ + src_decl.name.fmt(&mod.intern_pool), anon_prefix, @enumToInt(new_decl_index), + }) catch unreachable; try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); return new_decl_index; }, .parent => { - const name = try sema.gpa.dupeZ(u8, mem.sliceTo(sema.mod.declPtr(block.src_decl).name, 0)); - errdefer sema.gpa.free(name); + const name = mod.declPtr(block.src_decl).name; try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); return new_decl_index; }, @@ -2717,21 +2871,26 @@ fn createAnonymousDeclTypeNamed( const fn_info = sema.code.getFnInfo(sema.func.?.zir_body_inst); const zir_tags = sema.code.instructions.items(.tag); - var buf = std.ArrayList(u8).init(sema.gpa); + var buf = std.ArrayList(u8).init(gpa); defer buf.deinit(); - try buf.appendSlice(mem.sliceTo(sema.mod.declPtr(block.src_decl).name, 0)); - try buf.appendSlice("("); + + const writer = buf.writer(); + try writer.print("{}(", .{mod.declPtr(block.src_decl).name.fmt(&mod.intern_pool)}); var arg_i: usize = 0; for (fn_info.param_body) |zir_inst| switch (zir_tags[zir_inst]) { .param, .param_comptime, .param_anytype, .param_anytype_comptime => { const arg = sema.inst_map.get(zir_inst).?; - // The comptime call code in analyzeCall already did this, so we're - // just repeating it here and it's guaranteed to work. - const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg, "") catch unreachable; + // 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 = sema.resolveConstMaybeUndefVal(block, .unneeded, arg, "") catch + return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null); - if (arg_i != 0) try buf.appendSlice(","); - try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)}); + if (arg_i != 0) try writer.writeByte(','); + try writer.print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)}); arg_i += 1; continue; @@ -2739,9 +2898,8 @@ fn createAnonymousDeclTypeNamed( else => continue, }; - try buf.appendSlice(")"); - const name = try buf.toOwnedSliceSentinel(0); - errdefer sema.gpa.free(name); + try writer.writeByte(')'); + const name = try mod.intern_pool.getOrPutString(gpa, buf.items); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); return new_decl_index; }, @@ -2754,10 +2912,9 @@ fn createAnonymousDeclTypeNamed( .dbg_var_ptr, .dbg_var_val => { if (zir_data[i].str_op.operand != ref) continue; - const name = try std.fmt.allocPrintZ(sema.gpa, "{s}.{s}", .{ - src_decl.name, zir_data[i].str_op.getStr(sema.code), + const name = try mod.intern_pool.getOrPutStringFmt(gpa, "{}.{s}", .{ + src_decl.name.fmt(&mod.intern_pool), zir_data[i].str_op.getStr(sema.code), }); - errdefer sema.gpa.free(name); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); return new_decl_index; @@ -2814,53 +2971,28 @@ fn zirEnumDecl( break :blk decls_len; } else 0; - var done = false; - - var new_decl_arena = std.heap.ArenaAllocator.init(gpa); - errdefer if (!done) new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); + // Because these three things each reference each other, `undefined` + // placeholders are used before being set after the enum type gains an + // InternPool index. - const enum_obj = try new_decl_arena_allocator.create(Module.EnumFull); - const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumFull); - enum_ty_payload.* = .{ - .base = .{ .tag = if (small.nonexhaustive) .enum_nonexhaustive else .enum_full }, - .data = enum_obj, - }; - const enum_ty = Type.initPayload(&enum_ty_payload.base); - const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); + var done = false; const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.type, - .val = enum_val, + .ty = Type.noreturn, + .val = Value.@"unreachable", }, small.name_strategy, "enum", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer if (!done) mod.abortAnonDecl(new_decl_index); - enum_obj.* = .{ - .owner_decl = new_decl_index, - .tag_ty = Type.null, - .tag_ty_inferred = true, - .fields = .{}, - .values = .{}, - .namespace = .{ - .parent = block.namespace, - .ty = enum_ty, - .file_scope = block.getFileScope(), - }, - }; - std.log.scoped(.module).debug("create enum {*} owned by {*} ({s})", .{ - &enum_obj.namespace, new_decl, new_decl.name, + const new_namespace_index = try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .ty = undefined, + .file_scope = block.getFileScope(mod), }); + const new_namespace = mod.namespacePtr(new_namespace_index); + errdefer if (!done) mod.destroyNamespace(new_namespace_index); - try new_decl.finalizeNewArena(&new_decl_arena); - const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index); - done = true; - - var decl_arena: std.heap.ArenaAllocator = undefined; - const decl_arena_allocator = new_decl.value_arena.?.acquire(gpa, &decl_arena); - defer new_decl.value_arena.?.release(&decl_arena); - - extra_index = try mod.scanNamespace(&enum_obj.namespace, extra_index, decls_len, new_decl); + extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; @@ -2869,7 +3001,34 @@ fn zirEnumDecl( 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 incomplete_enum = try mod.intern_pool.getIncompleteEnum(gpa, .{ + .decl = new_decl_index, + .namespace = new_namespace_index.toOptional(), + .fields_len = fields_len, + .has_values = any_values, + .tag_mode = if (small.nonexhaustive) + .nonexhaustive + else if (tag_type_ref == .none) + .auto + else + .explicit, + }); + // TODO: figure out InternPool removals for incremental compilation + //errdefer if (!done) mod.intern_pool.remove(incomplete_enum.index); + + new_decl.ty = Type.type; + new_decl.val = incomplete_enum.index.toValue(); + new_namespace.ty = incomplete_enum.index.toType(); + + const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + done = true; + + const int_tag_ty = ty: { // We create a block for the field type instructions because they // may need to reference Decls from inside the enum namespace. // Within the field type, default value, and alignment expressions, the "owner decl" @@ -2885,21 +3044,27 @@ fn zirEnumDecl( } const prev_owner_func = sema.owner_func; + const prev_owner_func_index = sema.owner_func_index; sema.owner_func = null; + sema.owner_func_index = .none; defer sema.owner_func = prev_owner_func; + defer sema.owner_func_index = prev_owner_func_index; const prev_func = sema.func; + const prev_func_index = sema.func_index; sema.func = null; + sema.func_index = .none; defer sema.func = prev_func; + defer sema.func_index = prev_func_index; - var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope); + var wip_captures = try WipCaptureScope.init(gpa, new_decl.src_scope); defer wip_captures.deinit(); var enum_block: Block = .{ .parent = null, .sema = sema, .src_decl = new_decl_index, - .namespace = &enum_obj.namespace, + .namespace = new_namespace_index, .wip_capture_scope = wip_captures.scope, .instructions = .{}, .inlining = null, @@ -2915,43 +3080,29 @@ fn zirEnumDecl( if (tag_type_ref != .none) { const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref); - if (ty.zigTypeTag() != .Int and ty.zigTypeTag() != .ComptimeInt) { + if (ty.zigTypeTag(mod) != .Int and ty.zigTypeTag(mod) != .ComptimeInt) { return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)}); } - enum_obj.tag_ty = try ty.copy(decl_arena_allocator); - enum_obj.tag_ty_inferred = false; + incomplete_enum.setTagType(&mod.intern_pool, ty.toIntern()); + break :ty ty; } else if (fields_len == 0) { - enum_obj.tag_ty = try Type.Tag.int_unsigned.create(decl_arena_allocator, 0); - enum_obj.tag_ty_inferred = true; + break :ty try mod.intType(.unsigned, 0); } else { const bits = std.math.log2_int_ceil(usize, fields_len); - enum_obj.tag_ty = try Type.Tag.int_unsigned.create(decl_arena_allocator, bits); - enum_obj.tag_ty_inferred = true; + break :ty try mod.intType(.unsigned, bits); } - } + }; - if (small.nonexhaustive and enum_obj.tag_ty.zigTypeTag() != .ComptimeInt) { - if (fields_len > 1 and std.math.log2_int(u64, fields_len) == enum_obj.tag_ty.bitSize(sema.mod.getTarget())) { + 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(mod)) { return sema.fail(block, src, "non-exhaustive enum specifies every value", .{}); } } - try enum_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len); - const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { - if (bag != 0) break true; - } else false; - if (any_values) { - try enum_obj.values.ensureTotalCapacityContext(decl_arena_allocator, fields_len, .{ - .ty = enum_obj.tag_ty, - .mod = mod, - }); - } - var bit_bag_index: usize = body_end; var cur_bit_bag: u32 = undefined; var field_i: u32 = 0; var last_tag_val: ?Value = null; - var tag_val_buf: Value.Payload.U64 = undefined; while (field_i < fields_len) : (field_i += 1) { if (field_i % 32 == 0) { cur_bit_bag = sema.code.extra[bit_bag_index]; @@ -2966,15 +3117,12 @@ fn zirEnumDecl( // doc comment extra_index += 1; - // This string needs to outlive the ZIR code. - const field_name = try decl_arena_allocator.dupe(u8, field_name_zir); - - const gop_field = enum_obj.fields.getOrPutAssumeCapacity(field_name); - if (gop_field.found_existing) { - const field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy; - const other_field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = gop_field.index }).lazy; + const field_name = try mod.intern_pool.getOrPutString(gpa, field_name_zir); + if (try incomplete_enum.addFieldName(&mod.intern_pool, gpa, field_name)) |other_index| { + const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy; + const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; const msg = msg: { - const msg = try sema.errMsg(block, field_src, "duplicate enum field '{s}'", .{field_name}); + const msg = try sema.errMsg(block, field_src, "duplicate enum field '{s}'", .{field_name_zir}); errdefer msg.destroy(gpa); try sema.errNote(block, other_field_src, msg, "other field here", .{}); break :msg msg; @@ -2982,13 +3130,13 @@ fn zirEnumDecl( return sema.failWithOwnedErrorMsg(msg); } - if (has_tag_value) { + const tag_overflow = if (has_tag_value) overflow: { const tag_val_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; const tag_inst = try sema.resolveInst(tag_val_ref); - const tag_val = sema.resolveConstValue(block, .unneeded, tag_inst, "") catch |err| switch (err) { + last_tag_val = sema.resolveConstValue(block, .unneeded, tag_inst, "") catch |err| switch (err) { error.NeededSourceLocation => { - const value_src = enum_obj.fieldSrcLoc(sema.mod, .{ + const value_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i, .range = .value, }).lazy; @@ -2997,63 +3145,56 @@ fn zirEnumDecl( }, else => |e| return e, }; - last_tag_val = tag_val; - const copied_tag_val = try tag_val.copy(decl_arena_allocator); - const gop_val = enum_obj.values.getOrPutAssumeCapacityContext(copied_tag_val, .{ - .ty = enum_obj.tag_ty, - .mod = mod, - }); - if (gop_val.found_existing) { - const value_src = enum_obj.fieldSrcLoc(sema.mod, .{ + if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true; + last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty); + if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, last_tag_val.?.toIntern())) |other_index| { + const value_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i, .range = .value, }).lazy; - const other_field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = gop_val.index }).lazy; + const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; const msg = msg: { - const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{tag_val.fmtValue(enum_obj.tag_ty, sema.mod)}); + const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)}); errdefer msg.destroy(gpa); try sema.errNote(block, other_field_src, msg, "other occurrence here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } - } else if (any_values) { - const tag_val = if (last_tag_val) |val| - try sema.intAdd(val, Value.one, enum_obj.tag_ty) + break :overflow false; + } else if (any_values) overflow: { + var overflow: ?usize = null; + last_tag_val = if (last_tag_val) |val| + try sema.intAdd(val, try mod.intValue(int_tag_ty, 1), int_tag_ty, &overflow) else - Value.zero; - last_tag_val = tag_val; - const copied_tag_val = try tag_val.copy(decl_arena_allocator); - const gop_val = enum_obj.values.getOrPutAssumeCapacityContext(copied_tag_val, .{ - .ty = enum_obj.tag_ty, - .mod = mod, - }); - if (gop_val.found_existing) { - const field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy; - const other_field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = gop_val.index }).lazy; + try mod.intValue(int_tag_ty, 0); + if (overflow != null) break :overflow true; + if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, last_tag_val.?.toIntern())) |other_index| { + const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy; + const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; const msg = msg: { - const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{tag_val.fmtValue(enum_obj.tag_ty, sema.mod)}); + const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)}); errdefer msg.destroy(gpa); try sema.errNote(block, other_field_src, msg, "other occurrence here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } - } else { - tag_val_buf = .{ - .base = .{ .tag = .int_u64 }, - .data = field_i, - }; - last_tag_val = Value.initPayload(&tag_val_buf.base); - } + break :overflow false; + } else overflow: { + last_tag_val = try mod.intValue(Type.comptime_int, field_i); + if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true; + last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty); + break :overflow false; + }; - if (!(try sema.intFitsInType(last_tag_val.?, enum_obj.tag_ty, null))) { - const value_src = enum_obj.fieldSrcLoc(sema.mod, .{ + if (tag_overflow) { + const value_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i, .range = if (has_tag_value) .value else .name, }).lazy; const msg = try sema.errMsg(block, value_src, "enumeration value '{}' too large for type '{}'", .{ - last_tag_val.?.fmtValue(enum_obj.tag_ty, mod), enum_obj.tag_ty.fmt(mod), + last_tag_val.?.fmtValue(int_tag_ty, mod), int_tag_ty.fmt(mod), }); return sema.failWithOwnedErrorMsg(msg); } @@ -3070,6 +3211,8 @@ fn zirUnionDecl( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; + const gpa = sema.gpa; const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); var extra_index: usize = extended.operand; @@ -3089,55 +3232,60 @@ fn zirUnionDecl( break :blk decls_len; } else 0; - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); - - const union_obj = try new_decl_arena_allocator.create(Module.Union); - const type_tag = if (small.has_tag_type or small.auto_enum_tag) - Type.Tag.union_tagged - else if (small.layout != .Auto) - Type.Tag.@"union" - else switch (block.sema.mod.optimizeMode()) { - .Debug, .ReleaseSafe => Type.Tag.union_safety_tagged, - .ReleaseFast, .ReleaseSmall => Type.Tag.@"union", - }; - const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union); - union_payload.* = .{ - .base = .{ .tag = type_tag }, - .data = union_obj, - }; - const union_ty = Type.initPayload(&union_payload.base); - const union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); - const mod = sema.mod; + // Because these three things each reference each other, `undefined` + // placeholders are used before being set after the union type gains an + // InternPool index. + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.type, - .val = union_val, + .ty = Type.noreturn, + .val = Value.@"unreachable", }, small.name_strategy, "union", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); - union_obj.* = .{ + + const new_namespace_index = try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .ty = undefined, + .file_scope = block.getFileScope(mod), + }); + const new_namespace = mod.namespacePtr(new_namespace_index); + errdefer mod.destroyNamespace(new_namespace_index); + + const union_index = try mod.createUnion(.{ .owner_decl = new_decl_index, - .tag_ty = Type.initTag(.null), + .tag_ty = Type.null, .fields = .{}, .zir_index = inst, .layout = small.layout, .status = .none, - .namespace = .{ - .parent = block.namespace, - .ty = union_ty, - .file_scope = block.getFileScope(), - }, - }; - std.log.scoped(.module).debug("create union {*} owned by {*} ({s})", .{ - &union_obj.namespace, new_decl, new_decl.name, + .namespace = new_namespace_index, }); - - _ = try mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl); - - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + errdefer mod.destroyUnion(union_index); + + const union_ty = try mod.intern_pool.get(gpa, .{ .union_type = .{ + .index = union_index, + .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) + .tagged + else if (small.layout != .Auto) + .none + else switch (block.sema.mod.optimizeMode()) { + .Debug, .ReleaseSafe => .safety, + .ReleaseFast, .ReleaseSmall => .none, + }, + } }); + // TODO: figure out InternPool removals for incremental compilation + //errdefer mod.intern_pool.remove(union_ty); + + new_decl.ty = Type.type; + new_decl.val = union_ty.toValue(); + new_namespace.ty = union_ty.toType(); + + _ = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); + + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; } fn zirOpaqueDecl( @@ -3150,7 +3298,6 @@ fn zirOpaqueDecl( defer tracy.end(); const mod = sema.mod; - const gpa = sema.gpa; const small = @bitCast(Zir.Inst.OpaqueDecl.Small, extended.small); var extra_index: usize = extended.operand; @@ -3166,42 +3313,42 @@ fn zirOpaqueDecl( break :blk decls_len; } else 0; - var new_decl_arena = std.heap.ArenaAllocator.init(gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); + // Because these three things each reference each other, `undefined` + // placeholders are used in two places before being set after the opaque + // type gains an InternPool index. - const opaque_obj = try new_decl_arena_allocator.create(Module.Opaque); - const opaque_ty_payload = try new_decl_arena_allocator.create(Type.Payload.Opaque); - opaque_ty_payload.* = .{ - .base = .{ .tag = .@"opaque" }, - .data = opaque_obj, - }; - const opaque_ty = Type.initPayload(&opaque_ty_payload.base); - const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.type, - .val = opaque_val, + .ty = Type.noreturn, + .val = Value.@"unreachable", }, small.name_strategy, "opaque", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); - opaque_obj.* = .{ - .owner_decl = new_decl_index, - .namespace = .{ - .parent = block.namespace, - .ty = opaque_ty, - .file_scope = block.getFileScope(), - }, - }; - std.log.scoped(.module).debug("create opaque {*} owned by {*} ({s})", .{ - &opaque_obj.namespace, new_decl, new_decl.name, + const new_namespace_index = try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .ty = undefined, + .file_scope = block.getFileScope(mod), }); + const new_namespace = mod.namespacePtr(new_namespace_index); + errdefer mod.destroyNamespace(new_namespace_index); + + const opaque_ty = try mod.intern(.{ .opaque_type = .{ + .decl = new_decl_index, + .namespace = new_namespace_index, + } }); + // TODO: figure out InternPool removals for incremental compilation + //errdefer mod.intern_pool.remove(opaque_ty); - extra_index = try mod.scanNamespace(&opaque_obj.namespace, extra_index, decls_len, new_decl); + new_decl.ty = Type.type; + new_decl.val = opaque_ty.toValue(); + new_namespace.ty = opaque_ty.toType(); - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); + + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; } fn zirErrorSetDecl( @@ -3213,48 +3360,39 @@ fn zirErrorSetDecl( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const gpa = sema.gpa; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index); - var new_decl_arena = std.heap.ArenaAllocator.init(gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); - - const error_set = try new_decl_arena_allocator.create(Module.ErrorSet); - const error_set_ty = try Type.Tag.error_set.create(new_decl_arena_allocator, error_set); - const error_set_val = try Value.Tag.ty.create(new_decl_arena_allocator, error_set_ty); - const mod = sema.mod; - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.type, - .val = error_set_val, - }, name_strategy, "error", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl_index); - - var names = Module.ErrorSet.NameMap{}; - try names.ensureUnusedCapacity(new_decl_arena_allocator, extra.data.fields_len); + var names: Module.Fn.InferredErrorSet.NameMap = .{}; + try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len); var extra_index = @intCast(u32, extra.end); const extra_index_end = extra_index + (extra.data.fields_len * 2); while (extra_index < extra_index_end) : (extra_index += 2) { // +2 to skip over doc_string const str_index = sema.code.extra[extra_index]; - const kv = try mod.getErrorValue(sema.code.nullTerminatedString(str_index)); - const result = names.getOrPutAssumeCapacity(kv.key); + const name = sema.code.nullTerminatedString(str_index); + const name_ip = try mod.intern_pool.getOrPutString(gpa, name); + _ = try mod.getErrorValue(name_ip); + const result = names.getOrPutAssumeCapacity(name_ip); assert(!result.found_existing); // verified in AstGen } - // names must be sorted. - Module.ErrorSet.sortNames(&names); + const error_set_ty = try mod.errorSetFromUnsortedNames(names.keys()); - error_set.* = .{ - .owner_decl = new_decl_index, - .names = names, - }; - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ + .ty = Type.type, + .val = error_set_ty.toValue(), + }, name_strategy, "error", inst); + const new_decl = mod.declPtr(new_decl_index); + new_decl.owns_tv = true; + errdefer mod.abortAnonDecl(new_decl_index); + + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; } fn zirRetPtr(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { @@ -3308,7 +3446,8 @@ fn ensureResultUsed( ty: Type, src: LazySrcLoc, ) CompileError!void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .Void, .NoReturn => return, .ErrorSet, .ErrorUnion => { const msg = msg: { @@ -3336,11 +3475,12 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand = try sema.resolveInst(inst_data.operand); const src = inst_data.src(); const operand_ty = sema.typeOf(operand); - switch (operand_ty.zigTypeTag()) { + switch (operand_ty.zigTypeTag(mod)) { .ErrorSet, .ErrorUnion => { const msg = msg: { const msg = try sema.errMsg(block, src, "error is discarded", .{}); @@ -3358,16 +3498,17 @@ fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - const err_union_ty = if (operand_ty.zigTypeTag() == .Pointer) - operand_ty.childType() + const err_union_ty = if (operand_ty.zigTypeTag(mod) == .Pointer) + operand_ty.childType(mod) else operand_ty; - if (err_union_ty.zigTypeTag() != .ErrorUnion) return; - const payload_ty = err_union_ty.errorUnionPayload().zigTypeTag(); + if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) return; + const payload_ty = err_union_ty.errorUnionPayload(mod).zigTypeTag(mod); if (payload_ty != .Void and payload_ty != .NoReturn) { const msg = msg: { const msg = try sema.errMsg(block, src, "error union payload is ignored", .{}); @@ -3396,30 +3537,27 @@ fn indexablePtrLen( src: LazySrcLoc, object: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const object_ty = sema.typeOf(object); - const is_pointer_to = object_ty.isSinglePointer(); - const array_ty = if (is_pointer_to) object_ty.childType() else object_ty; - try checkIndexable(sema, block, src, array_ty); - return sema.fieldVal(block, src, object, "len", src); + const is_pointer_to = object_ty.isSinglePointer(mod); + const indexable_ty = if (is_pointer_to) object_ty.childType(mod) else object_ty; + try checkIndexable(sema, block, src, indexable_ty); + const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len"); + return sema.fieldVal(block, src, object, field_name, src); } fn indexablePtrLenOrNone( sema: *Sema, block: *Block, src: LazySrcLoc, - object: Air.Inst.Ref, + operand: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { - const object_ty = sema.typeOf(object); - const array_ty = t: { - const ptr_size = object_ty.ptrSizeOrNull() orelse break :t object_ty; - break :t switch (ptr_size) { - .Many => return .none, - .One => object_ty.childType(), - else => object_ty, - }; - }; - try checkIndexable(sema, block, src, array_ty); - return sema.fieldVal(block, src, object, "len", src); + const mod = sema.mod; + const operand_ty = sema.typeOf(operand); + try checkMemOperand(sema, block, src, operand_ty); + if (operand_ty.ptrSize(mod) == .Many) return .none; + const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len"); + return sema.fieldVal(block, src, operand, field_name, src); } fn zirAllocExtended( @@ -3427,6 +3565,7 @@ fn zirAllocExtended( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { + const gpa = sema.gpa; const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand); const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = extra.data.src_node }; const align_src: LazySrcLoc = .{ .node_offset_var_decl_align = extra.data.src_node }; @@ -3447,22 +3586,19 @@ fn zirAllocExtended( break :blk alignment; } else 0; - const inferred_alloc_ty = if (small.is_const) - Type.initTag(.inferred_alloc_const) - else - Type.initTag(.inferred_alloc_mut); - if (block.is_comptime or small.is_comptime) { if (small.has_type) { return sema.analyzeComptimeAlloc(block, var_ty, alignment); } else { - return sema.addConstant( - inferred_alloc_ty, - try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{ + try sema.air_instructions.append(gpa, .{ + .tag = .inferred_alloc_comptime, + .data = .{ .inferred_alloc_comptime = .{ .decl_index = undefined, - .alignment = alignment, - }), - ); + .alignment = InternPool.Alignment.fromByteUnits(alignment), + .is_const = small.is_const, + } }, + }); + return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1)); } } @@ -3480,17 +3616,15 @@ fn zirAllocExtended( return block.addTy(.alloc, ptr_type); } - // `Sema.addConstant` does not add the instruction to the block because it is - // not needed in the case of constant values. However here, we plan to "downgrade" - // to a normal instruction when we hit `resolve_inferred_alloc`. So we append - // to the block even though it is currently a `.constant`. - const result = try sema.addConstant( - inferred_alloc_ty, - try Value.Tag.inferred_alloc.create(sema.arena, .{ .alignment = alignment }), - ); - try block.instructions.append(sema.gpa, Air.refToIndex(result).?); - try sema.unresolved_inferred_allocs.putNoClobber(sema.gpa, Air.refToIndex(result).?, {}); - return result; + const result_index = try block.addInstAsIndex(.{ + .tag = .inferred_alloc, + .data = .{ .inferred_alloc = .{ + .alignment = InternPool.Alignment.fromByteUnits(alignment), + .is_const = small.is_const, + } }, + }); + try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); + return Air.indexToRef(result_index); } fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -3504,11 +3638,12 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr } fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const alloc = try sema.resolveInst(inst_data.operand); const alloc_ty = sema.typeOf(alloc); - var ptr_info = alloc_ty.ptrInfo().data; + var ptr_info = alloc_ty.ptrInfo(mod); const elem_ty = ptr_info.pointee_type; // Detect if all stores to an `.alloc` were comptime-known. @@ -3554,18 +3689,26 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); return sema.analyzeDeclRef(try anon_decl.finish( - try elem_ty.copy(anon_decl.arena()), - try store_val.copy(anon_decl.arena()), + elem_ty, + store_val, ptr_info.@"align", )); } + return sema.makePtrConst(block, alloc); +} + +fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const alloc_ty = sema.typeOf(alloc); + + var ptr_info = alloc_ty.ptrInfo(mod); ptr_info.mutable = false; const const_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); // Detect if a comptime value simply needs to have its type changed. if (try sema.resolveMaybeUndefVal(alloc)) |val| { - return sema.addConstant(const_ptr_ty, val); + return sema.addConstant(const_ptr_ty, try mod.getCoerced(val, const_ptr_ty)); } return block.addBitCast(const_ptr_ty, alloc); @@ -3574,18 +3717,22 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro fn zirAllocInferredComptime( sema: *Sema, inst: Zir.Inst.Index, - inferred_alloc_ty: Type, + is_const: bool, ) CompileError!Air.Inst.Ref { + const gpa = sema.gpa; const src_node = sema.code.instructions.items(.data)[inst].node; const src = LazySrcLoc.nodeOffset(src_node); sema.src = src; - return sema.addConstant( - inferred_alloc_ty, - try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{ + + try sema.air_instructions.append(gpa, .{ + .tag = .inferred_alloc_comptime, + .data = .{ .inferred_alloc_comptime = .{ .decl_index = undefined, - .alignment = 0, - }), - ); + .alignment = .none, + .is_const = is_const, + } }, + }); + return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1)); } fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -3631,103 +3778,103 @@ fn zirAllocInferred( sema: *Sema, block: *Block, inst: Zir.Inst.Index, - inferred_alloc_ty: Type, + is_const: bool, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); + const gpa = sema.gpa; const src_node = sema.code.instructions.items(.data)[inst].node; const src = LazySrcLoc.nodeOffset(src_node); sema.src = src; if (block.is_comptime) { - return sema.addConstant( - inferred_alloc_ty, - try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{ + try sema.air_instructions.append(gpa, .{ + .tag = .inferred_alloc_comptime, + .data = .{ .inferred_alloc_comptime = .{ .decl_index = undefined, - .alignment = 0, - }), - ); + .alignment = .none, + .is_const = is_const, + } }, + }); + return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1)); } - // `Sema.addConstant` does not add the instruction to the block because it is - // not needed in the case of constant values. However here, we plan to "downgrade" - // to a normal instruction when we hit `resolve_inferred_alloc`. So we append - // to the block even though it is currently a `.constant`. - const result = try sema.addConstant( - inferred_alloc_ty, - try Value.Tag.inferred_alloc.create(sema.arena, .{ .alignment = 0 }), - ); - try block.instructions.append(sema.gpa, Air.refToIndex(result).?); - try sema.unresolved_inferred_allocs.putNoClobber(sema.gpa, Air.refToIndex(result).?, {}); - return result; + const result_index = try block.addInstAsIndex(.{ + .tag = .inferred_alloc, + .data = .{ .inferred_alloc = .{ + .alignment = .none, + .is_const = is_const, + } }, + }); + try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); + return Air.indexToRef(result_index); } fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; const ptr = try sema.resolveInst(inst_data.operand); const ptr_inst = Air.refToIndex(ptr).?; - assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant); - const value_index = sema.air_instructions.items(.data)[ptr_inst].ty_pl.payload; - const ptr_val = sema.air_values.items[value_index]; - const var_is_mut = switch (sema.typeOf(ptr).tag()) { - .inferred_alloc_const => false, - .inferred_alloc_mut => true, - else => unreachable, - }; - const target = sema.mod.getTarget(); + const target = mod.getTarget(); - switch (ptr_val.tag()) { + switch (sema.air_instructions.items(.tag)[ptr_inst]) { .inferred_alloc_comptime => { - const iac = ptr_val.castTag(.inferred_alloc_comptime).?; - const decl_index = iac.data.decl_index; - try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index); - - const decl = sema.mod.declPtr(decl_index); - const final_elem_ty = try decl.ty.copy(sema.arena); - const final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = final_elem_ty, - .mutable = true, - .@"align" = iac.data.alignment, - .@"addrspace" = target_util.defaultAddressSpace(target, .local), + const iac = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime; + const decl_index = iac.decl_index; + try mod.declareDeclDependency(sema.owner_decl_index, decl_index); + + const decl = mod.declPtr(decl_index); + if (iac.is_const) try decl.intern(mod); + const final_elem_ty = decl.ty; + const final_ptr_ty = try mod.ptrType(.{ + .child = final_elem_ty.toIntern(), + .flags = .{ + .is_const = false, + .alignment = iac.alignment, + .address_space = target_util.defaultAddressSpace(target, .local), + }, }); - const final_ptr_ty_inst = try sema.addType(final_ptr_ty); - sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst; - if (var_is_mut) { - sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{ - .decl_index = decl_index, - .runtime_index = block.runtime_index, - }); - } else { - sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, decl_index); - } + try sema.maybeQueueFuncBodyAnalysis(decl_index); + // Change it to an interned. + sema.air_instructions.set(ptr_inst, .{ + .tag = .interned, + .data = .{ .interned = try mod.intern(.{ .ptr = .{ + .ty = final_ptr_ty.toIntern(), + .addr = if (!iac.is_const) .{ .mut_decl = .{ + .decl = decl_index, + .runtime_index = block.runtime_index, + } } else .{ .decl = decl_index }, + } }) }, + }); }, .inferred_alloc => { - assert(sema.unresolved_inferred_allocs.remove(ptr_inst)); - const inferred_alloc = ptr_val.castTag(.inferred_alloc).?; - const peer_inst_list = inferred_alloc.data.prongs.items(.stored_inst); + const ia1 = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc; + const ia2 = sema.unresolved_inferred_allocs.fetchRemove(ptr_inst).?.value; + const peer_inst_list = ia2.prongs.items(.stored_inst); const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none); - const final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = final_elem_ty, - .mutable = true, - .@"align" = inferred_alloc.data.alignment, - .@"addrspace" = target_util.defaultAddressSpace(target, .local), + const final_ptr_ty = try mod.ptrType(.{ + .child = final_elem_ty.toIntern(), + .flags = .{ + .alignment = ia1.alignment, + .address_space = target_util.defaultAddressSpace(target, .local), + }, }); - if (var_is_mut) { + if (!ia1.is_const) { try sema.validateVarType(block, ty_src, final_elem_ty, false); } else ct: { // Detect if the value is comptime-known. In such case, the // last 3 AIR instructions of the block will look like this: // - // %a = constant + // %a = inferred_alloc // %b = bitcast(%a) // %c = store(%b, %d) // @@ -3767,42 +3914,46 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com } }; - const const_inst = while (true) { + while (true) { if (search_index == 0) break :ct; search_index -= 1; const candidate = block.instructions.items[search_index]; + if (candidate == ptr_inst) break; switch (air_tags[candidate]) { .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, - .constant => break candidate, else => break :ct, } - }; + } const store_op = air_datas[store_inst].bin_op; const store_val = (try sema.resolveMaybeUndefVal(store_op.rhs)) orelse break :ct; if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct; - if (air_datas[bitcast_inst].ty_op.operand != Air.indexToRef(const_inst)) break :ct; + if (air_datas[bitcast_inst].ty_op.operand != ptr) break :ct; const new_decl_index = d: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); const new_decl_index = try anon_decl.finish( - try final_elem_ty.copy(anon_decl.arena()), - try store_val.copy(anon_decl.arena()), - inferred_alloc.data.alignment, + final_elem_ty, + store_val, + ia1.alignment.toByteUnits(0), ); break :d new_decl_index; }; - try sema.mod.declareDeclDependency(sema.owner_decl_index, new_decl_index); + try mod.declareDeclDependency(sema.owner_decl_index, new_decl_index); // Even though we reuse the constant instruction, we still remove it from the // block so that codegen does not see it. block.instructions.shrinkRetainingCapacity(search_index); - sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl_index); - // if bitcast ty ref needs to be made const, make_ptr_const - // ZIR handles it later, so we can just use the ty ref here. - air_datas[ptr_inst].ty_pl.ty = air_datas[bitcast_inst].ty_op.ty; + try sema.maybeQueueFuncBodyAnalysis(new_decl_index); + sema.air_instructions.set(ptr_inst, .{ + .tag = .interned, + .data = .{ .interned = try mod.intern(.{ .ptr = .{ + .ty = final_ptr_ty.toIntern(), + .addr = .{ .decl = new_decl_index }, + } }) }, + }); // Unless the block is comptime, `alloc_inferred` always produces // a runtime constant. The final inferred type needs to be @@ -3823,19 +3974,19 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com // Now we need to go back over all the coerce_result_ptr instructions, which // previously inserted a bitcast as a placeholder, and do the logic as if // the new result ptr type was available. - const placeholders = inferred_alloc.data.prongs.items(.placeholder); + const placeholders = ia2.prongs.items(.placeholder); const gpa = sema.gpa; var trash_block = block.makeSubBlock(); trash_block.is_comptime = false; - trash_block.is_coerce_result_ptr = true; defer trash_block.instructions.deinit(gpa); - const mut_final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = final_elem_ty, - .mutable = true, - .@"align" = inferred_alloc.data.alignment, - .@"addrspace" = target_util.defaultAddressSpace(target, .local), + const mut_final_ptr_ty = try mod.ptrType(.{ + .child = final_elem_ty.toIntern(), + .flags = .{ + .alignment = ia1.alignment, + .address_space = target_util.defaultAddressSpace(target, .local), + }, }); const dummy_ptr = try trash_block.addTy(.alloc, mut_final_ptr_ty); const empty_trash_count = trash_block.instructions.items.len; @@ -3843,7 +3994,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com for (peer_inst_list, placeholders) |peer_inst, placeholder_inst| { const sub_ptr_ty = sema.typeOf(Air.indexToRef(placeholder_inst)); - if (mut_final_ptr_ty.eql(sub_ptr_ty, sema.mod)) { + if (mut_final_ptr_ty.eql(sub_ptr_ty, mod)) { // New result location type is the same as the old one; nothing // to do here. continue; @@ -3908,27 +4059,28 @@ fn zirArrayBasePtr( block: *Block, inst: Zir.Inst.Index, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const start_ptr = try sema.resolveInst(inst_data.operand); var base_ptr = start_ptr; - while (true) switch (sema.typeOf(base_ptr).childType().zigTypeTag()) { + while (true) switch (sema.typeOf(base_ptr).childType(mod).zigTypeTag(mod)) { .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true), else => break, }; - const elem_ty = sema.typeOf(base_ptr).childType(); - switch (elem_ty.zigTypeTag()) { + const elem_ty = sema.typeOf(base_ptr).childType(mod); + switch (elem_ty.zigTypeTag(mod)) { .Array, .Vector => return base_ptr, - .Struct => if (elem_ty.isTuple()) { + .Struct => if (elem_ty.isTuple(mod)) { // TODO validate element count return base_ptr; }, else => {}, } - return sema.failWithArrayInitNotSupported(block, src, sema.typeOf(start_ptr).childType()); + return sema.failWithArrayInitNotSupported(block, src, sema.typeOf(start_ptr).childType(mod)); } fn zirFieldBasePtr( @@ -3936,27 +4088,30 @@ fn zirFieldBasePtr( block: *Block, inst: Zir.Inst.Index, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const start_ptr = try sema.resolveInst(inst_data.operand); var base_ptr = start_ptr; - while (true) switch (sema.typeOf(base_ptr).childType().zigTypeTag()) { + while (true) switch (sema.typeOf(base_ptr).childType(mod).zigTypeTag(mod)) { .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true), else => break, }; - const elem_ty = sema.typeOf(base_ptr).childType(); - switch (elem_ty.zigTypeTag()) { + const elem_ty = sema.typeOf(base_ptr).childType(mod); + switch (elem_ty.zigTypeTag(mod)) { .Struct, .Union => return base_ptr, else => {}, } - return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType()); + return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType(mod)); } fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const gpa = sema.gpa; + const ip = &mod.intern_pool; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); const args = sema.code.refSlice(extra.end, extra.data.operands_len); @@ -3979,7 +4134,7 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const object_ty = sema.typeOf(object); // Each arg could be an indexable, or a range, in which case the length // is passed directly as an integer. - const is_int = switch (object_ty.zigTypeTag()) { + const is_int = switch (object_ty.zigTypeTag(mod)) { .Int, .ComptimeInt => true, else => false, }; @@ -3988,10 +4143,19 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. .input_index = i, } }; const arg_len_uncoerced = if (is_int) object else l: { - try checkIndexable(sema, block, arg_src, object_ty); - if (!object_ty.indexableHasLen()) continue; + if (!object_ty.isIndexable(mod)) { + // Instead of using checkIndexable we customize this error. + const msg = msg: { + const msg = try sema.errMsg(block, arg_src, "type '{}' is not indexable and not a range", .{object_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, arg_src, msg, "for loop operand must be a range, array, slice, tuple, or vector", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + if (!object_ty.indexableHasLen(mod)) continue; - break :l try sema.fieldVal(block, arg_src, object, "len", arg_src); + break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, "len"), arg_src); }; const arg_len = try sema.coerce(block, Type.usize, arg_len_uncoerced, arg_src); if (len == .none) { @@ -4040,7 +4204,7 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const object_ty = sema.typeOf(object); // Each arg could be an indexable, or a range, in which case the length // is passed directly as an integer. - switch (object_ty.zigTypeTag()) { + switch (object_ty.zigTypeTag(mod)) { .Int, .ComptimeInt => continue, else => {}, } @@ -4075,15 +4239,16 @@ fn validateArrayInitTy( block: *Block, inst: Zir.Inst.Index, ) CompileError!void { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const ty_src: LazySrcLoc = .{ .node_offset_init_ty = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data; const ty = try sema.resolveType(block, ty_src, extra.ty); - switch (ty.zigTypeTag()) { + switch (ty.zigTypeTag(mod)) { .Array => { - const array_len = ty.arrayLen(); + const array_len = ty.arrayLen(mod); if (extra.init_count != array_len) { return sema.fail(block, src, "expected {d} array elements; found {d}", .{ array_len, extra.init_count, @@ -4092,7 +4257,7 @@ fn validateArrayInitTy( return; }, .Vector => { - const array_len = ty.arrayLen(); + const array_len = ty.arrayLen(mod); if (extra.init_count != array_len) { return sema.fail(block, src, "expected {d} vector elements; found {d}", .{ array_len, extra.init_count, @@ -4100,9 +4265,9 @@ fn validateArrayInitTy( } return; }, - .Struct => if (ty.isTuple()) { + .Struct => if (ty.isTuple(mod)) { _ = try sema.resolveTypeFields(ty); - const array_len = ty.arrayLen(); + const array_len = ty.arrayLen(mod); if (extra.init_count > array_len) { return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{ array_len, extra.init_count, @@ -4120,11 +4285,12 @@ fn validateStructInitTy( block: *Block, inst: Zir.Inst.Index, ) CompileError!void { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const ty = try sema.resolveType(block, src, inst_data.operand); - switch (ty.zigTypeTag()) { + switch (ty.zigTypeTag(mod)) { .Struct, .Union => return, else => {}, } @@ -4139,6 +4305,7 @@ fn zirValidateStructInit( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const validate_inst = sema.code.instructions.items(.data)[inst].pl_node; const init_src = validate_inst.src(); const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); @@ -4146,8 +4313,8 @@ fn zirValidateStructInit( const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; const object_ptr = try sema.resolveInst(field_ptr_extra.lhs); - const agg_ty = sema.typeOf(object_ptr).childType(); - switch (agg_ty.zigTypeTag()) { + const agg_ty = sema.typeOf(object_ptr).childType(mod); + switch (agg_ty.zigTypeTag(mod)) { .Struct => return sema.validateStructInit( block, agg_ty, @@ -4173,6 +4340,9 @@ fn validateUnionInit( instrs: []const Zir.Inst.Index, union_ptr: Air.Inst.Ref, ) CompileError!void { + const mod = sema.mod; + const gpa = sema.gpa; + if (instrs.len != 1) { const msg = msg: { const msg = try sema.errMsg( @@ -4181,7 +4351,7 @@ fn validateUnionInit( "cannot initialize multiple union fields at once; unions can only have one active field", .{}, ); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); for (instrs[1..]) |inst| { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; @@ -4205,7 +4375,7 @@ fn validateUnionInit( const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node; const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node }; const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; - const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start); + const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_ptr_extra.field_name_start)); // Validate the field access but ignore the index since we want the tag enum field index. _ = try sema.unionFieldIndex(block, union_ty, field_name, field_src); const air_tags = sema.air_instructions.items(.tag); @@ -4270,21 +4440,25 @@ fn validateUnionInit( break; } - const tag_ty = union_ty.unionTagTypeHypothetical(); - const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?); - const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); + const tag_ty = union_ty.unionTagTypeHypothetical(mod); + const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name, mod).?); + const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); if (init_val) |val| { // Our task is to delete all the `field_ptr` and `store` instructions, and insert // instead a single `store` to the result ptr with a comptime union value. block.instructions.shrinkRetainingCapacity(first_block_index); - var union_val = try Value.Tag.@"union".create(sema.arena, .{ - .tag = tag_val, - .val = val, - }); - if (make_runtime) union_val = try Value.Tag.runtime_value.create(sema.arena, union_val); - const union_init = try sema.addConstant(union_ty, union_val); + var union_val = try mod.intern(.{ .un = .{ + .ty = union_ty.toIntern(), + .tag = tag_val.toIntern(), + .val = val.toIntern(), + } }); + if (make_runtime) union_val = try mod.intern(.{ .runtime_value = .{ + .ty = union_ty.toIntern(), + .val = union_val, + } }); + const union_init = try sema.addConstant(union_ty, union_val.toValue()); try sema.storePtr2(block, init_src, union_ptr, init_src, union_init, init_src, .store); return; } else if (try sema.typeRequiresComptime(union_ty)) { @@ -4302,10 +4476,12 @@ fn validateStructInit( init_src: LazySrcLoc, instrs: []const Zir.Inst.Index, ) CompileError!void { + const mod = sema.mod; const gpa = sema.gpa; + const ip = &mod.intern_pool; // Maps field index to field_ptr index of where it was already initialized. - const found_fields = try gpa.alloc(Zir.Inst.Index, struct_ty.structFieldCount()); + const found_fields = try gpa.alloc(Zir.Inst.Index, struct_ty.structFieldCount(mod)); defer gpa.free(found_fields); @memset(found_fields, 0); @@ -4316,8 +4492,11 @@ fn validateStructInit( const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node }; const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; struct_ptr_zir_ref = field_ptr_extra.lhs; - const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start); - const field_index = if (struct_ty.isTuple()) + const field_name = try ip.getOrPutString( + gpa, + sema.code.nullTerminatedString(field_ptr_extra.field_name_start), + ); + const field_index = if (struct_ty.isTuple(mod)) try sema.tupleFieldIndex(block, struct_ty, field_name, field_src) else try sema.structFieldIndex(block, struct_ty, field_name, field_src); @@ -4350,9 +4529,9 @@ fn validateStructInit( for (found_fields, 0..) |field_ptr, i| { if (field_ptr != 0) continue; - const default_val = struct_ty.structFieldDefaultValue(i); - if (default_val.tag() == .unreachable_value) { - if (struct_ty.isTuple()) { + const default_val = struct_ty.structFieldDefaultValue(i, mod); + if (default_val.toIntern() == .unreachable_value) { + if (struct_ty.isTuple(mod)) { const template = "missing tuple field with index {d}"; if (root_msg) |msg| { try sema.errNote(block, init_src, msg, template, .{i}); @@ -4361,9 +4540,9 @@ fn validateStructInit( } continue; } - const field_name = struct_ty.structFieldName(i); - const template = "missing struct field: {s}"; - const args = .{field_name}; + const field_name = struct_ty.structFieldName(i, mod); + const template = "missing struct field: {}"; + const args = .{field_name.fmt(ip)}; if (root_msg) |msg| { try sema.errNote(block, init_src, msg, template, args); } else { @@ -4373,25 +4552,23 @@ fn validateStructInit( } const field_src = init_src; // TODO better source location - const default_field_ptr = if (struct_ty.isTuple()) + const default_field_ptr = if (struct_ty.isTuple(mod)) try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(u32, i), true) else try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty, true); - const field_ty = sema.typeOf(default_field_ptr).childType(); + const field_ty = sema.typeOf(default_field_ptr).childType(mod); const init = try sema.addConstant(field_ty, default_val); try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); } if (root_msg) |msg| { - if (struct_ty.castTag(.@"struct")) |struct_obj| { - const mod = sema.mod; - const fqn = try struct_obj.data.getFullyQualifiedName(mod); - defer gpa.free(fqn); + if (mod.typeToStruct(struct_ty)) |struct_obj| { + const fqn = try struct_obj.getFullyQualifiedName(mod); try mod.errNoteNonLazy( - struct_obj.data.srcLoc(mod), + struct_obj.srcLoc(mod), msg, - "struct '{s}' declared here", - .{fqn}, + "struct '{}' declared here", + .{fqn.fmt(ip)}, ); } root_msg = null; @@ -4411,14 +4588,14 @@ fn validateStructInit( // We collect the comptime field values in case the struct initialization // ends up being comptime-known. - const field_values = try sema.arena.alloc(Value, struct_ty.structFieldCount()); + const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(mod)); field: for (found_fields, 0..) |field_ptr, i| { if (field_ptr != 0) { // Determine whether the value stored to this pointer is comptime-known. - const field_ty = struct_ty.structFieldType(i); + const field_ty = struct_ty.structFieldType(i, mod); if (try sema.typeHasOnePossibleValue(field_ty)) |opv| { - field_values[i] = opv; + field_values[i] = opv.toIntern(); continue; } @@ -4483,7 +4660,7 @@ fn validateStructInit( first_block_index = @min(first_block_index, block_index); } if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(bin_op.rhs, &make_runtime)) |val| { - field_values[i] = val; + field_values[i] = val.toIntern(); } else if (require_comptime) { const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node; return sema.failWithNeededComptime(block, field_ptr_data.src(), "initializer of comptime only struct must be comptime-known"); @@ -4496,9 +4673,9 @@ fn validateStructInit( continue :field; } - const default_val = struct_ty.structFieldDefaultValue(i); - if (default_val.tag() == .unreachable_value) { - if (struct_ty.isTuple()) { + const default_val = struct_ty.structFieldDefaultValue(i, mod); + if (default_val.toIntern() == .unreachable_value) { + if (struct_ty.isTuple(mod)) { const template = "missing tuple field with index {d}"; if (root_msg) |msg| { try sema.errNote(block, init_src, msg, template, .{i}); @@ -4507,9 +4684,9 @@ fn validateStructInit( } continue; } - const field_name = struct_ty.structFieldName(i); - const template = "missing struct field: {s}"; - const args = .{field_name}; + const field_name = struct_ty.structFieldName(i, mod); + const template = "missing struct field: {}"; + const args = .{field_name.fmt(ip)}; if (root_msg) |msg| { try sema.errNote(block, init_src, msg, template, args); } else { @@ -4517,18 +4694,17 @@ fn validateStructInit( } continue; } - field_values[i] = default_val; + field_values[i] = default_val.toIntern(); } if (root_msg) |msg| { - if (struct_ty.castTag(.@"struct")) |struct_obj| { - const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod); - defer gpa.free(fqn); - try sema.mod.errNoteNonLazy( - struct_obj.data.srcLoc(sema.mod), + if (mod.typeToStruct(struct_ty)) |struct_obj| { + const fqn = try struct_obj.getFullyQualifiedName(mod); + try mod.errNoteNonLazy( + struct_obj.srcLoc(mod), msg, - "struct '{s}' declared here", - .{fqn}, + "struct '{}' declared here", + .{fqn.fmt(ip)}, ); } root_msg = null; @@ -4540,9 +4716,15 @@ fn validateStructInit( // instead a single `store` to the struct_ptr with a comptime struct value. block.instructions.shrinkRetainingCapacity(first_block_index); - var struct_val = try Value.Tag.aggregate.create(sema.arena, field_values); - if (make_runtime) struct_val = try Value.Tag.runtime_value.create(sema.arena, struct_val); - const struct_init = try sema.addConstant(struct_ty, struct_val); + var struct_val = try mod.intern(.{ .aggregate = .{ + .ty = struct_ty.toIntern(), + .storage = .{ .elems = field_values }, + } }); + if (make_runtime) struct_val = try mod.intern(.{ .runtime_value = .{ + .ty = struct_ty.toIntern(), + .val = struct_val, + } }); + const struct_init = try sema.addConstant(struct_ty, struct_val.toValue()); try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store); return; } @@ -4553,12 +4735,12 @@ fn validateStructInit( if (field_ptr != 0) continue; const field_src = init_src; // TODO better source location - const default_field_ptr = if (struct_ty.isTuple()) + const default_field_ptr = if (struct_ty.isTuple(mod)) try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(u32, i), true) else try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty, true); - const field_ty = sema.typeOf(default_field_ptr).childType(); - const init = try sema.addConstant(field_ty, field_values[i]); + const field_ty = sema.typeOf(default_field_ptr).childType(mod); + const init = try sema.addConstant(field_ty, field_values[i].toValue()); try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); } } @@ -4568,6 +4750,7 @@ fn zirValidateArrayInit( block: *Block, inst: Zir.Inst.Index, ) CompileError!void { + const mod = sema.mod; const validate_inst = sema.code.instructions.items(.data)[inst].pl_node; const init_src = validate_inst.src(); const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); @@ -4575,18 +4758,18 @@ fn zirValidateArrayInit( const first_elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data; const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr); - const array_ty = sema.typeOf(array_ptr).childType(); - const array_len = array_ty.arrayLen(); + const array_ty = sema.typeOf(array_ptr).childType(mod); + const array_len = array_ty.arrayLen(mod); - if (instrs.len != array_len) switch (array_ty.zigTypeTag()) { + if (instrs.len != array_len) switch (array_ty.zigTypeTag(mod)) { .Struct => { var root_msg: ?*Module.ErrorMsg = null; errdefer if (root_msg) |msg| msg.destroy(sema.gpa); var i = instrs.len; while (i < array_len) : (i += 1) { - const default_val = array_ty.structFieldDefaultValue(i); - if (default_val.tag() == .unreachable_value) { + const default_val = array_ty.structFieldDefaultValue(i, mod); + if (default_val.toIntern() == .unreachable_value) { const template = "missing tuple field with index {d}"; if (root_msg) |msg| { try sema.errNote(block, init_src, msg, template, .{i}); @@ -4621,39 +4804,41 @@ fn zirValidateArrayInit( // at comptime so we have almost nothing to do here. However, in case of a // sentinel-terminated array, the sentinel will not have been populated by // any ZIR instructions at comptime; we need to do that here. - if (array_ty.sentinel()) |sentinel_val| { + if (array_ty.sentinel(mod)) |sentinel_val| { const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len); const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true); - const sentinel = try sema.addConstant(array_ty.childType(), sentinel_val); + const sentinel = try sema.addConstant(array_ty.childType(mod), sentinel_val); try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store); } return; } + // If the array has one possible value, the value is always comptime-known. + if (try sema.typeHasOnePossibleValue(array_ty)) |array_opv| { + const array_init = try sema.addConstant(array_ty, array_opv); + try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store); + return; + } + var array_is_comptime = true; var first_block_index = block.instructions.items.len; var make_runtime = false; // Collect the comptime element values in case the array literal ends up // being comptime-known. - const array_len_s = try sema.usizeCast(block, init_src, array_ty.arrayLenIncludingSentinel()); - const element_vals = try sema.arena.alloc(Value, array_len_s); - const opt_opv = try sema.typeHasOnePossibleValue(array_ty); + const element_vals = try sema.arena.alloc( + InternPool.Index, + try sema.usizeCast(block, init_src, array_len), + ); const air_tags = sema.air_instructions.items(.tag); const air_datas = sema.air_instructions.items(.data); outer: for (instrs, 0..) |elem_ptr, i| { // Determine whether the value stored to this pointer is comptime-known. - if (array_ty.isTuple()) { - if (array_ty.structFieldValueComptime(i)) |opv| { - element_vals[i] = opv; - continue; - } - } else { - // Array has one possible value, so value is always comptime-known - if (opt_opv) |opv| { - element_vals[i] = opv; + if (array_ty.isTuple(mod)) { + if (try array_ty.structFieldValueComptime(mod, i)) |opv| { + element_vals[i] = opv.toIntern(); continue; } } @@ -4714,7 +4899,7 @@ fn zirValidateArrayInit( first_block_index = @min(first_block_index, block_index); } if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(bin_op.rhs, &make_runtime)) |val| { - element_vals[i] = val; + element_vals[i] = val.toIntern(); } else { array_is_comptime = false; } @@ -4726,50 +4911,55 @@ fn zirValidateArrayInit( if (array_is_comptime) { if (try sema.resolveDefinedValue(block, init_src, array_ptr)) |ptr_val| { - if (ptr_val.tag() == .comptime_field_ptr) { - // This store was validated by the individual elem ptrs. - return; + switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) { + .ptr => |ptr| switch (ptr.addr) { + .comptime_field => return, // This store was validated by the individual elem ptrs. + else => {}, + }, + else => {}, } } // Our task is to delete all the `elem_ptr` and `store` instructions, and insert // instead a single `store` to the array_ptr with a comptime struct value. - // Also to populate the sentinel value, if any. - if (array_ty.sentinel()) |sentinel_val| { - element_vals[instrs.len] = sentinel_val; - } - block.instructions.shrinkRetainingCapacity(first_block_index); - var array_val = try Value.Tag.aggregate.create(sema.arena, element_vals); - if (make_runtime) array_val = try Value.Tag.runtime_value.create(sema.arena, array_val); - const array_init = try sema.addConstant(array_ty, array_val); + var array_val = try mod.intern(.{ .aggregate = .{ + .ty = array_ty.toIntern(), + .storage = .{ .elems = element_vals }, + } }); + if (make_runtime) array_val = try mod.intern(.{ .runtime_value = .{ + .ty = array_ty.toIntern(), + .val = array_val, + } }); + const array_init = try sema.addConstant(array_ty, array_val.toValue()); try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store); } } fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - if (operand_ty.zigTypeTag() != .Pointer) { - return sema.fail(block, src, "cannot dereference non-pointer type '{}'", .{operand_ty.fmt(sema.mod)}); - } else switch (operand_ty.ptrSize()) { + if (operand_ty.zigTypeTag(mod) != .Pointer) { + return sema.fail(block, src, "cannot dereference non-pointer type '{}'", .{operand_ty.fmt(mod)}); + } else switch (operand_ty.ptrSize(mod)) { .One, .C => {}, - .Many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{}'", .{operand_ty.fmt(sema.mod)}), - .Slice => return sema.fail(block, src, "index syntax required for slice type '{}'", .{operand_ty.fmt(sema.mod)}), + .Many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{}'", .{operand_ty.fmt(mod)}), + .Slice => return sema.fail(block, src, "index syntax required for slice type '{}'", .{operand_ty.fmt(mod)}), } - if ((try sema.typeHasOnePossibleValue(operand_ty.childType())) != null) { + if ((try sema.typeHasOnePossibleValue(operand_ty.childType(mod))) != null) { // No need to validate the actual pointer value, we don't need it! return; } - const elem_ty = operand_ty.elemType2(); + const elem_ty = operand_ty.elemType2(mod); if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef()) { + if (val.isUndef(mod)) { return sema.fail(block, src, "cannot dereference undefined value", .{}); } } else if (!(try sema.validateRunTimeType(elem_ty, false))) { @@ -4778,12 +4968,12 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr block, src, "values of type '{}' must be comptime-known, but operand value is runtime-known", - .{elem_ty.fmt(sema.mod)}, + .{elem_ty.fmt(mod)}, ); errdefer msg.destroy(sema.gpa); - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(src_decl), elem_ty); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), elem_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -4795,23 +4985,24 @@ fn failWithBadMemberAccess( block: *Block, agg_ty: Type, field_src: LazySrcLoc, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, ) CompileError { - const kw_name = switch (agg_ty.zigTypeTag()) { + const mod = sema.mod; + const kw_name = switch (agg_ty.zigTypeTag(mod)) { .Union => "union", .Struct => "struct", .Opaque => "opaque", .Enum => "enum", else => unreachable, }; - if (agg_ty.getOwnerDeclOrNull()) |some| if (sema.mod.declIsRoot(some)) { - return sema.fail(block, field_src, "root struct of file '{}' has no member named '{s}'", .{ - agg_ty.fmt(sema.mod), field_name, + if (agg_ty.getOwnerDeclOrNull(mod)) |some| if (mod.declIsRoot(some)) { + return sema.fail(block, field_src, "root struct of file '{}' has no member named '{}'", .{ + agg_ty.fmt(mod), field_name.fmt(&mod.intern_pool), }); }; const msg = msg: { - const msg = try sema.errMsg(block, field_src, "{s} '{}' has no member named '{s}'", .{ - kw_name, agg_ty.fmt(sema.mod), field_name, + const msg = try sema.errMsg(block, field_src, "{s} '{}' has no member named '{}'", .{ + kw_name, agg_ty.fmt(mod), field_name.fmt(&mod.intern_pool), }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, agg_ty); @@ -4825,22 +5016,22 @@ fn failWithBadStructFieldAccess( block: *Block, struct_obj: *Module.Struct, field_src: LazySrcLoc, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, ) CompileError { + const mod = sema.mod; const gpa = sema.gpa; - const fqn = try struct_obj.getFullyQualifiedName(sema.mod); - defer gpa.free(fqn); + const fqn = try struct_obj.getFullyQualifiedName(mod); const msg = msg: { const msg = try sema.errMsg( block, field_src, - "no field named '{s}' in struct '{s}'", - .{ field_name, fqn }, + "no field named '{}' in struct '{}'", + .{ field_name.fmt(&mod.intern_pool), fqn.fmt(&mod.intern_pool) }, ); errdefer msg.destroy(gpa); - try sema.mod.errNoteNonLazy(struct_obj.srcLoc(sema.mod), msg, "struct declared here", .{}); + try mod.errNoteNonLazy(struct_obj.srcLoc(mod), msg, "struct declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -4851,30 +5042,31 @@ fn failWithBadUnionFieldAccess( block: *Block, union_obj: *Module.Union, field_src: LazySrcLoc, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, ) CompileError { + const mod = sema.mod; const gpa = sema.gpa; - const fqn = try union_obj.getFullyQualifiedName(sema.mod); - defer gpa.free(fqn); + const fqn = try union_obj.getFullyQualifiedName(mod); const msg = msg: { const msg = try sema.errMsg( block, field_src, - "no field named '{s}' in union '{s}'", - .{ field_name, fqn }, + "no field named '{}' in union '{}'", + .{ field_name.fmt(&mod.intern_pool), fqn.fmt(&mod.intern_pool) }, ); errdefer msg.destroy(gpa); - try sema.mod.errNoteNonLazy(union_obj.srcLoc(sema.mod), msg, "union declared here", .{}); + try mod.errNoteNonLazy(union_obj.srcLoc(mod), msg, "union declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !void { - const src_loc = decl_ty.declSrcLocOrNull(sema.mod) orelse return; - const category = switch (decl_ty.zigTypeTag()) { + const mod = sema.mod; + const src_loc = decl_ty.declSrcLocOrNull(mod) orelse return; + const category = switch (decl_ty.zigTypeTag(mod)) { .Union => "union", .Struct => "struct", .Enum => "enum", @@ -4882,7 +5074,7 @@ fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !vo .ErrorSet => "error set", else => unreachable, }; - try sema.mod.errNoteNonLazy(src_loc, parent, "{s} declared here", .{category}); + try mod.errNoteNonLazy(src_loc, parent, "{s} declared here", .{category}); } fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { @@ -4898,17 +5090,14 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const src: LazySrcLoc = sema.src; blk: { const ptr_inst = Air.refToIndex(ptr) orelse break :blk; - if (sema.air_instructions.items(.tag)[ptr_inst] != .constant) break :blk; - const air_datas = sema.air_instructions.items(.data); - const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload]; - switch (ptr_val.tag()) { + switch (sema.air_instructions.items(.tag)[ptr_inst]) { .inferred_alloc_comptime => { - const iac = ptr_val.castTag(.inferred_alloc_comptime).?; + const iac = &sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime; return sema.storeToInferredAllocComptime(block, src, operand, iac); }, .inferred_alloc => { - const inferred_alloc = ptr_val.castTag(.inferred_alloc).?; - return sema.storeToInferredAlloc(block, ptr, operand, inferred_alloc); + const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?; + return sema.storeToInferredAlloc(block, ptr, operand, ia); }, else => break :blk, } @@ -4926,18 +5115,16 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi const ptr = try sema.resolveInst(bin_inst.lhs); const operand = try sema.resolveInst(bin_inst.rhs); const ptr_inst = Air.refToIndex(ptr).?; - assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant); const air_datas = sema.air_instructions.items(.data); - const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload]; - switch (ptr_val.tag()) { + switch (sema.air_instructions.items(.tag)[ptr_inst]) { .inferred_alloc_comptime => { - const iac = ptr_val.castTag(.inferred_alloc_comptime).?; + const iac = &air_datas[ptr_inst].inferred_alloc_comptime; return sema.storeToInferredAllocComptime(block, src, operand, iac); }, .inferred_alloc => { - const inferred_alloc = ptr_val.castTag(.inferred_alloc).?; - return sema.storeToInferredAlloc(block, ptr, operand, inferred_alloc); + const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?; + return sema.storeToInferredAlloc(block, ptr, operand, ia); }, else => unreachable, } @@ -4948,14 +5135,14 @@ fn storeToInferredAlloc( block: *Block, ptr: Air.Inst.Ref, operand: Air.Inst.Ref, - inferred_alloc: *Value.Payload.InferredAlloc, + inferred_alloc: *InferredAlloc, ) CompileError!void { // Create a store instruction as a placeholder. This will be replaced by a // proper store sequence once we know the stored type. const dummy_store = try block.addBinOp(.store, ptr, operand); // Add the stored instruction to the set we will use to resolve peer types // for the inferred allocation. - try inferred_alloc.data.prongs.append(sema.arena, .{ + try inferred_alloc.prongs.append(sema.arena, .{ .stored_inst = operand, .placeholder = Air.refToIndex(dummy_store).?, }); @@ -4966,20 +5153,21 @@ fn storeToInferredAllocComptime( block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, - iac: *Value.Payload.InferredAllocComptime, + iac: *Air.Inst.Data.InferredAllocComptime, ) CompileError!void { const operand_ty = sema.typeOf(operand); // There will be only one store_to_inferred_ptr because we are running at comptime. // The alloc will turn into a Decl. if (try sema.resolveMaybeUndefValAllowVariables(operand)) |operand_val| store: { - if (operand_val.tag() == .variable) break :store; + if (operand_val.getVariable(sema.mod) != null) break :store; var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); - iac.data.decl_index = try anon_decl.finish( - try operand_ty.copy(anon_decl.arena()), - try operand_val.copy(anon_decl.arena()), - iac.data.alignment, + iac.decl_index = try anon_decl.finish( + operand_ty, + operand_val, + iac.alignment.toByteUnits(0), ); + try sema.comptime_mutable_decls.append(iac.decl_index); return; } @@ -5007,6 +5195,7 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const zir_tags = sema.code.instructions.items(.tag); const zir_datas = sema.code.instructions.items(.data); const inst_data = zir_datas[inst].pl_node; @@ -5025,9 +5214,9 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v // %b = store(%a, %c) // Where %c is an error union or error set. In such case we need to add // to the current function's inferred error set, if any. - if (is_ret and (sema.typeOf(operand).zigTypeTag() == .ErrorUnion or - sema.typeOf(operand).zigTypeTag() == .ErrorSet) and - sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) + if (is_ret and (sema.typeOf(operand).zigTypeTag(mod) == .ErrorUnion or + sema.typeOf(operand).zigTypeTag(mod) == .ErrorSet) and + sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion) { try sema.addToInferredErrorSet(operand); } @@ -5051,47 +5240,30 @@ fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins return sema.addStrLit(block, bytes); } -fn addStrLit(sema: *Sema, block: *Block, zir_bytes: []const u8) CompileError!Air.Inst.Ref { - // `zir_bytes` references memory inside the ZIR module, which can get deallocated - // after semantic analysis is complete, for example in the case of the initialization - // expression of a variable declaration. +fn addStrLit(sema: *Sema, block: *Block, bytes: []const u8) CompileError!Air.Inst.Ref { const mod = sema.mod; const gpa = sema.gpa; - const string_bytes = &mod.string_literal_bytes; - const StringLiteralAdapter = Module.StringLiteralAdapter; - const StringLiteralContext = Module.StringLiteralContext; - try string_bytes.ensureUnusedCapacity(gpa, zir_bytes.len); - const gop = try mod.string_literal_table.getOrPutContextAdapted(gpa, zir_bytes, StringLiteralAdapter{ - .bytes = string_bytes, - }, StringLiteralContext{ - .bytes = string_bytes, + // TODO: write something like getCoercedInts to avoid needing to dupe + const duped_bytes = try sema.arena.dupe(u8, bytes); + const ty = try mod.arrayType(.{ + .len = bytes.len, + .child = .u8_type, + .sentinel = .zero_u8, }); + const val = try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .bytes = duped_bytes }, + } }); + const gop = try mod.memoized_decls.getOrPut(gpa, val); if (!gop.found_existing) { - gop.key_ptr.* = .{ - .index = @intCast(u32, string_bytes.items.len), - .len = @intCast(u32, zir_bytes.len), - }; - string_bytes.appendSliceAssumeCapacity(zir_bytes); - gop.value_ptr.* = .none; + const new_decl_index = try mod.createAnonymousDecl(block, .{ + .ty = ty, + .val = val.toValue(), + }); + gop.value_ptr.* = new_decl_index; + try mod.finalizeAnonDecl(new_decl_index); } - const decl_index = gop.value_ptr.unwrap() orelse di: { - var anon_decl = try block.startAnonDecl(); - defer anon_decl.deinit(); - - const decl_index = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), gop.key_ptr.len), - try Value.Tag.str_lit.create(anon_decl.arena(), gop.key_ptr.*), - 0, // default alignment - ); - - // Needed so that `Decl.clearValues` will additionally set the corresponding - // string literal table value back to `Decl.OptionalIndex.none`. - mod.declPtr(decl_index).owns_tv = true; - - gop.value_ptr.* = decl_index.toOptional(); - break :di decl_index; - }; - return sema.analyzeDeclRef(decl_index); + return sema.analyzeDeclRef(gop.value_ptr.*); } fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -5100,7 +5272,7 @@ fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins defer tracy.end(); const int = sema.code.instructions.items(.data)[inst].int; - return sema.addIntUnsigned(Type.initTag(.comptime_int), int); + return sema.addIntUnsigned(Type.comptime_int, int); } fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -5108,38 +5280,43 @@ fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const tracy = trace(@src()); defer tracy.end(); - const arena = sema.arena; + const mod = sema.mod; const int = sema.code.instructions.items(.data)[inst].str; const byte_count = int.len * @sizeOf(std.math.big.Limb); const limb_bytes = sema.code.string_bytes[int.start..][0..byte_count]; - const limbs = try arena.alloc(std.math.big.Limb, int.len); + + // TODO: this allocation and copy is only needed because the limbs may be unaligned. + // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these + // two lines can be removed. + const limbs = try sema.arena.alloc(std.math.big.Limb, int.len); @memcpy(mem.sliceAsBytes(limbs), limb_bytes); return sema.addConstant( - Type.initTag(.comptime_int), - try Value.Tag.int_big_positive.create(arena, limbs), + Type.comptime_int, + try mod.intValue_big(Type.comptime_int, .{ + .limbs = limbs, + .positive = true, + }), ); } fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { _ = block; - const arena = sema.arena; const number = sema.code.instructions.items(.data)[inst].float; return sema.addConstant( - Type.initTag(.comptime_float), - try Value.Tag.float_64.create(arena, number), + Type.comptime_float, + try sema.mod.floatValue(Type.comptime_float, number), ); } fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { _ = block; - const arena = sema.arena; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data; const number = extra.get(); return sema.addConstant( - Type.initTag(.comptime_float), - try Value.Tag.float_128.create(arena, number), + Type.comptime_float, + try sema.mod.floatValue(Type.comptime_float, number), ); } @@ -5158,7 +5335,9 @@ fn zirCompileLog( sema: *Sema, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { - var managed = sema.mod.compile_log_text.toManaged(sema.gpa); + const mod = sema.mod; + + var managed = mod.compile_log_text.toManaged(sema.gpa); defer sema.mod.compile_log_text = managed.moveToUnmanaged(); const writer = managed.writer(); @@ -5171,19 +5350,18 @@ fn zirCompileLog( const arg = try sema.resolveInst(arg_ref); const arg_ty = sema.typeOf(arg); - if (try sema.resolveMaybeUndefVal(arg)) |val| { - try sema.resolveLazyValue(val); + if (try sema.resolveMaybeUndefLazyVal(arg)) |val| { try writer.print("@as({}, {})", .{ - arg_ty.fmt(sema.mod), val.fmtValue(arg_ty, sema.mod), + arg_ty.fmt(mod), val.fmtValue(arg_ty, mod), }); } else { - try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(sema.mod)}); + try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(mod)}); } } try writer.print("\n", .{}); const decl_index = if (sema.func) |some| some.owner_decl else sema.owner_decl_index; - const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, decl_index); + const gop = try mod.compile_log_decls.getOrPut(sema.gpa, decl_index); if (!gop.found_existing) { gop.value_ptr.* = src_node; } @@ -5198,7 +5376,7 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.I if (block.is_comptime) { return sema.fail(block, src, "encountered @panic at comptime", .{}); } - try sema.panicWithMsg(block, src, msg_inst); + try sema.panicWithMsg(block, msg_inst); return always_noreturn; } @@ -5214,6 +5392,7 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); @@ -5263,7 +5442,7 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError try sema.analyzeBody(&loop_block, body); const loop_block_len = loop_block.instructions.items.len; - if (loop_block_len > 0 and sema.typeOf(Air.indexToRef(loop_block.instructions.items[loop_block_len - 1])).isNoReturn()) { + if (loop_block_len > 0 and sema.typeOf(Air.indexToRef(loop_block.instructions.items[loop_block_len - 1])).isNoReturn(mod)) { // If the loop ended with a noreturn terminator, then there is no way for it to loop, // so we can just use the block instead. try child_block.instructions.appendSlice(gpa, loop_block.instructions.items); @@ -5290,7 +5469,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr // we check this here to avoid undefined symbols if (!@import("build_options").have_llvm) - return sema.fail(parent_block, src, "cannot do C import on Zig compiler not built with LLVM-extension", .{}); + return sema.fail(parent_block, src, "C import unavailable; Zig compiler built without LLVM extensions", .{}); var c_import_buf = std.ArrayList(u8).init(sema.gpa); defer c_import_buf.deinit(); @@ -5333,7 +5512,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr if (!mod.comp.bin_file.options.link_libc) try sema.errNote(&child_block, src, msg, "libc headers not available; compilation does not link against libc", .{}); - const gop = try sema.mod.cimport_errors.getOrPut(sema.gpa, sema.owner_decl_index); + const gop = try mod.cimport_errors.getOrPut(sema.gpa, sema.owner_decl_index); if (!gop.found_existing) { var errs = try std.ArrayListUnmanaged(Module.CImportError).initCapacity(sema.gpa, c_import_res.errors.len); errdefer { @@ -5516,7 +5695,7 @@ fn analyzeBlockBody( // Blocks must terminate with noreturn instruction. assert(child_block.instructions.items.len != 0); - assert(sema.typeOf(Air.indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1])).isNoReturn()); + assert(sema.typeOf(Air.indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1])).isNoReturn(mod)); if (merges.results.items.len == 0) { // No need for a block instruction. We can put the new instructions @@ -5557,7 +5736,7 @@ fn analyzeBlockBody( try sema.errNote(child_block, runtime_src, msg, "runtime control flow here", .{}); const child_src_decl = mod.declPtr(child_block.src_decl); - try sema.explainWhyTypeIsComptime(child_block, type_src, msg, type_src.toSrcLoc(child_src_decl), resolved_ty); + try sema.explainWhyTypeIsComptime(msg, type_src.toSrcLoc(child_src_decl, mod), resolved_ty); break :msg msg; }; @@ -5628,15 +5807,16 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const decl_name = sema.code.nullTerminatedString(extra.decl_name); + const decl_name = try mod.intern_pool.getOrPutString(mod.gpa, sema.code.nullTerminatedString(extra.decl_name)); const decl_index = if (extra.namespace != .none) index_blk: { const container_ty = try sema.resolveType(block, operand_src, extra.namespace); - const container_namespace = container_ty.getNamespace().?; + const container_namespace = container_ty.getNamespaceIndex(mod).unwrap().?; const maybe_index = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false); break :index_blk maybe_index orelse @@ -5650,10 +5830,10 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void else => |e| return e, }; { - try sema.mod.ensureDeclAnalyzed(decl_index); - const exported_decl = sema.mod.declPtr(decl_index); - if (exported_decl.val.castTag(.function)) |some| { - return sema.analyzeExport(block, src, options, some.data.owner_decl); + try mod.ensureDeclAnalyzed(decl_index); + const exported_decl = mod.declPtr(decl_index); + if (exported_decl.val.getFunction(mod)) |function| { + return sema.analyzeExport(block, src, options, function.owner_decl); } } try sema.analyzeExport(block, src, options, decl_index); @@ -5676,17 +5856,14 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }, else => |e| return e, }; - const decl_index = switch (operand.val.tag()) { - .function => operand.val.castTag(.function).?.data.owner_decl, - else => blk: { - var anon_decl = try block.startAnonDecl(); - defer anon_decl.deinit(); - break :blk try anon_decl.finish( - try operand.ty.copy(anon_decl.arena()), - try operand.val.copy(anon_decl.arena()), - 0, - ); - }, + const decl_index = if (operand.val.getFunction(sema.mod)) |function| function.owner_decl else blk: { + var anon_decl = try block.startAnonDecl(); + defer anon_decl.deinit(); + break :blk try anon_decl.finish( + operand.ty, + operand.val, + 0, + ); }; try sema.analyzeExport(block, src, options, decl_index); } @@ -5695,13 +5872,13 @@ pub fn analyzeExport( sema: *Sema, block: *Block, src: LazySrcLoc, - borrowed_options: std.builtin.ExportOptions, + options: Module.Export.Options, exported_decl_index: Decl.Index, ) !void { const Export = Module.Export; const mod = sema.mod; - if (borrowed_options.linkage == .Internal) { + if (options.linkage == .Internal) { return; } @@ -5710,11 +5887,11 @@ pub fn analyzeExport( if (!try sema.validateExternType(exported_decl.ty, .other)) { const msg = msg: { - const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(sema.mod)}); + const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), exported_decl.ty, .other); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), exported_decl.ty, .other); try sema.addDeclaredHereNote(msg, exported_decl.ty); break :msg msg; @@ -5723,14 +5900,15 @@ pub fn analyzeExport( } // TODO: some backends might support re-exporting extern decls - if (exported_decl.isExtern()) { + if (exported_decl.isExtern(mod)) { return sema.fail(block, src, "export target cannot be extern", .{}); } // This decl is alive no matter what, since it's being exported - mod.markDeclAlive(exported_decl); + try mod.markDeclAlive(exported_decl); + try sema.maybeQueueFuncBodyAnalysis(exported_decl_index); - const gpa = mod.gpa; + const gpa = sema.gpa; try mod.decl_exports.ensureUnusedCapacity(gpa, 1); try mod.export_owners.ensureUnusedCapacity(gpa, 1); @@ -5738,19 +5916,8 @@ pub fn analyzeExport( const new_export = try gpa.create(Export); errdefer gpa.destroy(new_export); - const symbol_name = try gpa.dupe(u8, borrowed_options.name); - errdefer gpa.free(symbol_name); - - const section: ?[]const u8 = if (borrowed_options.section) |s| try gpa.dupe(u8, s) else null; - errdefer if (section) |s| gpa.free(s); - new_export.* = .{ - .options = .{ - .name = symbol_name, - .linkage = borrowed_options.linkage, - .section = section, - .visibility = borrowed_options.visibility, - }, + .opts = options, .src = src, .owner_decl = sema.owner_decl_index, .src_decl = block.src_decl, @@ -5776,6 +5943,7 @@ pub fn analyzeExport( } fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const src = LazySrcLoc.nodeOffset(extra.node); @@ -5785,11 +5953,12 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst alignment, }); } - const func = sema.func orelse + const func_index = sema.func_index.unwrap() orelse return sema.fail(block, src, "@setAlignStack outside function body", .{}); + const func = mod.funcPtr(func_index); - const fn_owner_decl = sema.mod.declPtr(func.owner_decl); - switch (fn_owner_decl.ty.fnCallingConvention()) { + const fn_owner_decl = mod.declPtr(func.owner_decl); + switch (fn_owner_decl.ty.fnCallingConvention(mod)) { .Naked => return sema.fail(block, src, "@setAlignStack in naked function", .{}), .Inline => return sema.fail(block, src, "@setAlignStack in inline function", .{}), else => if (block.inlining != null) { @@ -5797,7 +5966,7 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst }, } - const gop = try sema.mod.align_stack_fns.getOrPut(sema.mod.gpa, func); + const gop = try mod.align_stack_fns.getOrPut(sema.gpa, func_index); if (gop.found_existing) { const msg = msg: { const msg = try sema.errMsg(block, src, "multiple @setAlignStack in the same function body", .{}); @@ -5949,10 +6118,11 @@ fn addDbgVar( air_tag: Air.Inst.Tag, name: []const u8, ) CompileError!void { + const mod = sema.mod; const operand_ty = sema.typeOf(operand); switch (air_tag) { .dbg_var_ptr => { - if (!(try sema.typeHasRuntimeBits(operand_ty.childType()))) return; + if (!(try sema.typeHasRuntimeBits(operand_ty.childType(mod)))) return; }, .dbg_var_val => { if (!(try sema.typeHasRuntimeBits(operand_ty))) return; @@ -5981,29 +6151,32 @@ fn addDbgVar( } fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].str_tok; const src = inst_data.src(); - const decl_name = inst_data.get(sema.code); + const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); const decl_index = try sema.lookupIdentifier(block, src, decl_name); try sema.addReferencedBy(block, src, decl_index); return sema.analyzeDeclRef(decl_index); } fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].str_tok; const src = inst_data.src(); - const decl_name = inst_data.get(sema.code); + const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); const decl = try sema.lookupIdentifier(block, src, decl_name); return sema.analyzeDeclVal(block, src, decl); } -fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: []const u8) !Decl.Index { +fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPool.NullTerminatedString) !Decl.Index { + const mod = sema.mod; var namespace = block.namespace; while (true) { if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl_index| { return decl_index; } - namespace = namespace.parent orelse break; + namespace = mod.namespacePtr(namespace).parent.unwrap() orelse break; } unreachable; // AstGen detects use of undeclared identifier errors. } @@ -6014,21 +6187,22 @@ fn lookupInNamespace( sema: *Sema, block: *Block, src: LazySrcLoc, - namespace: *Namespace, - ident_name: []const u8, + namespace_index: Namespace.Index, + ident_name: InternPool.NullTerminatedString, observe_usingnamespace: bool, ) CompileError!?Decl.Index { const mod = sema.mod; - const namespace_decl_index = namespace.getDeclIndex(); - const namespace_decl = sema.mod.declPtr(namespace_decl_index); + const namespace = mod.namespacePtr(namespace_index); + const namespace_decl_index = namespace.getDeclIndex(mod); + const namespace_decl = mod.declPtr(namespace_decl_index); if (namespace_decl.analysis == .file_failure) { try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index); return error.AnalysisFail; } if (observe_usingnamespace and namespace.usingnamespace_set.count() != 0) { - const src_file = block.namespace.file_scope; + const src_file = mod.namespacePtr(block.namespace).file_scope; const gpa = sema.gpa; var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, bool) = .{}; @@ -6047,7 +6221,7 @@ fn lookupInNamespace( // Skip decls which are not marked pub, which are in a different // file than the `a.b`/`@hasDecl` syntax. const decl = mod.declPtr(decl_index); - if (decl.is_pub or (src_file == decl.getFileScope() and checked_namespaces.values()[check_i])) { + if (decl.is_pub or (src_file == decl.getFileScope(mod) and checked_namespaces.values()[check_i])) { try candidates.append(gpa, decl_index); } } @@ -6058,15 +6232,15 @@ fn lookupInNamespace( if (sub_usingnamespace_decl_index == sema.owner_decl_index) continue; const sub_usingnamespace_decl = mod.declPtr(sub_usingnamespace_decl_index); const sub_is_pub = entry.value_ptr.*; - if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope()) { + if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope(mod)) { // Skip usingnamespace decls which are not marked pub, which are in // a different file than the `a.b`/`@hasDecl` syntax. continue; } try sema.ensureDeclAnalyzed(sub_usingnamespace_decl_index); - const ns_ty = sub_usingnamespace_decl.val.castTag(.ty).?.data; - const sub_ns = ns_ty.getNamespace().?; - try checked_namespaces.put(gpa, sub_ns, src_file == sub_usingnamespace_decl.getFileScope()); + const ns_ty = sub_usingnamespace_decl.val.toType(); + const sub_ns = ns_ty.getNamespace(mod).?; + try checked_namespaces.put(gpa, sub_ns, src_file == sub_usingnamespace_decl.getFileScope(mod)); } } @@ -6094,7 +6268,7 @@ fn lookupInNamespace( errdefer msg.destroy(gpa); for (candidates.items) |candidate_index| { const candidate = mod.declPtr(candidate_index); - const src_loc = candidate.srcLoc(); + const src_loc = candidate.srcLoc(mod); try mod.errNoteNonLazy(src_loc, msg, "declared here", .{}); } break :msg msg; @@ -6107,9 +6281,6 @@ fn lookupInNamespace( return decl_index; } - log.debug("{*} ({s}) depends on non-existence of '{s}' in {*} ({s})", .{ - sema.owner_decl, sema.owner_decl.name, ident_name, namespace_decl, namespace_decl.name, - }); // TODO This dependency is too strong. Really, it should only be a dependency // on the non-existence of `ident_name` in the namespace. We can lessen the number of // outdated declarations by making this dependency more sophisticated. @@ -6118,22 +6289,28 @@ fn lookupInNamespace( } fn funcDeclSrc(sema: *Sema, func_inst: Air.Inst.Ref) !?*Decl { + const mod = sema.mod; const func_val = (try sema.resolveMaybeUndefVal(func_inst)) orelse return null; - if (func_val.isUndef()) return null; - const owner_decl_index = switch (func_val.tag()) { - .extern_fn => func_val.castTag(.extern_fn).?.data.owner_decl, - .function => func_val.castTag(.function).?.data.owner_decl, - .decl_ref => sema.mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data.owner_decl, + if (func_val.isUndef(mod)) return null; + const owner_decl_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) { + .extern_func => |extern_func| extern_func.decl, + .func => |func| mod.funcPtr(func.index).owner_decl, + .ptr => |ptr| switch (ptr.addr) { + .decl => |decl| mod.declPtr(decl).val.getFunction(mod).?.owner_decl, + else => return null, + }, else => return null, }; - return sema.mod.declPtr(owner_decl_index); + return mod.declPtr(owner_decl_index); } pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref { + const mod = sema.mod; + const gpa = sema.gpa; const src = sema.src; - if (!sema.mod.backendSupportsFeature(.error_return_trace)) return .none; - if (!sema.mod.comp.bin_file.options.error_return_tracing) return .none; + if (!mod.backendSupportsFeature(.error_return_trace)) return .none; + if (!mod.comp.bin_file.options.error_return_tracing) return .none; if (block.is_comptime) return .none; @@ -6146,7 +6323,8 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, else => |e| return e, }; - const field_index = sema.structFieldIndex(block, stack_trace_ty, "index", src) catch |err| switch (err) { + const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); + const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, src) catch |err| switch (err) { error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, else => |e| return e, }; @@ -6169,6 +6347,8 @@ fn popErrorReturnTrace( operand: Air.Inst.Ref, saved_error_trace_index: Air.Inst.Ref, ) CompileError!void { + const mod = sema.mod; + const gpa = sema.gpa; var is_non_error: ?bool = null; var is_non_error_inst: Air.Inst.Ref = undefined; if (operand != .none) { @@ -6183,15 +6363,16 @@ fn popErrorReturnTrace( const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); - const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty); + const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); - const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, "index", src, stack_trace_ty, true); + const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); + const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true); try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store); } else if (is_non_error == null) { // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need // to pop any error trace that may have been propagated from our arguments. - try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Block).Struct.fields.len); + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len); const cond_block_inst = try block.addInstAsIndex(.{ .tag = .block, .data = .{ @@ -6203,28 +6384,29 @@ fn popErrorReturnTrace( }); var then_block = block.makeSubBlock(); - defer then_block.instructions.deinit(sema.gpa); + defer then_block.instructions.deinit(gpa); // If non-error, then pop the error return trace by restoring the index. const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); - const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty); + const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty); - const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, "index", src, stack_trace_ty, true); + const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); + const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true); try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store); _ = try then_block.addBr(cond_block_inst, Air.Inst.Ref.void_value); // Otherwise, do nothing var else_block = block.makeSubBlock(); - defer else_block.instructions.deinit(sema.gpa); + defer else_block.instructions.deinit(gpa); _ = try else_block.addBr(cond_block_inst, Air.Inst.Ref.void_value); - try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.CondBr).Struct.fields.len + + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + then_block.instructions.items.len + else_block.instructions.items.len + @typeInfo(Air.Block).Struct.fields.len + 1); // +1 for the sole .cond_br instruction in the .block const cond_br_inst = @intCast(Air.Inst.Index, sema.air_instructions.len); - try sema.air_instructions.append(sema.gpa, .{ .tag = .cond_br, .data = .{ .pl_op = .{ + try sema.air_instructions.append(gpa, .{ .tag = .cond_br, .data = .{ .pl_op = .{ .operand = is_non_error_inst, .payload = sema.addExtraAssumeCapacity(Air.CondBr{ .then_body_len = @intCast(u32, then_block.instructions.items.len), @@ -6243,94 +6425,63 @@ fn zirCall( sema: *Sema, block: *Block, inst: Zir.Inst.Index, + comptime kind: enum { direct, field }, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; + const callee_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; const call_src = inst_data.src(); - const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index); + const ExtraType = switch (kind) { + .direct => Zir.Inst.Call, + .field => Zir.Inst.FieldCall, + }; + const extra = sema.code.extraData(ExtraType, inst_data.payload_index); const args_len = extra.data.flags.args_len; const modifier = @intToEnum(std.builtin.CallModifier, extra.data.flags.packed_modifier); const ensure_result_used = extra.data.flags.ensure_result_used; const pop_error_return_trace = extra.data.flags.pop_error_return_trace; - var func = try sema.resolveInst(extra.data.callee); + const callee: ResolvedFieldCallee = switch (kind) { + .direct => .{ .direct = try sema.resolveInst(extra.data.callee) }, + .field => blk: { + const object_ptr = try sema.resolveInst(extra.data.obj_ptr); + const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.data.field_name_start)); + const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; + break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src); + }, + }; var resolved_args: []Air.Inst.Ref = undefined; - var arg_index: u32 = 0; - - const func_type = sema.typeOf(func); - - // Desugar bound functions here var bound_arg_src: ?LazySrcLoc = null; - if (func_type.tag() == .bound_fn) { - bound_arg_src = func_src; - const bound_func = try sema.resolveValue(block, .unneeded, func, ""); - const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data; - func = bound_data.func_inst; - resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len + 1); - resolved_args[arg_index] = bound_data.arg0_inst; - arg_index += 1; - } else { - resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len); + var func: Air.Inst.Ref = undefined; + var arg_index: u32 = 0; + switch (callee) { + .direct => |func_inst| { + resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len); + func = func_inst; + }, + .method => |method| { + resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len + 1); + func = method.func_inst; + resolved_args[0] = method.arg0_inst; + arg_index += 1; + bound_arg_src = callee_src; + }, } - const total_args = args_len + @boolToInt(bound_arg_src != null); const callee_ty = sema.typeOf(func); - const func_ty = func_ty: { - switch (callee_ty.zigTypeTag()) { - .Fn => break :func_ty callee_ty, - .Pointer => { - const ptr_info = callee_ty.ptrInfo().data; - if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Fn) { - break :func_ty ptr_info.pointee_type; - } - }, - else => {}, - } - return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)}); - }; - const func_ty_info = func_ty.fnInfo(); - - const fn_params_len = func_ty_info.param_types.len; - check_args: { - if (func_ty_info.is_var_args) { - assert(func_ty_info.cc == .C); - if (total_args >= fn_params_len) break :check_args; - } else if (fn_params_len == total_args) { - break :check_args; - } - - const maybe_decl = try sema.funcDeclSrc(func); - const member_str = if (bound_arg_src != null) "member function " else ""; - const variadic_str = if (func_ty_info.is_var_args) "at least " else ""; - const msg = msg: { - const msg = try sema.errMsg( - block, - func_src, - "{s}expected {s}{d} argument(s), found {d}", - .{ - member_str, - variadic_str, - fn_params_len - @boolToInt(bound_arg_src != null), - args_len, - }, - ); - errdefer msg.destroy(sema.gpa); - - if (maybe_decl) |fn_decl| try sema.mod.errNoteNonLazy(fn_decl.srcLoc(), msg, "function declared here", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(msg); - } + const total_args = args_len + @boolToInt(bound_arg_src != null); + const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, bound_arg_src != null); const args_body = sema.code.extra[extra.end..]; var input_is_error = false; const block_index = @intCast(Air.Inst.Index, block.instructions.items.len); + const fn_params_len = mod.typeToFunc(func_ty).?.param_types.len; const parent_comptime = block.is_comptime; // `extra_index` and `arg_index` are separate since the bound function is passed as the first argument. var extra_index: usize = 0; @@ -6339,55 +6490,62 @@ fn zirCall( extra_index += 1; arg_index += 1; }) { + const func_ty_info = mod.typeToFunc(func_ty).?; const arg_end = sema.code.extra[extra.end + extra_index]; defer arg_start = arg_end; - const param_ty = if (arg_index >= fn_params_len or - func_ty_info.param_types[arg_index].tag() == .generic_poison) - Type.initTag(.var_args_param) - else - func_ty_info.param_types[arg_index]; - // Generate args to comptime params in comptime block. defer block.is_comptime = parent_comptime; - if (arg_index < fn_params_len and func_ty_info.comptime_params[arg_index]) { + if (arg_index < @min(fn_params_len, 32) and func_ty_info.paramIsComptime(@intCast(u5, arg_index))) { block.is_comptime = true; // TODO set comptime_reason } - const param_ty_inst = try sema.addType(param_ty); - sema.inst_map.putAssumeCapacity(inst, param_ty_inst); + sema.inst_map.putAssumeCapacity(inst, inst: { + if (arg_index >= fn_params_len) + break :inst Air.Inst.Ref.var_args_param_type; + + if (func_ty_info.param_types[arg_index] == .generic_poison_type) + break :inst Air.Inst.Ref.generic_poison_type; + + break :inst try sema.addType(func_ty_info.param_types[arg_index].toType()); + }); const resolved = try sema.resolveBody(block, args_body[arg_start..arg_end], inst); const resolved_ty = sema.typeOf(resolved); - if (resolved_ty.zigTypeTag() == .NoReturn) { + if (resolved_ty.zigTypeTag(mod) == .NoReturn) { return resolved; } - if (resolved_ty.isError()) { + if (resolved_ty.isError(mod)) { input_is_error = true; } resolved_args[arg_index] = resolved; } - if (sema.owner_func == null or !sema.owner_func.?.calls_or_awaits_errorable_fn) + if (sema.owner_func == null or !sema.owner_func.?.calls_or_awaits_errorable_fn) { input_is_error = false; // input was an error type, but no errorable fn's were actually called + } + + // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction. + const call_dbg_node = inst - 1; - if (sema.mod.backendSupportsFeature(.error_return_trace) and sema.mod.comp.bin_file.options.error_return_tracing and + if (mod.backendSupportsFeature(.error_return_trace) and mod.comp.bin_file.options.error_return_tracing and !block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace)) { const call_inst: Air.Inst.Ref = if (modifier == .always_tail) undefined else b: { - break :b try sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src); + break :b try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); }; const return_ty = sema.typeOf(call_inst); - if (modifier != .always_tail and return_ty.isNoReturn()) + if (modifier != .always_tail and return_ty.isNoReturn(mod)) return call_inst; // call to "fn(...) noreturn", don't pop // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only // 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 modifier != .always_tail and return_ty.isError())) { + if (input_is_error or (pop_error_return_trace and modifier != .always_tail and return_ty.isError(mod))) { const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); - const field_index = try sema.structFieldIndex(block, stack_trace_ty, "index", call_src); + const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "index"); + const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src); // Insert a save instruction before the arg resolution + call instructions we just generated const save_inst = try block.insertInst(block_index, .{ @@ -6404,113 +6562,136 @@ fn zirCall( } if (modifier == .always_tail) // Perform the call *after* the restore, so that a tail call is possible. - return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src); + return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); return call_inst; } else { - return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src); + return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); } } -const GenericCallAdapter = struct { - generic_fn: *Module.Fn, - precomputed_hash: u64, - func_ty_info: Type.Payload.Function.Data, - args: []const Arg, - module: *Module, +fn checkCallArgumentCount( + sema: *Sema, + block: *Block, + func: Air.Inst.Ref, + func_src: LazySrcLoc, + callee_ty: Type, + total_args: usize, + member_fn: bool, +) !Type { + const mod = sema.mod; + const func_ty = func_ty: { + switch (callee_ty.zigTypeTag(mod)) { + .Fn => break :func_ty callee_ty, + .Pointer => { + const ptr_info = callee_ty.ptrInfo(mod); + if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag(mod) == .Fn) { + break :func_ty ptr_info.pointee_type; + } + }, + .Optional => { + const opt_child = callee_ty.optionalChild(mod); + if (opt_child.zigTypeTag(mod) == .Fn or (opt_child.isSinglePointer(mod) and + opt_child.childType(mod).zigTypeTag(mod) == .Fn)) + { + const msg = msg: { + const msg = try sema.errMsg(block, func_src, "cannot call optional type '{}'", .{ + callee_ty.fmt(mod), + }); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, func_src, msg, "consider using '.?', 'orelse' or 'if'", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + }, + else => {}, + } + return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(mod)}); + }; + + const func_ty_info = mod.typeToFunc(func_ty).?; + const fn_params_len = func_ty_info.param_types.len; + const args_len = total_args - @boolToInt(member_fn); + if (func_ty_info.is_var_args) { + assert(func_ty_info.cc == .C); + if (total_args >= fn_params_len) return func_ty; + } else if (fn_params_len == total_args) { + return func_ty; + } - const Arg = struct { - ty: Type, - val: Value, - is_anytype: bool, + const maybe_decl = try sema.funcDeclSrc(func); + const member_str = if (member_fn) "member function " else ""; + const variadic_str = if (func_ty_info.is_var_args) "at least " else ""; + const msg = msg: { + const msg = try sema.errMsg( + block, + func_src, + "{s}expected {s}{d} argument(s), found {d}", + .{ + member_str, + variadic_str, + fn_params_len - @boolToInt(member_fn), + args_len, + }, + ); + errdefer msg.destroy(sema.gpa); + + if (maybe_decl) |fn_decl| try mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{}); + break :msg msg; }; + return sema.failWithOwnedErrorMsg(msg); +} - pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool { - _ = adapted_key; - // Checking for equality may happen on an item that has been inserted - // into the map but is not yet fully initialized. In such case, the - // two initialized fields are `hash` and `generic_owner_decl`. - if (ctx.generic_fn.owner_decl != other_key.generic_owner_decl.unwrap().?) return false; - - const other_comptime_args = other_key.comptime_args.?; - for (other_comptime_args[0..ctx.func_ty_info.param_types.len], 0..) |other_arg, i| { - const this_arg = ctx.args[i]; - const this_is_comptime = this_arg.val.tag() != .generic_poison; - const other_is_comptime = other_arg.val.tag() != .generic_poison; - const this_is_anytype = this_arg.is_anytype; - const other_is_anytype = other_key.isAnytypeParam(ctx.module, @intCast(u32, i)); - - if (other_is_anytype != this_is_anytype) return false; - if (other_is_comptime != this_is_comptime) return false; - - if (this_is_anytype) { - // Both are anytype parameters. - if (!this_arg.ty.eql(other_arg.ty, ctx.module)) { - return false; - } - if (this_is_comptime) { - // Both are comptime and anytype parameters with matching types. - if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.module)) { - return false; - } - } - } else if (this_is_comptime) { - // Both are comptime parameters but not anytype parameters. - // We assert no error is possible here because any lazy values must be resolved - // before inserting into the generic function hash map. - const is_eql = Value.eqlAdvanced( - this_arg.val, - this_arg.ty, - other_arg.val, - other_arg.ty, - ctx.module, - null, - ) catch unreachable; - if (!is_eql) { - return false; +fn callBuiltin( + sema: *Sema, + block: *Block, + builtin_fn: Air.Inst.Ref, + modifier: std.builtin.CallModifier, + args: []const Air.Inst.Ref, +) !void { + const mod = sema.mod; + const callee_ty = sema.typeOf(builtin_fn); + const func_ty = func_ty: { + switch (callee_ty.zigTypeTag(mod)) { + .Fn => break :func_ty callee_ty, + .Pointer => { + const ptr_info = callee_ty.ptrInfo(mod); + if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag(mod) == .Fn) { + break :func_ty ptr_info.pointee_type; } - } + }, + else => {}, } - return true; - } + std.debug.panic("type '{}' is not a function calling builtin fn", .{callee_ty.fmt(mod)}); + }; - /// The implementation of the hash is in semantic analysis of function calls, so - /// that any errors when computing the hash can be properly reported. - pub fn hash(ctx: @This(), adapted_key: void) u64 { - _ = adapted_key; - return ctx.precomputed_hash; + const func_ty_info = mod.typeToFunc(func_ty).?; + const fn_params_len = func_ty_info.param_types.len; + if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) { + std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len }); } -}; + _ = try sema.analyzeCall(block, builtin_fn, func_ty, sema.src, sema.src, modifier, false, args, null, null); +} fn analyzeCall( sema: *Sema, block: *Block, func: Air.Inst.Ref, + func_ty: Type, func_src: LazySrcLoc, call_src: LazySrcLoc, modifier: std.builtin.CallModifier, ensure_result_used: bool, uncasted_args: []const Air.Inst.Ref, bound_arg_src: ?LazySrcLoc, + call_dbg_node: ?Zir.Inst.Index, ) CompileError!Air.Inst.Ref { const mod = sema.mod; const callee_ty = sema.typeOf(func); - const func_ty = func_ty: { - switch (callee_ty.zigTypeTag()) { - .Fn => break :func_ty callee_ty, - .Pointer => { - const ptr_info = callee_ty.ptrInfo().data; - if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Fn) { - break :func_ty ptr_info.pointee_type; - } - }, - else => {}, - } - return sema.fail(block, func_src, "type '{}' is not a function", .{callee_ty.fmt(sema.mod)}); - }; - - const func_ty_info = func_ty.fnInfo(); + const func_ty_info = mod.typeToFunc(func_ty).?; + const fn_params_len = func_ty_info.param_types.len; const cc = func_ty_info.cc; if (cc == .Naked) { const maybe_decl = try sema.funcDeclSrc(func); @@ -6523,32 +6704,11 @@ fn analyzeCall( ); errdefer msg.destroy(sema.gpa); - if (maybe_decl) |fn_decl| try sema.mod.errNoteNonLazy(fn_decl.srcLoc(), msg, "function declared here", .{}); + if (maybe_decl) |fn_decl| try mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } - const fn_params_len = func_ty_info.param_types.len; - if (func_ty_info.is_var_args) { - assert(cc == .C); - if (uncasted_args.len < fn_params_len) { - // TODO add error note: declared here - return sema.fail( - block, - func_src, - "expected at least {d} argument(s), found {d}", - .{ fn_params_len, uncasted_args.len }, - ); - } - } else if (fn_params_len != uncasted_args.len) { - // TODO add error note: declared here - return sema.fail( - block, - call_src, - "expected {d} argument(s), found {d}", - .{ fn_params_len, uncasted_args.len }, - ); - } const call_tag: Air.Inst.Tag = switch (modifier) { .auto, @@ -6565,7 +6725,10 @@ fn analyzeCall( }; if (modifier == .never_inline and func_ty_info.cc == .Inline) { - return sema.fail(block, call_src, "no-inline call of inline function", .{}); + return sema.fail(block, call_src, "'never_inline' call of inline function", .{}); + } + if (modifier == .always_inline and func_ty_info.is_noinline) { + return sema.fail(block, call_src, "'always_inline' call of noinline function", .{}); } const gpa = sema.gpa; @@ -6575,7 +6738,7 @@ fn analyzeCall( var comptime_reason_buf: Block.ComptimeReason = undefined; var comptime_reason: ?*const Block.ComptimeReason = null; if (!is_comptime_call) { - if (sema.typeRequiresComptime(func_ty_info.return_type)) |ct| { + if (sema.typeRequiresComptime(func_ty_info.return_type.toType())) |ct| { is_comptime_call = ct; if (ct) { // stage1 can't handle doing this directly @@ -6583,7 +6746,7 @@ fn analyzeCall( .block = block, .func = func, .func_src = func_src, - .return_ty = func_ty_info.return_type, + .return_ty = func_ty_info.return_type.toType(), } }; comptime_reason = &comptime_reason_buf; } @@ -6601,11 +6764,12 @@ fn analyzeCall( func, func_src, call_src, - func_ty_info, + func_ty, ensure_result_used, uncasted_args, call_tag, bound_arg_src, + call_dbg_node, )) |some| { return some; } else |err| switch (err) { @@ -6620,7 +6784,7 @@ fn analyzeCall( .block = block, .func = func, .func_src = func_src, - .return_ty = func_ty_info.return_type, + .return_ty = func_ty_info.return_type.toType(), } }; comptime_reason = &comptime_reason_buf; }, @@ -6637,18 +6801,21 @@ fn analyzeCall( if (err == error.AnalysisFail and comptime_reason != null) try comptime_reason.?.explain(sema, sema.err); return err; }; - const module_fn = switch (func_val.tag()) { - .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data, - .function => func_val.castTag(.function).?.data, - .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{ + const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) { + .extern_func => return sema.fail(block, call_src, "{s} call of extern function", .{ @as([]const u8, if (is_comptime_call) "comptime" else "inline"), }), - else => { - assert(callee_ty.isPtrAtRuntime()); - return sema.fail(block, call_src, "{s} call of function pointer", .{ - @as([]const u8, if (is_comptime_call) "comptime" else "inline"), - }); + .func => |function| function.index, + .ptr => |ptr| switch (ptr.addr) { + .decl => |decl| mod.declPtr(decl).val.getFunctionIndex(mod).unwrap().?, + else => { + assert(callee_ty.isPtrAtRuntime(mod)); + return sema.fail(block, call_src, "{s} call of function pointer", .{ + @as([]const u8, if (is_comptime_call) "comptime" else "inline"), + }); + }, }, + else => unreachable, }; if (func_ty_info.is_var_args) { return sema.fail(block, call_src, "{s} call of variadic function", .{ @@ -6681,8 +6848,9 @@ fn analyzeCall( // In order to save a bit of stack space, directly modify Sema rather // than create a child one. const parent_zir = sema.code; + const module_fn = mod.funcPtr(module_fn_index); const fn_owner_decl = mod.declPtr(module_fn.owner_decl); - sema.code = fn_owner_decl.getFileScope().zir; + sema.code = fn_owner_decl.getFileScope(mod).zir; defer sema.code = parent_zir; try mod.declareDeclDependencyType(sema.owner_decl_index, module_fn.owner_decl, .function_body); @@ -6696,14 +6864,17 @@ fn analyzeCall( } const parent_func = sema.func; + const parent_func_index = sema.func_index; sema.func = module_fn; + sema.func_index = module_fn_index.toOptional(); defer sema.func = parent_func; + defer sema.func_index = parent_func_index; const parent_err_ret_index = sema.error_return_trace_index_on_fn_entry; sema.error_return_trace_index_on_fn_entry = block.error_return_trace_index; defer sema.error_return_trace_index_on_fn_entry = parent_err_ret_index; - var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, fn_owner_decl.src_scope); + var wip_captures = try WipCaptureScope.init(gpa, fn_owner_decl.src_scope); defer wip_captures.deinit(); var child_block: Block = .{ @@ -6726,28 +6897,18 @@ fn analyzeCall( defer child_block.instructions.deinit(gpa); defer merges.deinit(gpa); - // If it's a comptime function call, we need to memoize it as long as no external - // comptime memory is mutated. - var memoized_call_key: Module.MemoizedCall.Key = undefined; - var delete_memoized_call_key = false; - defer if (delete_memoized_call_key) gpa.free(memoized_call_key.args); - if (is_comptime_call) { - memoized_call_key = .{ - .func = module_fn, - .args = try gpa.alloc(TypedValue, func_ty_info.param_types.len), - }; - delete_memoized_call_key = true; - } - try sema.emitBackwardBranch(block, call_src); - // Whether this call should be memoized, set to false if the call can mutate - // comptime state. + // Whether this call should be memoized, set to false if the call can mutate comptime state. var should_memoize = true; - var new_fn_info = fn_owner_decl.ty.fnInfo(); - new_fn_info.param_types = try sema.arena.alloc(Type, new_fn_info.param_types.len); - new_fn_info.comptime_params = (try sema.arena.alloc(bool, new_fn_info.param_types.len)).ptr; + // If it's a comptime function call, we need to memoize it as long as no external + // comptime memory is mutated. + const memoized_arg_values = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); + + var new_fn_info = mod.typeToFunc(fn_owner_decl.ty).?; + new_fn_info.param_types = try sema.arena.alloc(InternPool.Index, new_fn_info.param_types.len); + new_fn_info.comptime_bits = 0; // This will have return instructions analyzed as break instructions to // the block_inst above. Here we are performing "comptime/inline semantic analysis" @@ -6766,31 +6927,31 @@ fn analyzeCall( &child_block, .unneeded, inst, - new_fn_info, + &new_fn_info, &arg_i, uncasted_args, is_comptime_call, &should_memoize, - memoized_call_key, - func_ty_info.param_types, + memoized_arg_values, + mod.typeToFunc(func_ty).?.param_types, func, &has_comptime_args, ) catch |err| switch (err) { error.NeededSourceLocation => { _ = sema.inst_map.remove(inst); - const decl = sema.mod.declPtr(block.src_decl); + const decl = mod.declPtr(block.src_decl); try sema.analyzeInlineCallArg( block, &child_block, - Module.argSrc(call_src.node_offset.x, sema.gpa, decl, arg_i, bound_arg_src), + mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src), inst, - new_fn_info, + &new_fn_info, &arg_i, uncasted_args, is_comptime_call, &should_memoize, - memoized_call_key, - func_ty_info.param_types, + memoized_arg_values, + mod.typeToFunc(func_ty).?.param_types, func, &has_comptime_args, ); @@ -6826,21 +6987,15 @@ fn analyzeCall( // Create a fresh inferred error set type for inline/comptime calls. const fn_ret_ty = blk: { if (module_fn.hasInferredErrorSet(mod)) { - const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode); - node.data = .{ .func = module_fn }; - if (parent_func) |some| { - some.inferred_error_sets.prepend(node); - } - - const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data); - break :blk try Type.Tag.error_union.create(sema.arena, .{ - .error_set = error_set_ty, - .payload = bare_return_type, + const ies_index = try mod.intern_pool.createInferredErrorSet(gpa, .{ + .func = module_fn_index, }); + const error_set_ty = try mod.intern(.{ .inferred_error_set_type = ies_index }); + break :blk try mod.errorUnionType(error_set_ty.toType(), bare_return_type); } break :blk bare_return_type; }; - new_fn_info.return_type = fn_ret_ty; + new_fn_info.return_type = fn_ret_ty.toIntern(); const parent_fn_ret_ty = sema.fn_ret_ty; sema.fn_ret_ty = fn_ret_ty; defer sema.fn_ret_ty = parent_fn_ret_ty; @@ -6849,23 +7004,22 @@ fn analyzeCall( // bug generating invalid LLVM IR. const res2: Air.Inst.Ref = res2: { if (should_memoize and is_comptime_call) { - if (mod.memoized_calls.getContext(memoized_call_key, .{ .module = mod })) |result| { - const ty_inst = try sema.addType(fn_ret_ty); - try sema.air_values.append(gpa, result.val); - sema.air_instructions.set(block_inst, .{ - .tag = .constant, - .data = .{ .ty_pl = .{ - .ty = ty_inst, - .payload = @intCast(u32, sema.air_values.items.len - 1), - } }, - }); - break :res2 Air.indexToRef(block_inst); + if (mod.intern_pool.getIfExists(.{ .memoized_call = .{ + .func = module_fn_index, + .arg_values = memoized_arg_values, + .result = .none, + } })) |memoized_call_index| { + const memoized_call = mod.intern_pool.indexToKey(memoized_call_index).memoized_call; + break :res2 try sema.addConstant( + mod.intern_pool.typeOf(memoized_call.result).toType(), + memoized_call.result.toValue(), + ); } } - const new_func_resolved_ty = try Type.Tag.function.create(sema.arena, new_fn_info); + const new_func_resolved_ty = try mod.funcType(new_fn_info); if (!is_comptime_call and !block.is_typeof) { - try sema.emitDbgInline(block, parent_func.?, module_fn, new_func_resolved_ty, .dbg_inline_begin); + try sema.emitDbgInline(block, parent_func_index.unwrap().?, module_fn_index, new_func_resolved_ty, .dbg_inline_begin); const zir_tags = sema.code.instructions.items(.tag); for (fn_info.param_body) |param| switch (zir_tags[param]) { @@ -6897,7 +7051,7 @@ fn analyzeCall( error.ComptimeReturn => break :result inlining.comptime_result, error.AnalysisFail => { const err_msg = sema.err orelse return err; - if (std.mem.eql(u8, err_msg.msg, recursive_msg)) return err; + if (mem.eql(u8, err_msg.msg, recursive_msg)) return err; try sema.errNote(block, call_src, err_msg, "called from here", .{}); err_msg.clearTrace(sema.gpa); return err; @@ -6907,11 +7061,11 @@ fn analyzeCall( break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges); }; - if (!is_comptime_call and !block.is_typeof and sema.typeOf(result).zigTypeTag() != .NoReturn) { + if (!is_comptime_call and !block.is_typeof and sema.typeOf(result).zigTypeTag(mod) != .NoReturn) { try sema.emitDbgInline( block, - module_fn, - parent_func.?, + module_fn_index, + parent_func_index.unwrap().?, mod.declPtr(parent_func.?.owner_decl).ty, .dbg_inline_end, ); @@ -6922,23 +7076,11 @@ fn analyzeCall( // TODO: check whether any external comptime memory was mutated by the // comptime function call. If so, then do not memoize the call here. - // TODO: re-evaluate whether memoized_calls needs its own arena. I think - // it should be fine to use the Decl arena for the function. - { - var arena_allocator = std.heap.ArenaAllocator.init(gpa); - errdefer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - - for (memoized_call_key.args) |*arg| { - arg.* = try arg.*.copy(arena); - } - - try mod.memoized_calls.putContext(gpa, memoized_call_key, .{ - .val = try result_val.copy(arena), - .arena = arena_allocator.state, - }, .{ .module = mod }); - delete_memoized_call_key = false; - } + _ = try mod.intern(.{ .memoized_call = .{ + .func = module_fn_index, + .arg_values = memoized_arg_values, + .result = try result_val.intern(fn_ret_ty, mod), + } }); } break :res2 result; @@ -6957,7 +7099,7 @@ fn analyzeCall( .func_inst = func, .param_i = @intCast(u32, i), } }; - const param_ty = func_ty.fnParamType(i); + const param_ty = mod.typeToFunc(func_ty).?.param_types[i].toType(); args[i] = sema.analyzeCallArg( block, .unneeded, @@ -6966,10 +7108,10 @@ fn analyzeCall( opts, ) catch |err| switch (err) { error.NeededSourceLocation => { - const decl = sema.mod.declPtr(block.src_decl); + const decl = mod.declPtr(block.src_decl); _ = try sema.analyzeCallArg( block, - Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i, bound_arg_src), + mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src), param_ty, uncasted_arg, opts, @@ -6981,11 +7123,11 @@ fn analyzeCall( } else { args[i] = sema.coerceVarArgParam(block, uncasted_arg, .unneeded) catch |err| switch (err) { error.NeededSourceLocation => { - const decl = sema.mod.declPtr(block.src_decl); + const decl = mod.declPtr(block.src_decl); _ = try sema.coerceVarArgParam( block, uncasted_arg, - Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i, bound_arg_src), + mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src), ); unreachable; }, @@ -6994,11 +7136,19 @@ fn analyzeCall( } } - try sema.queueFullTypeResolution(func_ty_info.return_type); - if (sema.owner_func != null and func_ty_info.return_type.isError()) { + if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); + + try sema.queueFullTypeResolution(func_ty_info.return_type.toType()); + if (sema.owner_func != null and func_ty_info.return_type.toType().isError(mod)) { sema.owner_func.?.calls_or_awaits_errorable_fn = true; } + if (try sema.resolveMaybeUndefVal(func)) |func_val| { + if (mod.intern_pool.indexToFunc(func_val.toIntern()).unwrap()) |func_index| { + try mod.ensureFuncBodyAnalysisQueued(func_index); + } + } + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + args.len); const func_inst = try block.addInst(.{ @@ -7011,30 +7161,55 @@ fn analyzeCall( } }, }); sema.appendRefsAssumeCapacity(args); + + if (call_tag == .call_always_tail) { + if (ensure_result_used) { + try sema.ensureResultUsed(block, sema.typeOf(func_inst), call_src); + } + return sema.handleTailCall(block, call_src, func_ty, func_inst); + } + if (block.wantSafety() and func_ty_info.return_type == .noreturn_type) skip_safety: { + // Function pointers and extern functions aren't guaranteed to + // actually be noreturn so we add a safety check for them. + if (try sema.resolveMaybeUndefVal(func)) |func_val| { + switch (mod.intern_pool.indexToKey(func_val.toIntern())) { + .func => break :skip_safety, + .ptr => |ptr| switch (ptr.addr) { + .decl => |decl| if (!mod.declPtr(decl).isExtern(mod)) break :skip_safety, + else => {}, + }, + else => {}, + } + } + try sema.safetyPanic(block, .noreturn_returned); + return Air.Inst.Ref.unreachable_value; + } + if (func_ty_info.return_type == .noreturn_type) { + _ = try block.addNoOp(.unreach); + return Air.Inst.Ref.unreachable_value; + } break :res func_inst; }; if (ensure_result_used) { try sema.ensureResultUsed(block, sema.typeOf(result), call_src); } - if (call_tag == .call_always_tail) { - return sema.handleTailCall(block, call_src, func_ty, result); - } return result; } fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref { - const target = sema.mod.getTarget(); - const backend = sema.mod.comp.getZigBackend(); + const mod = sema.mod; + const target = mod.getTarget(); + const backend = mod.comp.getZigBackend(); if (!target_util.supportsTailCall(target, backend)) { return sema.fail(block, call_src, "unable to perform tail call: compiler backend '{s}' does not support tail calls on target architecture '{s}' with the selected CPU feature flags", .{ @tagName(backend), @tagName(target.cpu.arch), }); } - const func_decl = sema.mod.declPtr(sema.owner_func.?.owner_decl); - if (!func_ty.eql(func_decl.ty, sema.mod)) { + const func_decl = mod.declPtr(sema.owner_func.?.owner_decl); + if (!func_ty.eql(func_decl.ty, mod)) { return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{}' does not match type of calling function '{}'", .{ - func_ty.fmt(sema.mod), func_decl.ty.fmt(sema.mod), + func_ty.fmt(mod), func_decl.ty.fmt(mod), }); } _ = try block.addUnOp(.ret, result); @@ -7047,16 +7222,17 @@ fn analyzeInlineCallArg( param_block: *Block, arg_src: LazySrcLoc, inst: Zir.Inst.Index, - new_fn_info: Type.Payload.Function.Data, + new_fn_info: *InternPool.Key.FuncType, arg_i: *usize, uncasted_args: []const Air.Inst.Ref, is_comptime_call: bool, should_memoize: *bool, - memoized_call_key: Module.MemoizedCall.Key, - raw_param_types: []const Type, + memoized_arg_values: []InternPool.Index, + raw_param_types: []const InternPool.Index, func_inst: Air.Inst.Ref, has_comptime_args: *bool, ) !void { + const mod = sema.mod; const zir_tags = sema.code.instructions.items(.tag); switch (zir_tags[inst]) { .param_comptime, .param_anytype_comptime => has_comptime_args.* = true, @@ -7072,13 +7248,14 @@ fn analyzeInlineCallArg( const param_body = sema.code.extra[extra.end..][0..extra.data.body_len]; const param_ty = param_ty: { const raw_param_ty = raw_param_types[arg_i.*]; - if (raw_param_ty.tag() != .generic_poison) break :param_ty raw_param_ty; + if (raw_param_ty != .generic_poison_type) break :param_ty raw_param_ty; const param_ty_inst = try sema.resolveBody(param_block, param_body, inst); - break :param_ty try sema.analyzeAsType(param_block, param_src, param_ty_inst); + const param_ty = try sema.analyzeAsType(param_block, param_src, param_ty_inst); + break :param_ty param_ty.toIntern(); }; new_fn_info.param_types[arg_i.*] = param_ty; const uncasted_arg = uncasted_args[arg_i.*]; - if (try sema.typeRequiresComptime(param_ty)) { + if (try sema.typeRequiresComptime(param_ty.toType())) { _ = sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to parameter with comptime-only type must be comptime-known") catch |err| { if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); return err; @@ -7086,7 +7263,7 @@ fn analyzeInlineCallArg( } else if (!is_comptime_call and zir_tags[inst] == .param_comptime) { _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime"); } - const casted_arg = sema.coerceExtra(arg_block, param_ty, uncasted_arg, arg_src, .{ .param_src = .{ + const casted_arg = sema.coerceExtra(arg_block, param_ty.toType(), uncasted_arg, arg_src, .{ .param_src = .{ .func_inst = func_inst, .param_i = @intCast(u32, arg_i.*), } }) catch |err| switch (err) { @@ -7100,24 +7277,20 @@ fn analyzeInlineCallArg( if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); return err; }; - switch (arg_val.tag()) { + switch (arg_val.toIntern()) { .generic_poison, .generic_poison_type => { // This function is currently evaluated as part of an as-of-yet unresolvable // parameter or return type. return error.GenericPoison; }, - else => { - // Needed so that lazy values do not trigger - // assertion due to type not being resolved - // when the hash function is called. - try sema.resolveLazyValue(arg_val); - }, + else => {}, } - should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(); - memoized_call_key.args[arg_i.*] = .{ - .ty = param_ty, - .val = arg_val, - }; + // Needed so that lazy values do not trigger + // assertion due to type not being resolved + // when the hash function is called. + const resolved_arg_val = try sema.resolveLazyValue(arg_val); + should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod); + memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(param_ty.toType(), mod); } else { sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg); } @@ -7131,7 +7304,7 @@ fn analyzeInlineCallArg( .param_anytype, .param_anytype_comptime => { // No coercion needed. const uncasted_arg = uncasted_args[arg_i.*]; - new_fn_info.param_types[arg_i.*] = sema.typeOf(uncasted_arg); + new_fn_info.param_types[arg_i.*] = sema.typeOf(uncasted_arg).toIntern(); if (is_comptime_call) { sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); @@ -7139,24 +7312,20 @@ fn analyzeInlineCallArg( if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); return err; }; - switch (arg_val.tag()) { + switch (arg_val.toIntern()) { .generic_poison, .generic_poison_type => { // This function is currently evaluated as part of an as-of-yet unresolvable // parameter or return type. return error.GenericPoison; }, - else => { - // Needed so that lazy values do not trigger - // assertion due to type not being resolved - // when the hash function is called. - try sema.resolveLazyValue(arg_val); - }, + else => {}, } - should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(); - memoized_call_key.args[arg_i.*] = .{ - .ty = sema.typeOf(uncasted_arg), - .val = arg_val, - }; + // Needed so that lazy values do not trigger + // assertion due to type not being resolved + // when the hash function is called. + const resolved_arg_val = try sema.resolveLazyValue(arg_val); + should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod); + memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(sema.typeOf(uncasted_arg), mod); } else { if (zir_tags[inst] == .param_anytype_comptime) { _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime"); @@ -7196,14 +7365,15 @@ fn analyzeGenericCallArg( uncasted_arg: Air.Inst.Ref, comptime_arg: TypedValue, runtime_args: []Air.Inst.Ref, - new_fn_info: Type.Payload.Function.Data, + new_fn_info: InternPool.Key.FuncType, runtime_i: *u32, ) !void { - const is_runtime = comptime_arg.val.tag() == .generic_poison and - comptime_arg.ty.hasRuntimeBits() and + const mod = sema.mod; + const is_runtime = comptime_arg.val.isGenericPoison() and + comptime_arg.ty.hasRuntimeBits(mod) and !(try sema.typeRequiresComptime(comptime_arg.ty)); if (is_runtime) { - const param_ty = new_fn_info.param_types[runtime_i.*]; + const param_ty = new_fn_info.param_types[runtime_i.*].toType(); const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); try sema.queueFullTypeResolution(param_ty); runtime_args[runtime_i.*] = casted_arg; @@ -7213,10 +7383,16 @@ fn analyzeGenericCallArg( } } -fn analyzeGenericCallArgVal(sema: *Sema, block: *Block, arg_src: LazySrcLoc, uncasted_arg: Air.Inst.Ref) !Value { - const arg_val = try sema.resolveValue(block, arg_src, uncasted_arg, "parameter is comptime"); - try sema.resolveLazyValue(arg_val); - return arg_val; +fn analyzeGenericCallArgVal( + sema: *Sema, + block: *Block, + arg_src: LazySrcLoc, + arg_ty: Type, + uncasted_arg: Air.Inst.Ref, + reason: []const u8, +) !Value { + const casted_arg = try sema.coerce(block, arg_ty, uncasted_arg, arg_src); + return sema.resolveLazyValue(try sema.resolveValue(block, arg_src, casted_arg, reason)); } fn instantiateGenericCall( @@ -7225,56 +7401,52 @@ fn instantiateGenericCall( func: Air.Inst.Ref, func_src: LazySrcLoc, call_src: LazySrcLoc, - func_ty_info: Type.Payload.Function.Data, + generic_func_ty: Type, ensure_result_used: bool, uncasted_args: []const Air.Inst.Ref, call_tag: Air.Inst.Tag, bound_arg_src: ?LazySrcLoc, + call_dbg_node: ?Zir.Inst.Index, ) CompileError!Air.Inst.Ref { const mod = sema.mod; const gpa = sema.gpa; const func_val = try sema.resolveConstValue(block, func_src, func, "generic function being called must be comptime-known"); - const module_fn = switch (func_val.tag()) { - .function => func_val.castTag(.function).?.data, - .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data, + const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) { + .func => |function| function.index, + .ptr => |ptr| mod.declPtr(ptr.addr.decl).val.getFunctionIndex(mod).unwrap().?, else => unreachable, }; + const module_fn = mod.funcPtr(module_fn_index); // Check the Module's generic function map with an adapted context, so that we // can match against `uncasted_args` rather than doing the work below to create a // generic Scope only to junk it if it matches an existing instantiation. const fn_owner_decl = mod.declPtr(module_fn.owner_decl); - const namespace = fn_owner_decl.src_namespace; + const namespace_index = fn_owner_decl.src_namespace; + const namespace = mod.namespacePtr(namespace_index); const fn_zir = namespace.file_scope.zir; const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst); const zir_tags = fn_zir.instructions.items(.tag); - // This hash must match `Module.MonomorphedFuncsContext.hash`. - // For parameters explicitly marked comptime and simple parameter type expressions, - // we know whether a parameter is elided from a monomorphed function, and can - // use it in the hash here. However, for parameter type expressions that are not - // explicitly marked comptime and rely on previous parameter comptime values, we - // don't find out until after generating a monomorphed function whether the parameter - // type ended up being a "must-be-comptime-known" type. - var hasher = std.hash.Wyhash.init(0); - std.hash.autoHash(&hasher, module_fn.owner_decl); - - const generic_args = try sema.arena.alloc(GenericCallAdapter.Arg, func_ty_info.param_types.len); - { - var i: usize = 0; + const monomorphed_args = try sema.arena.alloc(InternPool.Index, mod.typeToFunc(generic_func_ty).?.param_types.len); + const callee_index = callee: { + var arg_i: usize = 0; + var monomorphed_arg_i: u32 = 0; + var known_unique = false; for (fn_info.param_body) |inst| { + const generic_func_ty_info = mod.typeToFunc(generic_func_ty).?; var is_comptime = false; var is_anytype = false; switch (zir_tags[inst]) { .param => { - is_comptime = func_ty_info.paramIsComptime(i); + is_comptime = generic_func_ty_info.paramIsComptime(@intCast(u5, arg_i)); }, .param_comptime => { is_comptime = true; }, .param_anytype => { is_anytype = true; - is_comptime = func_ty_info.paramIsComptime(i); + is_comptime = generic_func_ty_info.paramIsComptime(@intCast(u5, arg_i)); }, .param_anytype_comptime => { is_anytype = true; @@ -7283,87 +7455,90 @@ fn instantiateGenericCall( else => continue, } - const arg_ty = sema.typeOf(uncasted_args[i]); + defer arg_i += 1; + const param_ty = generic_func_ty_info.param_types[arg_i]; + const is_generic = !is_anytype and param_ty == .generic_poison_type; + + if (known_unique) { + if (is_comptime or is_anytype or is_generic) { + monomorphed_arg_i += 1; + } + continue; + } + + const uncasted_arg = uncasted_args[arg_i]; + const arg_ty = if (is_generic) mod.monomorphed_funcs.getAdapted( + Module.MonomorphedFuncAdaptedKey{ + .func = module_fn_index, + .args = monomorphed_args[0..monomorphed_arg_i], + }, + Module.MonomorphedFuncsAdaptedContext{ .mod = mod }, + ) orelse { + known_unique = true; + monomorphed_arg_i += 1; + continue; + } else if (is_anytype) sema.typeOf(uncasted_arg).toIntern() else param_ty; + const was_comptime = is_comptime; + if (!is_comptime and try sema.typeRequiresComptime(arg_ty.toType())) is_comptime = true; if (is_comptime or is_anytype) { // Tuple default values are a part of the type and need to be // resolved to hash the type. - try sema.resolveTupleLazyValues(block, call_src, arg_ty); + try sema.resolveTupleLazyValues(block, call_src, arg_ty.toType()); } if (is_comptime) { - const arg_val = sema.analyzeGenericCallArgVal(block, .unneeded, uncasted_args[i]) catch |err| switch (err) { + const casted_arg = sema.analyzeGenericCallArgVal(block, .unneeded, arg_ty.toType(), uncasted_arg, "") catch |err| switch (err) { error.NeededSourceLocation => { - const decl = sema.mod.declPtr(block.src_decl); - const arg_src = Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i, bound_arg_src); - _ = try sema.analyzeGenericCallArgVal(block, arg_src, uncasted_args[i]); + const decl = mod.declPtr(block.src_decl); + const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src); + _ = try sema.analyzeGenericCallArgVal( + block, + arg_src, + arg_ty.toType(), + uncasted_arg, + if (was_comptime) + "parameter is comptime" + else + "argument to parameter with comptime-only type must be comptime-known", + ); unreachable; }, else => |e| return e, }; - arg_val.hashUncoerced(arg_ty, &hasher, mod); - if (is_anytype) { - arg_ty.hashWithHasher(&hasher, mod); - generic_args[i] = .{ - .ty = arg_ty, - .val = arg_val, - .is_anytype = true, - }; - } else { - generic_args[i] = .{ - .ty = arg_ty, - .val = arg_val, - .is_anytype = false, - }; - } - } else if (is_anytype) { - arg_ty.hashWithHasher(&hasher, mod); - generic_args[i] = .{ - .ty = arg_ty, - .val = Value.initTag(.generic_poison), - .is_anytype = true, - }; - } else { - generic_args[i] = .{ - .ty = arg_ty, - .val = Value.initTag(.generic_poison), - .is_anytype = false, - }; + monomorphed_args[monomorphed_arg_i] = casted_arg.toIntern(); + monomorphed_arg_i += 1; + } else if (is_anytype or is_generic) { + monomorphed_args[monomorphed_arg_i] = try mod.intern(.{ .undef = arg_ty }); + monomorphed_arg_i += 1; } - - i += 1; } - } - const precomputed_hash = hasher.final(); + if (!known_unique) { + if (mod.monomorphed_funcs.getAdapted( + Module.MonomorphedFuncAdaptedKey{ + .func = module_fn_index, + .args = monomorphed_args[0..monomorphed_arg_i], + }, + Module.MonomorphedFuncsAdaptedContext{ .mod = mod }, + )) |callee_func| break :callee mod.intern_pool.indexToKey(callee_func).func.index; + } - const adapter: GenericCallAdapter = .{ - .generic_fn = module_fn, - .precomputed_hash = precomputed_hash, - .func_ty_info = func_ty_info, - .args = generic_args, - .module = mod, - }; - const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter); - const callee = if (!gop.found_existing) callee: { - const new_module_func = try gpa.create(Module.Fn); + const new_module_func_index = try mod.createFunc(undefined); + const new_module_func = mod.funcPtr(new_module_func_index); - // This ensures that we can operate on the hash map before the Module.Fn - // struct is fully initialized. - new_module_func.hash = precomputed_hash; new_module_func.generic_owner_decl = module_fn.owner_decl.toOptional(); new_module_func.comptime_args = null; - gop.key_ptr.* = new_module_func; try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); // Create a Decl for the new function. - const src_decl_index = namespace.getDeclIndex(); + const src_decl_index = namespace.getDeclIndex(mod); const src_decl = mod.declPtr(src_decl_index); - const new_decl_index = try mod.allocateNewDecl(namespace, fn_owner_decl.src_node, src_decl.src_scope); + const new_decl_index = try mod.allocateNewDecl(namespace_index, fn_owner_decl.src_node, src_decl.src_scope); const new_decl = mod.declPtr(new_decl_index); // TODO better names for generic function instantiations - const decl_name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{ - fn_owner_decl.name, @enumToInt(new_decl_index), + const decl_name = try mod.intern_pool.getOrPutStringFmt(gpa, "{}__anon_{d}", .{ + fn_owner_decl.name.fmt(&mod.intern_pool), @enumToInt(new_decl_index), }); new_decl.name = decl_name; new_decl.src_line = fn_owner_decl.src_line; @@ -7385,25 +7560,21 @@ fn instantiateGenericCall( assert(new_decl.dependencies.keys().len == 0); try mod.declareDeclDependencyType(new_decl_index, module_fn.owner_decl, .function_body); - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - const new_decl_arena_allocator = new_decl_arena.allocator(); - const new_func = sema.resolveGenericInstantiationType( block, - new_decl_arena_allocator, fn_zir, new_decl, new_decl_index, uncasted_args, - module_fn, - new_module_func, - namespace, - func_ty_info, + monomorphed_arg_i, + module_fn_index, + new_module_func_index, + namespace_index, + generic_func_ty, call_src, bound_arg_src, ) catch |err| switch (err) { error.GenericPoison, error.ComptimeReturn => { - new_decl_arena.deinit(); // Resolving the new function type below will possibly declare more decl dependencies // and so we remove them all here in case of error. for (new_decl.dependencies.keys()) |dep_index| { @@ -7412,16 +7583,10 @@ fn instantiateGenericCall( } assert(namespace.anon_decls.orderedRemove(new_decl_index)); mod.destroyDecl(new_decl_index); - assert(mod.monomorphed_funcs.remove(new_module_func)); - gpa.destroy(new_module_func); + mod.destroyFunc(new_module_func_index); return err; }, else => { - assert(mod.monomorphed_funcs.remove(new_module_func)); - { - errdefer new_decl_arena.deinit(); - try new_decl.finalizeNewArena(&new_decl_arena); - } // TODO look up the compile error that happened here and attach a note to it // pointing here, at the generic instantiation callsite. if (sema.owner_func) |owner_func| { @@ -7432,12 +7597,10 @@ fn instantiateGenericCall( return err; }, }; - errdefer new_decl_arena.deinit(); - try new_decl.finalizeNewArena(&new_decl_arena); break :callee new_func; - } else gop.key_ptr.*; - + }; + const callee = mod.funcPtr(callee_index); callee.branch_quota = @max(callee.branch_quota, sema.branch_quota); const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl); @@ -7445,8 +7608,7 @@ fn instantiateGenericCall( // Make a runtime call to the new function, making sure to omit the comptime args. const comptime_args = callee.comptime_args.?; const func_ty = mod.declPtr(callee.owner_decl).ty; - const new_fn_info = func_ty.fnInfo(); - const runtime_args_len = @intCast(u32, new_fn_info.param_types.len); + const runtime_args_len = @intCast(u32, mod.typeToFunc(func_ty).?.param_types.len); const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len); { var runtime_i: u32 = 0; @@ -7462,18 +7624,18 @@ fn instantiateGenericCall( uncasted_args[total_i], comptime_args[total_i], runtime_args, - new_fn_info, + mod.typeToFunc(func_ty).?, &runtime_i, ) catch |err| switch (err) { error.NeededSourceLocation => { - const decl = sema.mod.declPtr(block.src_decl); + const decl = mod.declPtr(block.src_decl); _ = try sema.analyzeGenericCallArg( block, - Module.argSrc(call_src.node_offset.x, sema.gpa, decl, total_i, bound_arg_src), + mod.argSrc(call_src.node_offset.x, decl, total_i, bound_arg_src), uncasted_args[total_i], comptime_args[total_i], runtime_args, - new_fn_info, + mod.typeToFunc(func_ty).?, &runtime_i, ); unreachable; @@ -7483,13 +7645,17 @@ fn instantiateGenericCall( total_i += 1; } - try sema.queueFullTypeResolution(new_fn_info.return_type); + try sema.queueFullTypeResolution(mod.typeToFunc(func_ty).?.return_type.toType()); } - if (sema.owner_func != null and new_fn_info.return_type.isError()) { + if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); + + if (sema.owner_func != null and mod.typeToFunc(func_ty).?.return_type.toType().isError(mod)) { sema.owner_func.?.calls_or_awaits_errorable_fn = true; } + try mod.ensureFuncBodyAnalysisQueued(callee_index); + try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + runtime_args_len); const result = try block.addInst(.{ @@ -7509,28 +7675,33 @@ fn instantiateGenericCall( if (call_tag == .call_always_tail) { return sema.handleTailCall(block, call_src, func_ty, result); } + if (func_ty.fnReturnType(mod).isNoReturn(mod)) { + _ = try block.addNoOp(.unreach); + return Air.Inst.Ref.unreachable_value; + } return result; } fn resolveGenericInstantiationType( sema: *Sema, block: *Block, - new_decl_arena_allocator: Allocator, fn_zir: Zir, new_decl: *Decl, new_decl_index: Decl.Index, uncasted_args: []const Air.Inst.Ref, - module_fn: *Module.Fn, - new_module_func: *Module.Fn, - namespace: *Namespace, - func_ty_info: Type.Payload.Function.Data, + monomorphed_args_len: u32, + module_fn_index: Module.Fn.Index, + new_module_func: Module.Fn.Index, + namespace: Namespace.Index, + generic_func_ty: Type, call_src: LazySrcLoc, bound_arg_src: ?LazySrcLoc, -) !*Module.Fn { +) !Module.Fn.Index { const mod = sema.mod; const gpa = sema.gpa; const zir_tags = fn_zir.instructions.items(.tag); + const module_fn = mod.funcPtr(module_fn_index); const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst); // Re-run the block that creates the function, with the comptime parameters @@ -7541,23 +7712,26 @@ fn resolveGenericInstantiationType( .mod = mod, .gpa = gpa, .arena = sema.arena, - .perm_arena = new_decl_arena_allocator, .code = fn_zir, .owner_decl = new_decl, .owner_decl_index = new_decl_index, .func = null, + .func_index = .none, .fn_ret_ty = Type.void, .owner_func = null, - .comptime_args = try new_decl_arena_allocator.alloc(TypedValue, uncasted_args.len), + .owner_func_index = .none, + // TODO: fully migrate functions into InternPool + .comptime_args = try mod.tmp_hack_arena.allocator().alloc(TypedValue, uncasted_args.len), .comptime_args_fn_inst = module_fn.zir_body_inst, - .preallocated_new_func = new_module_func, + .preallocated_new_func = new_module_func.toOptional(), .is_generic_instantiation = true, .branch_quota = sema.branch_quota, .branch_count = sema.branch_count, + .comptime_mutable_decls = sema.comptime_mutable_decls, }; defer child_sema.deinit(); - var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope); + var wip_captures = try WipCaptureScope.init(gpa, new_decl.src_scope); defer wip_captures.deinit(); var child_block: Block = .{ @@ -7579,18 +7753,19 @@ fn resolveGenericInstantiationType( var arg_i: usize = 0; for (fn_info.param_body) |inst| { + const generic_func_ty_info = mod.typeToFunc(generic_func_ty).?; var is_comptime = false; var is_anytype = false; switch (zir_tags[inst]) { .param => { - is_comptime = func_ty_info.paramIsComptime(arg_i); + is_comptime = generic_func_ty_info.paramIsComptime(@intCast(u5, arg_i)); }, .param_comptime => { is_comptime = true; }, .param_anytype => { is_anytype = true; - is_comptime = func_ty_info.paramIsComptime(arg_i); + is_comptime = generic_func_ty_info.paramIsComptime(@intCast(u5, arg_i)); }, .param_anytype_comptime => { is_anytype = true; @@ -7608,8 +7783,8 @@ fn resolveGenericInstantiationType( if (try sema.typeRequiresComptime(arg_ty)) { const arg_val = sema.resolveConstValue(block, .unneeded, arg, "") catch |err| switch (err) { error.NeededSourceLocation => { - const decl = sema.mod.declPtr(block.src_decl); - const arg_src = Module.argSrc(call_src.node_offset.x, sema.gpa, decl, arg_i, bound_arg_src); + const decl = mod.declPtr(block.src_decl); + const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src); _ = try sema.resolveConstValue(block, arg_src, arg, "argument to parameter with comptime-only type must be comptime-known"); unreachable; }, @@ -7641,50 +7816,61 @@ fn resolveGenericInstantiationType( const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst); const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable; - const new_func = new_func_val.castTag(.function).?.data; - errdefer new_func.deinit(gpa); + const new_func = new_func_val.getFunctionIndex(mod).unwrap().?; assert(new_func == new_module_func); + const monomorphed_args_index = @intCast(u32, mod.monomorphed_func_keys.items.len); + const monomorphed_args = try mod.monomorphed_func_keys.addManyAsSlice(gpa, monomorphed_args_len); + var monomorphed_arg_i: u32 = 0; + try mod.monomorphed_funcs.ensureUnusedCapacityContext(gpa, monomorphed_args_len + 1, .{ .mod = mod }); + arg_i = 0; for (fn_info.param_body) |inst| { + const generic_func_ty_info = mod.typeToFunc(generic_func_ty).?; var is_comptime = false; + var is_anytype = false; switch (zir_tags[inst]) { .param => { - is_comptime = func_ty_info.paramIsComptime(arg_i); + is_comptime = generic_func_ty_info.paramIsComptime(@intCast(u5, arg_i)); }, .param_comptime => { is_comptime = true; }, .param_anytype => { - is_comptime = func_ty_info.paramIsComptime(arg_i); + is_anytype = true; + is_comptime = generic_func_ty_info.paramIsComptime(@intCast(u5, arg_i)); }, .param_anytype_comptime => { + is_anytype = true; is_comptime = true; }, else => continue, } - // We populate the Type here regardless because it is needed by - // `GenericCallAdapter.eql` as well as function body analysis. - // Whether it is anytype is communicated by `isAnytypeParam`. + const param_ty = generic_func_ty_info.param_types[arg_i]; + const is_generic = !is_anytype and param_ty == .generic_poison_type; + const arg = child_sema.inst_map.get(inst).?; - const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator); + const arg_ty = child_sema.typeOf(arg); - if (try sema.typeRequiresComptime(copied_arg_ty)) { - is_comptime = true; - } + if (is_generic) if (mod.monomorphed_funcs.fetchPutAssumeCapacityContext(.{ + .func = module_fn_index, + .args_index = monomorphed_args_index, + .args_len = monomorphed_arg_i, + }, arg_ty.toIntern(), .{ .mod = mod })) |kv| assert(kv.value == arg_ty.toIntern()); + if (!is_comptime and try sema.typeRequiresComptime(arg_ty)) is_comptime = true; if (is_comptime) { const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(arg) catch unreachable).?; - child_sema.comptime_args[arg_i] = .{ - .ty = copied_arg_ty, - .val = try arg_val.copy(new_decl_arena_allocator), - }; + monomorphed_args[monomorphed_arg_i] = arg_val.toIntern(); + monomorphed_arg_i += 1; + child_sema.comptime_args[arg_i] = .{ .ty = arg_ty, .val = arg_val }; } else { - child_sema.comptime_args[arg_i] = .{ - .ty = copied_arg_ty, - .val = Value.initTag(.generic_poison), - }; + if (is_anytype or is_generic) { + monomorphed_args[monomorphed_arg_i] = try mod.intern(.{ .undef = arg_ty.toIntern() }); + monomorphed_arg_i += 1; + } + child_sema.comptime_args[arg_i] = .{ .ty = arg_ty, .val = Value.generic_poison }; } arg_i += 1; @@ -7693,11 +7879,11 @@ fn resolveGenericInstantiationType( try wip_captures.finalize(); // Populate the Decl ty/val with the function and its type. - new_decl.ty = try child_sema.typeOf(new_func_inst).copy(new_decl_arena_allocator); + new_decl.ty = child_sema.typeOf(new_func_inst); // If the call evaluated to a return type that requires comptime, never mind // our generic instantiation. Instead we need to perform a comptime call. - const new_fn_info = new_decl.ty.fnInfo(); - if (try sema.typeRequiresComptime(new_fn_info.return_type)) { + const new_fn_info = mod.typeToFunc(new_decl.ty).?; + if (try sema.typeRequiresComptime(new_fn_info.return_type.toType())) { return error.ComptimeReturn; } // Similarly, if the call evaluated to a generic type we need to instead @@ -7706,15 +7892,20 @@ fn resolveGenericInstantiationType( return error.GenericPoison; } - new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func); + new_decl.val = (try mod.intern(.{ .func = .{ + .ty = new_decl.ty.toIntern(), + .index = new_func, + } })).toValue(); new_decl.@"align" = 0; new_decl.has_tv = true; new_decl.owns_tv = true; new_decl.analysis = .complete; - log.debug("generic function '{s}' instantiated with type {}", .{ - new_decl.name, new_decl.ty.fmtDebug(), - }); + mod.monomorphed_funcs.putAssumeCapacityNoClobberContext(.{ + .func = module_fn_index, + .args_index = monomorphed_args_index, + .args_len = monomorphed_arg_i, + }, new_decl.val.toIntern(), .{ .mod = mod }); // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field // will be populated, ensuring it will have `analyzeBody` called with the ZIR @@ -7724,46 +7915,46 @@ fn resolveGenericInstantiationType( } fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { - if (!ty.isSimpleTupleOrAnonStruct()) return; - const tuple = ty.tupleFields(); - for (tuple.values, 0..) |field_val, i| { - try sema.resolveTupleLazyValues(block, src, tuple.types[i]); - if (field_val.tag() == .unreachable_value) continue; - try sema.resolveLazyValue(field_val); + const mod = sema.mod; + const tuple = switch (mod.intern_pool.indexToKey(ty.toIntern())) { + .anon_struct_type => |tuple| tuple, + else => return, + }; + for (tuple.types, tuple.values) |field_ty, field_val| { + try sema.resolveTupleLazyValues(block, src, field_ty.toType()); + if (field_val == .none) continue; + // TODO: mutate in intern pool + _ = try sema.resolveLazyValue(field_val.toValue()); } } fn emitDbgInline( sema: *Sema, block: *Block, - old_func: *Module.Fn, - new_func: *Module.Fn, + old_func: Module.Fn.Index, + new_func: Module.Fn.Index, new_func_ty: Type, tag: Air.Inst.Tag, ) CompileError!void { - if (sema.mod.comp.bin_file.options.strip) return; + const mod = sema.mod; + if (mod.comp.bin_file.options.strip) return; // Recursive inline call; no dbg_inline needed. if (old_func == new_func) return; - try sema.air_values.append(sema.gpa, try Value.Tag.function.create(sema.arena, new_func)); _ = try block.addInst(.{ .tag = tag, - .data = .{ .ty_pl = .{ + .data = .{ .ty_fn = .{ .ty = try sema.addType(new_func_ty), - .payload = @intCast(u32, sema.air_values.items.len - 1), + .func = new_func, } }, }); } -fn zirIntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - _ = block; - const tracy = trace(@src()); - defer tracy.end(); - +fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const int_type = sema.code.instructions.items(.data)[inst].int_type; - const ty = try Module.makeIntType(sema.arena, int_type.signedness, int_type.bit_count); - + const ty = try mod.intType(int_type.signedness, int_type.bit_count); return sema.addType(ty); } @@ -7771,43 +7962,46 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; const child_type = try sema.resolveType(block, operand_src, inst_data.operand); - if (child_type.zigTypeTag() == .Opaque) { - return sema.fail(block, operand_src, "opaque type '{}' cannot be optional", .{child_type.fmt(sema.mod)}); - } else if (child_type.zigTypeTag() == .Null) { - return sema.fail(block, operand_src, "type '{}' cannot be optional", .{child_type.fmt(sema.mod)}); + if (child_type.zigTypeTag(mod) == .Opaque) { + return sema.fail(block, operand_src, "opaque type '{}' cannot be optional", .{child_type.fmt(mod)}); + } else if (child_type.zigTypeTag(mod) == .Null) { + return sema.fail(block, operand_src, "type '{}' cannot be optional", .{child_type.fmt(mod)}); } - const opt_type = try Type.optional(sema.arena, child_type); + const opt_type = try Type.optional(sema.arena, child_type, mod); return sema.addType(opt_type); } fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const bin = sema.code.instructions.items(.data)[inst].bin; const indexable_ty = try sema.resolveType(block, .unneeded, bin.lhs); - assert(indexable_ty.isIndexable()); // validated by a previous instruction - if (indexable_ty.zigTypeTag() == .Struct) { - const elem_type = indexable_ty.structFieldType(@enumToInt(bin.rhs)); + assert(indexable_ty.isIndexable(mod)); // validated by a previous instruction + if (indexable_ty.zigTypeTag(mod) == .Struct) { + const elem_type = indexable_ty.structFieldType(@enumToInt(bin.rhs), mod); return sema.addType(elem_type); } else { - const elem_type = indexable_ty.elemType2(); + const elem_type = indexable_ty.elemType2(mod); return sema.addType(elem_type); } } fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const len = try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector length must be comptime-known"); + const len = @intCast(u32, try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector length must be comptime-known")); const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs); try sema.checkVectorElemType(block, elem_type_src, elem_type); - const vector_type = try Type.Tag.vector.create(sema.arena, .{ - .len = @intCast(u32, len), - .elem_type = elem_type, + const vector_type = try mod.vectorType(.{ + .len = len, + .child = elem_type.toIntern(), }); return sema.addType(vector_type); } @@ -7849,9 +8043,10 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil } fn validateArrayElemType(sema: *Sema, block: *Block, elem_type: Type, elem_src: LazySrcLoc) !void { - if (elem_type.zigTypeTag() == .Opaque) { - return sema.fail(block, elem_src, "array of opaque type '{}' not allowed", .{elem_type.fmt(sema.mod)}); - } else if (elem_type.zigTypeTag() == .NoReturn) { + const mod = sema.mod; + if (elem_type.zigTypeTag(mod) == .Opaque) { + return sema.fail(block, elem_src, "array of opaque type '{}' not allowed", .{elem_type.fmt(mod)}); + } else if (elem_type.zigTypeTag(mod) == .NoReturn) { return sema.fail(block, elem_src, "array of 'noreturn' not allowed", .{}); } } @@ -7864,9 +8059,10 @@ fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro if (true) { return sema.failWithUseOfAsync(block, inst_data.src()); } + const mod = sema.mod; const operand_src: LazySrcLoc = .{ .node_offset_anyframe_type = inst_data.src_node }; const return_type = try sema.resolveType(block, operand_src, inst_data.operand); - const anyframe_type = try Type.Tag.anyframe_T.create(sema.arena, return_type); + const anyframe_type = try mod.anyframeType(return_type); return sema.addType(anyframe_type); } @@ -7875,6 +8071,7 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; @@ -7882,50 +8079,48 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const error_set = try sema.resolveType(block, lhs_src, extra.lhs); const payload = try sema.resolveType(block, rhs_src, extra.rhs); - if (error_set.zigTypeTag() != .ErrorSet) { + if (error_set.zigTypeTag(mod) != .ErrorSet) { return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{ - error_set.fmt(sema.mod), + error_set.fmt(mod), }); } try sema.validateErrorUnionPayloadType(block, payload, rhs_src); - const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, sema.mod); + const err_union_ty = try mod.errorUnionType(error_set, payload); return sema.addType(err_union_ty); } fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void { - if (payload_ty.zigTypeTag() == .Opaque) { + const mod = sema.mod; + if (payload_ty.zigTypeTag(mod) == .Opaque) { return sema.fail(block, payload_src, "error union with payload of opaque type '{}' not allowed", .{ - payload_ty.fmt(sema.mod), + payload_ty.fmt(mod), }); - } else if (payload_ty.zigTypeTag() == .ErrorSet) { + } else if (payload_ty.zigTypeTag(mod) == .ErrorSet) { return sema.fail(block, payload_src, "error union with payload of error set type '{}' not allowed", .{ - payload_ty.fmt(sema.mod), + payload_ty.fmt(mod), }); } } fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { _ = block; - const tracy = trace(@src()); - defer tracy.end(); - + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].str_tok; - - // Create an anonymous error set type with only this error value, and return the value. - const kv = try sema.mod.getErrorValue(inst_data.get(sema.code)); - const result_type = try Type.Tag.error_set_single.create(sema.arena, kv.key); - return sema.addConstant( - result_type, - try Value.Tag.@"error".create(sema.arena, .{ - .name = kv.key, - }), - ); + const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); + _ = try mod.getErrorValue(name); + // Create an error set type with only this error value, and return the value. + const error_set_type = try mod.singleErrorSetType(name); + return sema.addConstant(error_set_type, (try mod.intern(.{ .err = .{ + .ty = error_set_type.toIntern(), + .name = name, + } })).toValue()); } fn zirErrorToInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; @@ -7933,34 +8128,26 @@ fn zirErrorToInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat const operand = try sema.coerce(block, Type.anyerror, uncasted_operand, operand_src); if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef()) { + if (val.isUndef(mod)) { return sema.addConstUndef(Type.err_int); } - switch (val.tag()) { - .@"error" => { - const payload = try sema.arena.create(Value.Payload.U64); - payload.* = .{ - .base = .{ .tag = .int_u64 }, - .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value, - }; - return sema.addConstant(Type.err_int, Value.initPayload(&payload.base)); - }, - - // This is not a valid combination with the type `anyerror`. - .the_only_possible_value => unreachable, - - // Assume it's already encoded as an integer. - else => return sema.addConstant(Type.err_int, val), - } + const err_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; + return sema.addConstant(Type.err_int, try mod.intValue( + Type.err_int, + try mod.getErrorValue(err_name), + )); } const op_ty = sema.typeOf(uncasted_operand); try sema.resolveInferredErrorSetTy(block, src, op_ty); - if (!op_ty.isAnyError()) { - const names = op_ty.errorSetNames(); + if (!op_ty.isAnyError(mod)) { + const names = op_ty.errorSetNames(mod); switch (names.len) { - 0 => return sema.addConstant(Type.err_int, Value.zero), - 1 => return sema.addIntUnsigned(Type.err_int, sema.mod.global_error_set.get(names[0]).?), + 0 => return sema.addConstant(Type.err_int, try mod.intValue(Type.err_int, 0)), + 1 => { + const int = @intCast(Module.ErrorInt, mod.global_error_set.getIndex(names[0]).?); + return sema.addIntUnsigned(Type.err_int, int); + }, else => {}, } } @@ -7973,28 +8160,26 @@ fn zirIntToError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const uncasted_operand = try sema.resolveInst(extra.operand); const operand = try sema.coerce(block, Type.err_int, uncasted_operand, operand_src); - const target = sema.mod.getTarget(); if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { - const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(target)); - if (int > sema.mod.global_error_set.count() or int == 0) + const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(mod)); + if (int > mod.global_error_set.count() or int == 0) return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int}); - const payload = try sema.arena.create(Value.Payload.Error); - payload.* = .{ - .base = .{ .tag = .@"error" }, - .data = .{ .name = sema.mod.error_name_list.items[int] }, - }; - return sema.addConstant(Type.anyerror, Value.initPayload(&payload.base)); + return sema.addConstant(Type.anyerror, (try mod.intern(.{ .err = .{ + .ty = .anyerror_type, + .name = mod.global_error_set.keys()[int], + } })).toValue()); } try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety()) { const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand); - const zero_val = try sema.addConstant(Type.err_int, Value.zero); + const zero_val = try sema.addConstant(Type.err_int, try mod.intValue(Type.err_int, 0)); const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val); const ok = try block.addBinOp(.bit_and, is_lt_len, is_non_zero); try sema.addSafetyCheck(block, ok, .invalid_error_code); @@ -8012,6 +8197,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; @@ -8019,7 +8205,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const lhs = try sema.resolveInst(extra.lhs); const rhs = try sema.resolveInst(extra.rhs); - if (sema.typeOf(lhs).zigTypeTag() == .Bool and sema.typeOf(rhs).zigTypeTag() == .Bool) { + if (sema.typeOf(lhs).zigTypeTag(mod) == .Bool and sema.typeOf(rhs).zigTypeTag(mod) == .Bool) { const msg = msg: { const msg = try sema.errMsg(block, lhs_src, "expected error set type, found 'bool'", .{}); errdefer msg.destroy(sema.gpa); @@ -8030,32 +8216,32 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr } const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs); const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs); - if (lhs_ty.zigTypeTag() != .ErrorSet) - return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{lhs_ty.fmt(sema.mod)}); - if (rhs_ty.zigTypeTag() != .ErrorSet) - return sema.fail(block, rhs_src, "expected error set type, found '{}'", .{rhs_ty.fmt(sema.mod)}); + if (lhs_ty.zigTypeTag(mod) != .ErrorSet) + return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{lhs_ty.fmt(mod)}); + if (rhs_ty.zigTypeTag(mod) != .ErrorSet) + return sema.fail(block, rhs_src, "expected error set type, found '{}'", .{rhs_ty.fmt(mod)}); // Anything merged with anyerror is anyerror. - if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) { + if (lhs_ty.toIntern() == .anyerror_type or rhs_ty.toIntern() == .anyerror_type) { return Air.Inst.Ref.anyerror_type; } - if (lhs_ty.castTag(.error_set_inferred)) |payload| { - try sema.resolveInferredErrorSet(block, src, payload.data); + if (mod.typeToInferredErrorSetIndex(lhs_ty).unwrap()) |ies_index| { + try sema.resolveInferredErrorSet(block, src, ies_index); // isAnyError might have changed from a false negative to a true positive after resolution. - if (lhs_ty.isAnyError()) { + if (lhs_ty.isAnyError(mod)) { return Air.Inst.Ref.anyerror_type; } } - if (rhs_ty.castTag(.error_set_inferred)) |payload| { - try sema.resolveInferredErrorSet(block, src, payload.data); + if (mod.typeToInferredErrorSetIndex(rhs_ty).unwrap()) |ies_index| { + try sema.resolveInferredErrorSet(block, src, ies_index); // isAnyError might have changed from a false negative to a true positive after resolution. - if (rhs_ty.isAnyError()) { + if (rhs_ty.isAnyError(mod)) { return Air.Inst.Ref.anyerror_type; } } - const err_set_ty = try lhs_ty.errorSetMerge(sema.arena, rhs_ty); + const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty); return sema.addType(err_set_ty); } @@ -8064,27 +8250,27 @@ fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].str_tok; - const duped_name = try sema.arena.dupe(u8, inst_data.get(sema.code)); - return sema.addConstant( - Type.initTag(.enum_literal), - try Value.Tag.enum_literal.create(sema.arena, duped_name), - ); + const name = inst_data.get(sema.code); + return sema.addConstant(.{ .ip_index = .enum_literal_type }, (try mod.intern(.{ + .enum_literal = try mod.intern_pool.getOrPutString(sema.gpa, name), + })).toValue()); } fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const arena = sema.arena; + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag()) { + const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(mod)) { .Enum => operand, .Union => blk: { const union_ty = try sema.resolveTypeFields(operand_ty); - const tag_ty = union_ty.unionTagType() orelse { + const tag_ty = union_ty.unionTagType(mod) orelse { return sema.fail( block, operand_src, @@ -8096,22 +8282,20 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }, else => { return sema.fail(block, operand_src, "expected enum or tagged union, found '{}'", .{ - operand_ty.fmt(sema.mod), + operand_ty.fmt(mod), }); }, }; const enum_tag_ty = sema.typeOf(enum_tag); - var int_tag_type_buffer: Type.Payload.Bits = undefined; - const int_tag_ty = try enum_tag_ty.intTagType(&int_tag_type_buffer).copy(arena); + const int_tag_ty = enum_tag_ty.intTagType(mod); if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| { - return sema.addConstant(int_tag_ty, opv); + return sema.addConstant(int_tag_ty, try mod.getCoerced(opv, int_tag_ty)); } if (try sema.resolveMaybeUndefVal(enum_tag)) |enum_tag_val| { - var buffer: Value.Payload.U64 = undefined; - const val = enum_tag_val.enumToInt(enum_tag_ty, &buffer); + const val = try enum_tag_val.enumToInt(enum_tag_ty, mod); return sema.addConstant(int_tag_ty, try val.copy(sema.arena)); } @@ -8120,6 +8304,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const src = inst_data.src(); @@ -8128,24 +8313,23 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = try sema.resolveInst(extra.rhs); - if (dest_ty.zigTypeTag() != .Enum) { - return sema.fail(block, dest_ty_src, "expected enum, found '{}'", .{dest_ty.fmt(sema.mod)}); + if (dest_ty.zigTypeTag(mod) != .Enum) { + return sema.fail(block, dest_ty_src, "expected enum, found '{}'", .{dest_ty.fmt(mod)}); } _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand)); if (try sema.resolveMaybeUndefVal(operand)) |int_val| { - if (dest_ty.isNonexhaustiveEnum()) { - var buffer: Type.Payload.Bits = undefined; - const int_tag_ty = dest_ty.intTagType(&buffer); + if (dest_ty.isNonexhaustiveEnum(mod)) { + const int_tag_ty = dest_ty.intTagType(mod); if (try sema.intFitsInType(int_val, int_tag_ty, null)) { - return sema.addConstant(dest_ty, int_val); + return sema.addConstant(dest_ty, try mod.getCoerced(int_val, dest_ty)); } const msg = msg: { const msg = try sema.errMsg( block, src, "int value '{}' out of range of non-exhaustive enum '{}'", - .{ int_val.fmtValue(sema.typeOf(operand), sema.mod), dest_ty.fmt(sema.mod) }, + .{ int_val.fmtValue(sema.typeOf(operand), mod), dest_ty.fmt(mod) }, ); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, dest_ty); @@ -8153,7 +8337,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }; return sema.failWithOwnedErrorMsg(msg); } - if (int_val.isUndef()) { + if (int_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, operand_src); } if (!(try sema.enumHasInt(dest_ty, int_val))) { @@ -8162,7 +8346,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A block, src, "enum '{}' has no tag with value '{}'", - .{ dest_ty.fmt(sema.mod), int_val.fmtValue(sema.typeOf(operand), sema.mod) }, + .{ dest_ty.fmt(mod), int_val.fmtValue(sema.typeOf(operand), mod) }, ); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, dest_ty); @@ -8170,7 +8354,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }; return sema.failWithOwnedErrorMsg(msg); } - return sema.addConstant(dest_ty, int_val); + return sema.addConstant(dest_ty, try mod.getCoerced(int_val, dest_ty)); } if (try sema.typeHasOnePossibleValue(dest_ty)) |opv| { @@ -8184,8 +8368,8 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A try sema.requireRuntimeBlock(block, src, operand_src); const result = try block.addTyOp(.intcast, dest_ty, operand); - if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum() and - sema.mod.backendSupportsFeature(.is_named_enum_value)) + if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum(mod) and + mod.backendSupportsFeature(.is_named_enum_value)) { const ok = try block.addUnOp(.is_named_enum_value, result); try sema.addSafetyCheck(block, ok, .invalid_enum_value); @@ -8218,49 +8402,44 @@ fn analyzeOptionalPayloadPtr( safety_check: bool, initializing: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const optional_ptr_ty = sema.typeOf(optional_ptr); - assert(optional_ptr_ty.zigTypeTag() == .Pointer); + assert(optional_ptr_ty.zigTypeTag(mod) == .Pointer); - const opt_type = optional_ptr_ty.elemType(); - if (opt_type.zigTypeTag() != .Optional) { - return sema.fail(block, src, "expected optional type, found '{}'", .{opt_type.fmt(sema.mod)}); + const opt_type = optional_ptr_ty.childType(mod); + if (opt_type.zigTypeTag(mod) != .Optional) { + return sema.fail(block, src, "expected optional type, found '{}'", .{opt_type.fmt(mod)}); } - const child_type = try opt_type.optionalChildAlloc(sema.arena); - const child_pointer = try Type.ptr(sema.arena, sema.mod, .{ + const child_type = opt_type.optionalChild(mod); + const child_pointer = try Type.ptr(sema.arena, mod, .{ .pointee_type = child_type, - .mutable = !optional_ptr_ty.isConstPtr(), - .@"addrspace" = optional_ptr_ty.ptrAddressSpace(), + .mutable = !optional_ptr_ty.isConstPtr(mod), + .@"addrspace" = optional_ptr_ty.ptrAddressSpace(mod), }); if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| { if (initializing) { - if (!ptr_val.isComptimeMutablePtr()) { + if (!ptr_val.isComptimeMutablePtr(mod)) { // If the pointer resulting from this function was stored at comptime, // the optional non-null bit would be set that way. But in this case, // we need to emit a runtime instruction to do it. _ = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); } - return sema.addConstant( - child_pointer, - try Value.Tag.opt_payload_ptr.create(sema.arena, .{ - .container_ptr = ptr_val, - .container_ty = optional_ptr_ty.childType(), - }), - ); + return sema.addConstant(child_pointer, (try mod.intern(.{ .ptr = .{ + .ty = child_pointer.toIntern(), + .addr = .{ .opt_payload = ptr_val.toIntern() }, + } })).toValue()); } if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| { - if (val.isNull()) { + if (val.isNull(mod)) { return sema.fail(block, src, "unable to unwrap null", .{}); } // The same Value represents the pointer to the optional and the payload. - return sema.addConstant( - child_pointer, - try Value.Tag.opt_payload_ptr.create(sema.arena, .{ - .container_ptr = ptr_val, - .container_ty = optional_ptr_ty.childType(), - }), - ); + return sema.addConstant(child_pointer, (try mod.intern(.{ .ptr = .{ + .ty = child_pointer.toIntern(), + .addr = .{ .opt_payload = ptr_val.toIntern() }, + } })).toValue()); } } @@ -8286,21 +8465,22 @@ fn zirOptionalPayload( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - const result_ty = switch (operand_ty.zigTypeTag()) { - .Optional => try operand_ty.optionalChildAlloc(sema.arena), + const result_ty = switch (operand_ty.zigTypeTag(mod)) { + .Optional => operand_ty.optionalChild(mod), .Pointer => t: { - if (operand_ty.ptrSize() != .C) { + if (operand_ty.ptrSize(mod) != .C) { return sema.failWithExpectedOptionalType(block, src, operand_ty); } // TODO https://github.com/ziglang/zig/issues/6597 if (true) break :t operand_ty; - const ptr_info = operand_ty.ptrInfo().data; - break :t try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = try ptr_info.pointee_type.copy(sema.arena), + const ptr_info = operand_ty.ptrInfo(mod); + break :t try Type.ptr(sema.arena, mod, .{ + .pointee_type = ptr_info.pointee_type, .@"align" = ptr_info.@"align", .@"addrspace" = ptr_info.@"addrspace", .mutable = ptr_info.mutable, @@ -8313,13 +8493,10 @@ fn zirOptionalPayload( }; if (try sema.resolveDefinedValue(block, src, operand)) |val| { - if (val.isNull()) { - return sema.fail(block, src, "unable to unwrap null", .{}); - } - if (val.castTag(.opt_payload)) |payload| { - return sema.addConstant(result_ty, payload.data); - } - return sema.addConstant(result_ty, val); + return if (val.optionalValue(mod)) |payload| + sema.addConstant(result_ty, payload) + else + sema.fail(block, src, "unable to unwrap null", .{}); } try sema.requireRuntimeBlock(block, src, null); @@ -8339,14 +8516,15 @@ fn zirErrUnionPayload( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand = try sema.resolveInst(inst_data.operand); const operand_src = src; const err_union_ty = sema.typeOf(operand); - if (err_union_ty.zigTypeTag() != .ErrorUnion) { + if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ - err_union_ty.fmt(sema.mod), + err_union_ty.fmt(mod), }); } return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); @@ -8357,24 +8535,27 @@ fn analyzeErrUnionPayload( block: *Block, src: LazySrcLoc, err_union_ty: Type, - operand: Zir.Inst.Ref, + operand: Air.Inst.Ref, operand_src: LazySrcLoc, safety_check: bool, ) CompileError!Air.Inst.Ref { - const payload_ty = err_union_ty.errorUnionPayload(); + const mod = sema.mod; + const payload_ty = err_union_ty.errorUnionPayload(mod); if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { - if (val.getError()) |name| { - return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); + if (val.getErrorName(mod).unwrap()) |name| { + return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&mod.intern_pool)}); } - const data = val.castTag(.eu_payload).?.data; - return sema.addConstant(payload_ty, data); + return sema.addConstant( + payload_ty, + mod.intern_pool.indexToKey(val.toIntern()).error_union.val.payload.toValue(), + ); } try sema.requireRuntimeBlock(block, src, null); // If the error set has no fields then no safety check is needed. if (safety_check and block.wantSafety() and - !err_union_ty.errorUnionSet().errorSetIsEmpty()) + !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { try sema.panicUnwrapError(block, operand, .unwrap_errunion_err, .is_non_err); } @@ -8406,52 +8587,46 @@ fn analyzeErrUnionPayloadPtr( safety_check: bool, initializing: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const operand_ty = sema.typeOf(operand); - assert(operand_ty.zigTypeTag() == .Pointer); + assert(operand_ty.zigTypeTag(mod) == .Pointer); - if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) { + if (operand_ty.childType(mod).zigTypeTag(mod) != .ErrorUnion) { return sema.fail(block, src, "expected error union type, found '{}'", .{ - operand_ty.elemType().fmt(sema.mod), + operand_ty.childType(mod).fmt(mod), }); } - const err_union_ty = operand_ty.elemType(); - const payload_ty = err_union_ty.errorUnionPayload(); - const operand_pointer_ty = try Type.ptr(sema.arena, sema.mod, .{ + const err_union_ty = operand_ty.childType(mod); + const payload_ty = err_union_ty.errorUnionPayload(mod); + const operand_pointer_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = payload_ty, - .mutable = !operand_ty.isConstPtr(), - .@"addrspace" = operand_ty.ptrAddressSpace(), + .mutable = !operand_ty.isConstPtr(mod), + .@"addrspace" = operand_ty.ptrAddressSpace(mod), }); if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| { if (initializing) { - if (!ptr_val.isComptimeMutablePtr()) { + if (!ptr_val.isComptimeMutablePtr(mod)) { // If the pointer resulting from this function was stored at comptime, // the error union error code would be set that way. But in this case, // we need to emit a runtime instruction to do it. try sema.requireRuntimeBlock(block, src, null); _ = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); } - return sema.addConstant( - operand_pointer_ty, - try Value.Tag.eu_payload_ptr.create(sema.arena, .{ - .container_ptr = ptr_val, - .container_ty = operand_ty.elemType(), - }), - ); + return sema.addConstant(operand_pointer_ty, (try mod.intern(.{ .ptr = .{ + .ty = operand_pointer_ty.toIntern(), + .addr = .{ .eu_payload = ptr_val.toIntern() }, + } })).toValue()); } if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| { - if (val.getError()) |name| { - return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); + if (val.getErrorName(mod).unwrap()) |name| { + return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&mod.intern_pool)}); } - - return sema.addConstant( - operand_pointer_ty, - try Value.Tag.eu_payload_ptr.create(sema.arena, .{ - .container_ptr = ptr_val, - .container_ty = operand_ty.elemType(), - }), - ); + return sema.addConstant(operand_pointer_ty, (try mod.intern(.{ .ptr = .{ + .ty = operand_pointer_ty.toIntern(), + .addr = .{ .eu_payload = ptr_val.toIntern() }, + } })).toValue()); } } @@ -8459,7 +8634,7 @@ fn analyzeErrUnionPayloadPtr( // If the error set has no fields then no safety check is needed. if (safety_check and block.wantSafety() and - !err_union_ty.errorUnionSet().errorSetIsEmpty()) + !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { try sema.panicUnwrapError(block, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); } @@ -8483,18 +8658,21 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro } fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { + const mod = sema.mod; const operand_ty = sema.typeOf(operand); - if (operand_ty.zigTypeTag() != .ErrorUnion) { + if (operand_ty.zigTypeTag(mod) != .ErrorUnion) { return sema.fail(block, src, "expected error union type, found '{}'", .{ - operand_ty.fmt(sema.mod), + operand_ty.fmt(mod), }); } - const result_ty = operand_ty.errorUnionSet(); + const result_ty = operand_ty.errorUnionSet(mod); if (try sema.resolveDefinedValue(block, src, operand)) |val| { - assert(val.getError() != null); - return sema.addConstant(result_ty, val); + return sema.addConstant(result_ty, (try mod.intern(.{ .err = .{ + .ty = result_ty.toIntern(), + .name = mod.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name, + } })).toValue()); } try sema.requireRuntimeBlock(block, src, null); @@ -8506,23 +8684,24 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - assert(operand_ty.zigTypeTag() == .Pointer); + assert(operand_ty.zigTypeTag(mod) == .Pointer); - if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) { + if (operand_ty.childType(mod).zigTypeTag(mod) != .ErrorUnion) { return sema.fail(block, src, "expected error union type, found '{}'", .{ - operand_ty.elemType().fmt(sema.mod), + operand_ty.childType(mod).fmt(mod), }); } - const result_ty = operand_ty.elemType().errorUnionSet(); + const result_ty = operand_ty.childType(mod).errorUnionSet(mod); if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { - assert(val.getError() != null); + assert(val.getErrorName(mod) != .none); return sema.addConstant(result_ty, val); } } @@ -8556,7 +8735,7 @@ fn zirFunc( break :blk ret_ty; } else |err| switch (err) { error.GenericPoison => { - break :blk Type.initTag(.generic_poison); + break :blk Type.generic_poison; }, else => |e| return e, } @@ -8566,8 +8745,7 @@ fn zirFunc( extra_index += ret_ty_body.len; const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type, "return type must be comptime-known"); - var buffer: Value.ToTypeBuffer = undefined; - break :blk try ret_ty_val.toType(&buffer).copy(sema.arena); + break :blk ret_ty_val.toType(); }, }; @@ -8634,10 +8812,10 @@ fn resolveGenericBody( }; switch (err) { error.GenericPoison => { - if (dest_ty.tag() == .type) { - return Value.initTag(.generic_poison_type); + if (dest_ty.toIntern() == .type_type) { + return Value.generic_poison_type; } else { - return Value.initTag(.generic_poison); + return Value.generic_poison; } }, else => |e| return e, @@ -8711,7 +8889,7 @@ fn handleExternLibName( const FuncLinkSection = union(enum) { generic, default, - explicit: []const u8, + explicit: InternPool.NullTerminatedString, }; fn funcCommon( @@ -8738,11 +8916,13 @@ fn funcCommon( noalias_bits: u32, is_noinline: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const gpa = sema.gpa; const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset }; const func_src = LazySrcLoc.nodeOffset(src_node_offset); - var is_generic = bare_return_type.tag() == .generic_poison or + var is_generic = bare_return_type.isGenericPoison() or alignment == null or address_space == null or section == .generic or @@ -8758,69 +8938,42 @@ fn funcCommon( } var destroy_fn_on_error = false; - const new_func: *Module.Fn = new_func: { + const new_func_index = new_func: { if (!has_body) break :new_func undefined; if (sema.comptime_args_fn_inst == func_inst) { - const new_func = sema.preallocated_new_func.?; - sema.preallocated_new_func = null; // take ownership - break :new_func new_func; + const new_func_index = sema.preallocated_new_func.unwrap().?; + sema.preallocated_new_func = .none; // take ownership + break :new_func new_func_index; } destroy_fn_on_error = true; - const new_func = try sema.gpa.create(Module.Fn); + var new_func: Module.Fn = undefined; // Set this here so that the inferred return type can be printed correctly if it appears in an error. new_func.owner_decl = sema.owner_decl_index; - break :new_func new_func; + const new_func_index = try mod.createFunc(new_func); + break :new_func new_func_index; }; - errdefer if (destroy_fn_on_error) sema.gpa.destroy(new_func); + errdefer if (destroy_fn_on_error) mod.destroyFunc(new_func_index); - var maybe_inferred_error_set_node: ?*Module.Fn.InferredErrorSetListNode = null; - errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node); - // Note: no need to errdefer since this will still be in its default state at the end of the function. - - const target = sema.mod.getTarget(); + const target = mod.getTarget(); const fn_ty: Type = fn_ty: { - // Hot path for some common function types. - // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated. - if (!is_generic and block.params.items.len == 0 and !var_args and !inferred_error_set and - alignment.? == 0 and - address_space.? == target_util.defaultAddressSpace(target, .function) and - section == .default) - { - if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Unspecified) { - break :fn_ty Type.initTag(.fn_noreturn_no_args); - } - - if (bare_return_type.zigTypeTag() == .Void and cc.? == .Unspecified) { - break :fn_ty Type.initTag(.fn_void_no_args); - } - - if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Naked) { - break :fn_ty Type.initTag(.fn_naked_noreturn_no_args); - } - - if (bare_return_type.zigTypeTag() == .Void and cc.? == .C) { - break :fn_ty Type.initTag(.fn_ccc_void_no_args); - } - } - // In the case of generic calling convention, or generic alignment, we use // default values which are only meaningful for the generic function, *not* // the instantiation, which can depend on comptime parameters. // Related proposal: https://github.com/ziglang/zig/issues/11834 const cc_resolved = cc orelse .Unspecified; - const param_types = try sema.arena.alloc(Type, block.params.items.len); - const comptime_params = try sema.arena.alloc(bool, block.params.items.len); - for (block.params.items, 0..) |param, i| { + const param_types = try sema.arena.alloc(InternPool.Index, block.params.items.len); + var comptime_bits: u32 = 0; + for (param_types, block.params.items, 0..) |*dest_param_ty, param, i| { const is_noalias = blk: { const index = std.math.cast(u5, i) orelse break :blk false; break :blk @truncate(u1, noalias_bits >> index) != 0; }; - param_types[i] = param.ty; + dest_param_ty.* = param.ty.toIntern(); sema.analyzeParameter( block, .unneeded, param, - comptime_params, + &comptime_bits, i, &is_generic, cc_resolved, @@ -8828,12 +8981,12 @@ fn funcCommon( is_noalias, ) catch |err| switch (err) { error.NeededSourceLocation => { - const decl = sema.mod.declPtr(block.src_decl); + const decl = mod.declPtr(block.src_decl); try sema.analyzeParameter( block, - Module.paramSrc(src_node_offset, sema.gpa, decl, i), + Module.paramSrc(src_node_offset, mod, decl, i), param, - comptime_params, + &comptime_bits, i, &is_generic, cc_resolved, @@ -8849,7 +9002,7 @@ fn funcCommon( var ret_ty_requires_comptime = false; const ret_poison = if (sema.typeRequiresComptime(bare_return_type)) |ret_comptime| rp: { ret_ty_requires_comptime = ret_comptime; - break :rp bare_return_type.tag() == .generic_poison; + break :rp bare_return_type.isGenericPoison(); } else |err| switch (err) { error.GenericPoison => rp: { is_generic = true; @@ -8858,43 +9011,41 @@ fn funcCommon( else => |e| return e, }; - const return_type = if (!inferred_error_set or ret_poison) + const return_type: Type = if (!inferred_error_set or ret_poison) bare_return_type else blk: { try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src); - const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode); - node.data = .{ .func = new_func }; - maybe_inferred_error_set_node = node; - - const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data); - break :blk try Type.Tag.error_union.create(sema.arena, .{ - .error_set = error_set_ty, - .payload = bare_return_type, + const ies_index = try mod.intern_pool.createInferredErrorSet(gpa, .{ + .func = new_func_index, }); + const error_set_ty = try mod.intern(.{ .inferred_error_set_type = ies_index }); + break :blk try mod.errorUnionType(error_set_ty.toType(), bare_return_type); }; - if (!return_type.isValidReturnType()) { - const opaque_str = if (return_type.zigTypeTag() == .Opaque) "opaque " else ""; + if (!return_type.isValidReturnType(mod)) { + const opaque_str = if (return_type.zigTypeTag(mod) == .Opaque) "opaque " else ""; const msg = msg: { const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{ - opaque_str, return_type.fmt(sema.mod), + opaque_str, return_type.fmt(mod), }); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); try sema.addDeclaredHereNote(msg, return_type); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } - if (!ret_poison and !Type.fnCallingConventionAllowsZigTypes(target, cc_resolved) and !try sema.validateExternType(return_type, .ret_ty)) { + if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved) and + !try sema.validateExternType(return_type, .ret_ty)) + { const msg = msg: { const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{ - return_type.fmt(sema.mod), @tagName(cc_resolved), + return_type.fmt(mod), @tagName(cc_resolved), }); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl, mod), return_type, .ret_ty); try sema.addDeclaredHereNote(msg, return_type); break :msg msg; @@ -8912,9 +9063,9 @@ fn funcCommon( block, ret_ty_src, "function with comptime-only return type '{}' requires all parameters to be comptime", - .{return_type.fmt(sema.mod)}, + .{return_type.fmt(mod)}, ); - try sema.explainWhyTypeIsComptime(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(sema.owner_decl), return_type); + try sema.explainWhyTypeIsComptime(msg, ret_ty_src.toSrcLoc(sema.owner_decl, mod), return_type); const tags = sema.code.instructions.items(.tag); const data = sema.code.instructions.items(.data); @@ -8937,7 +9088,7 @@ fn funcCommon( return sema.failWithOwnedErrorMsg(msg); } - const arch = sema.mod.getTarget().cpu.arch; + const arch = mod.getTarget().cpu.arch; if (switch (cc_resolved) { .Unspecified, .C, .Naked, .Async, .Inline => null, .Interrupt => switch (arch) { @@ -8980,8 +9131,7 @@ fn funcCommon( return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'Inline'", .{}); } if (is_generic and sema.no_partial_func_ty) return error.GenericPoison; - for (comptime_params) |ct| is_generic = is_generic or ct; - is_generic = is_generic or ret_ty_requires_comptime; + is_generic = is_generic or comptime_bits != 0 or ret_ty_requires_comptime; if (!is_generic and sema.wantErrorReturnTracing(return_type)) { // Make sure that StackTrace's fields are resolved so that the backend can @@ -8990,67 +9140,58 @@ fn funcCommon( _ = try sema.resolveTypeFields(unresolved_stack_trace_ty); } - break :fn_ty try Type.Tag.function.create(sema.arena, .{ + break :fn_ty try mod.funcType(.{ .param_types = param_types, - .comptime_params = comptime_params.ptr, - .return_type = return_type, + .noalias_bits = noalias_bits, + .comptime_bits = comptime_bits, + .return_type = return_type.toIntern(), .cc = cc_resolved, .cc_is_generic = cc == null, - .alignment = alignment orelse 0, + .alignment = if (alignment) |a| InternPool.Alignment.fromByteUnits(a) else .none, .align_is_generic = alignment == null, .section_is_generic = section == .generic, .addrspace_is_generic = address_space == null, .is_var_args = var_args, .is_generic = is_generic, - .noalias_bits = noalias_bits, + .is_noinline = is_noinline, }); }; sema.owner_decl.@"linksection" = switch (section) { - .generic => undefined, - .default => null, - .explicit => |section_name| try sema.perm_arena.dupeZ(u8, section_name), + .generic => .none, + .default => .none, + .explicit => |section_name| section_name.toOptional(), }; sema.owner_decl.@"align" = alignment orelse 0; sema.owner_decl.@"addrspace" = address_space orelse .generic; if (is_extern) { - const new_extern_fn = try sema.gpa.create(Module.ExternFn); - errdefer sema.gpa.destroy(new_extern_fn); - - new_extern_fn.* = Module.ExternFn{ - .owner_decl = sema.owner_decl_index, - .lib_name = null, - }; - - if (opt_lib_name) |lib_name| { - new_extern_fn.lib_name = try sema.handleExternLibName(block, .{ - .node_offset_lib_name = src_node_offset, - }, lib_name); - } - - const extern_fn_payload = try sema.arena.create(Value.Payload.ExternFn); - extern_fn_payload.* = .{ - .base = .{ .tag = .extern_fn }, - .data = new_extern_fn, - }; - return sema.addConstant(fn_ty, Value.initPayload(&extern_fn_payload.base)); + return sema.addConstant(fn_ty, (try mod.intern(.{ .extern_func = .{ + .ty = fn_ty.toIntern(), + .decl = sema.owner_decl_index, + .lib_name = if (opt_lib_name) |lib_name| (try mod.intern_pool.getOrPutString( + gpa, + try sema.handleExternLibName(block, .{ + .node_offset_lib_name = src_node_offset, + }, lib_name), + )).toOptional() else .none, + } })).toValue()); } if (!has_body) { return sema.addType(fn_ty); } - const is_inline = fn_ty.fnCallingConvention() == .Inline; - const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued; + const is_inline = fn_ty.fnCallingConvention(mod) == .Inline; + const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .none; const comptime_args: ?[*]TypedValue = if (sema.comptime_args_fn_inst == func_inst) blk: { break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr; } else null; + const new_func = mod.funcPtr(new_func_index); const hash = new_func.hash; const generic_owner_decl = if (comptime_args == null) .none else new_func.generic_owner_decl; - const fn_payload = try sema.arena.create(Value.Payload.Function); new_func.* = .{ .state = anal_state, .zir_body_inst = func_inst, @@ -9065,15 +9206,10 @@ fn funcCommon( .branch_quota = default_branch_quota, .is_noinline = is_noinline, }; - if (maybe_inferred_error_set_node) |node| { - new_func.inferred_error_sets.prepend(node); - } - maybe_inferred_error_set_node = null; - fn_payload.* = .{ - .base = .{ .tag = .function }, - .data = new_func, - }; - return sema.addConstant(fn_ty, Value.initPayload(&fn_payload.base)); + return sema.addConstant(fn_ty, (try mod.intern(.{ .func = .{ + .ty = fn_ty.toIntern(), + .index = new_func_index, + } })).toValue()); } fn analyzeParameter( @@ -9081,29 +9217,32 @@ fn analyzeParameter( block: *Block, param_src: LazySrcLoc, param: Block.Param, - comptime_params: []bool, + comptime_bits: *u32, i: usize, is_generic: *bool, cc: std.builtin.CallingConvention, has_body: bool, is_noalias: bool, ) !void { + const mod = sema.mod; const requires_comptime = try sema.typeRequiresComptime(param.ty); - comptime_params[i] = param.is_comptime or requires_comptime; - const this_generic = param.ty.tag() == .generic_poison; + if (param.is_comptime or requires_comptime) { + comptime_bits.* |= @as(u32, 1) << @intCast(u5, i); // TODO: handle cast error + } + const this_generic = param.ty.isGenericPoison(); is_generic.* = is_generic.* or this_generic; - const target = sema.mod.getTarget(); - if (param.is_comptime and !Type.fnCallingConventionAllowsZigTypes(target, cc)) { + const target = mod.getTarget(); + if (param.is_comptime and !target_util.fnCallConvAllowsZigTypes(target, cc)) { return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); } - if (this_generic and !sema.no_partial_func_ty and !Type.fnCallingConventionAllowsZigTypes(target, cc)) { + if (this_generic and !sema.no_partial_func_ty and !target_util.fnCallConvAllowsZigTypes(target, cc)) { return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); } - if (!param.ty.isValidParamType()) { - const opaque_str = if (param.ty.zigTypeTag() == .Opaque) "opaque " else ""; + if (!param.ty.isValidParamType(mod)) { + const opaque_str = if (param.ty.zigTypeTag(mod) == .Opaque) "opaque " else ""; const msg = msg: { const msg = try sema.errMsg(block, param_src, "parameter of {s}type '{}' not allowed", .{ - opaque_str, param.ty.fmt(sema.mod), + opaque_str, param.ty.fmt(mod), }); errdefer msg.destroy(sema.gpa); @@ -9112,15 +9251,15 @@ fn analyzeParameter( }; return sema.failWithOwnedErrorMsg(msg); } - if (!this_generic and !Type.fnCallingConventionAllowsZigTypes(target, cc) and !try sema.validateExternType(param.ty, .param_ty)) { + if (!this_generic and !target_util.fnCallConvAllowsZigTypes(target, cc) and !try sema.validateExternType(param.ty, .param_ty)) { const msg = msg: { const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{ - param.ty.fmt(sema.mod), @tagName(cc), + param.ty.fmt(mod), @tagName(cc), }); errdefer msg.destroy(sema.gpa); - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl, mod), param.ty, .param_ty); try sema.addDeclaredHereNote(msg, param.ty); break :msg msg; @@ -9130,12 +9269,12 @@ fn analyzeParameter( if (!sema.is_generic_instantiation and requires_comptime and !param.is_comptime and has_body) { const msg = msg: { const msg = try sema.errMsg(block, param_src, "parameter of type '{}' must be declared comptime", .{ - param.ty.fmt(sema.mod), + param.ty.fmt(mod), }); errdefer msg.destroy(sema.gpa); - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsComptime(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsComptime(msg, param_src.toSrcLoc(src_decl, mod), param.ty); try sema.addDeclaredHereNote(msg, param.ty); break :msg msg; @@ -9143,7 +9282,7 @@ fn analyzeParameter( return sema.failWithOwnedErrorMsg(msg); } if (!sema.is_generic_instantiation and !this_generic and is_noalias and - !(param.ty.zigTypeTag() == .Pointer or param.ty.isPtrLikeOptional())) + !(param.ty.zigTypeTag(mod) == .Pointer or param.ty.isPtrLikeOptional(mod))) { return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{}); } @@ -9170,7 +9309,7 @@ fn zirParam( const prev_preallocated_new_func = sema.preallocated_new_func; const prev_no_partial_func_type = sema.no_partial_func_ty; block.params = .{}; - sema.preallocated_new_func = null; + sema.preallocated_new_func = .none; sema.no_partial_func_ty = true; defer { block.params.deinit(sema.gpa); @@ -9196,7 +9335,7 @@ fn zirParam( // We result the param instruction with a poison value and // insert an anytype parameter. try block.params.append(sema.gpa, .{ - .ty = Type.initTag(.generic_poison), + .ty = Type.generic_poison, .is_comptime = comptime_syntax, .name = param_name, }); @@ -9217,7 +9356,7 @@ fn zirParam( // We result the param instruction with a poison value and // insert an anytype parameter. try block.params.append(sema.gpa, .{ - .ty = Type.initTag(.generic_poison), + .ty = Type.generic_poison, .is_comptime = comptime_syntax, .name = param_name, }); @@ -9227,7 +9366,7 @@ fn zirParam( else => |e| return e, } or comptime_syntax; if (sema.inst_map.get(inst)) |arg| { - if (is_comptime and sema.preallocated_new_func != null) { + if (is_comptime and sema.preallocated_new_func != .none) { // We have a comptime value for this parameter so it should be elided from the // function type of the function instruction in this block. const coerced_arg = sema.coerce(block, param_ty, arg, .unneeded) catch |err| switch (err) { @@ -9250,7 +9389,7 @@ fn zirParam( assert(sema.inst_map.remove(inst)); } - if (sema.preallocated_new_func != null) { + if (sema.preallocated_new_func != .none) { if (try sema.typeHasOnePossibleValue(param_ty)) |opv| { // In this case we are instantiating a generic function call with a non-comptime // non-anytype parameter that ended up being a one-possible-type. @@ -9270,7 +9409,7 @@ fn zirParam( if (is_comptime) { // If this is a comptime parameter we can add a constant generic_poison // since this is also a generic parameter. - const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison)); + const result = try sema.addConstant(Type.generic_poison, Value.generic_poison); sema.inst_map.putAssumeCapacityNoClobber(inst, result); } else { // Otherwise we need a dummy runtime instruction. @@ -9315,7 +9454,7 @@ fn zirParamAnytype( // We are evaluating a generic function without any comptime args provided. try block.params.append(sema.gpa, .{ - .ty = Type.initTag(.generic_poison), + .ty = Type.generic_poison, .is_comptime = comptime_syntax, .name = param_name, }); @@ -9359,16 +9498,20 @@ fn analyzeAs( zir_operand: Zir.Inst.Ref, no_cast_to_comptime_int: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const operand = try sema.resolveInst(zir_operand); + if (zir_dest_type == .var_args_param_type) return operand; + const dest_ty = sema.resolveType(block, src, zir_dest_type) catch |err| switch (err) { + error.GenericPoison => return operand, + else => |e| return e, + }; + if (dest_ty.zigTypeTag(mod) == .NoReturn) { + return sema.fail(block, src, "cannot cast to noreturn", .{}); + } const is_ret = if (Zir.refToIndex(zir_dest_type)) |ptr_index| sema.code.instructions.items(.tag)[ptr_index] == .ret_type else false; - const dest_ty = try sema.resolveType(block, src, zir_dest_type); - const operand = try sema.resolveInst(zir_operand); - if (dest_ty.tag() == .var_args_param) return operand; - if (dest_ty.zigTypeTag() == .NoReturn) { - return sema.fail(block, src, "cannot cast to noreturn", .{}); - } return sema.coerceExtra(block, dest_ty, operand, src, .{ .is_ret = is_ret, .no_cast_to_comptime_int = no_cast_to_comptime_int }) catch |err| switch (err) { error.NotCoercible => unreachable, else => |e| return e, @@ -9379,15 +9522,19 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ptr = try sema.resolveInst(inst_data.operand); const ptr_ty = sema.typeOf(ptr); - if (!ptr_ty.isPtrAtRuntime()) { - return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}); + if (!ptr_ty.isPtrAtRuntime(mod)) { + return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(mod)}); } if (try sema.resolveMaybeUndefValIntable(ptr)) |ptr_val| { - return sema.addConstant(Type.usize, ptr_val); + return sema.addConstant( + Type.usize, + try mod.intValue(Type.usize, (try ptr_val.getUnsignedIntAdvanced(mod, sema)).?), + ); } try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src); return block.addUnOp(.ptrtoint, ptr); @@ -9397,11 +9544,12 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; - const field_name = sema.code.nullTerminatedString(extra.field_name_start); + const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); const object = try sema.resolveInst(extra.lhs); return sema.fieldVal(block, src, object, field_name, field_name_src); } @@ -9410,28 +9558,16 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index, initializing: b const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; - const field_name = sema.code.nullTerminatedString(extra.field_name_start); + const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); const object_ptr = try sema.resolveInst(extra.lhs); return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, initializing); } -fn zirFieldCallBind(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; - const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; - const field_name = sema.code.nullTerminatedString(extra.field_name_start); - const object_ptr = try sema.resolveInst(extra.lhs); - return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src); -} - fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -9441,7 +9577,7 @@ fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; const object = try sema.resolveInst(extra.lhs); - const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime-known"); + const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, "field name must be comptime-known"); return sema.fieldVal(block, src, object, field_name, field_name_src); } @@ -9454,22 +9590,10 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; const object_ptr = try sema.resolveInst(extra.lhs); - const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime-known"); + const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, "field name must be comptime-known"); return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); } -fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - - const extra = sema.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data; - const src = LazySrcLoc.nodeOffset(extra.node); - const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; - const object_ptr = try sema.resolveInst(extra.lhs); - const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime-known"); - return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src); -} - fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -9495,31 +9619,31 @@ fn intCast( operand_src: LazySrcLoc, runtime_safety: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const operand_ty = sema.typeOf(operand); const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src); const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); if (try sema.isComptimeKnown(operand)) { return sema.coerce(block, dest_ty, operand, operand_src); - } else if (dest_scalar_ty.zigTypeTag() == .ComptimeInt) { + } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) { return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{}); } try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src); - const is_vector = dest_ty.zigTypeTag() == .Vector; + const is_vector = dest_ty.zigTypeTag(mod) == .Vector; if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| { // requirement: intCast(u0, input) iff input == 0 if (runtime_safety and block.wantSafety()) { try sema.requireRuntimeBlock(block, src, operand_src); - const target = sema.mod.getTarget(); - const wanted_info = dest_scalar_ty.intInfo(target); + const wanted_info = dest_scalar_ty.intInfo(mod); const wanted_bits = wanted_info.bits; if (wanted_bits == 0) { const ok = if (is_vector) ok: { - const zeros = try Value.Tag.repeated.create(sema.arena, Value.zero); - const zero_inst = try sema.addConstant(sema.typeOf(operand), zeros); + const zeros = try sema.splat(operand_ty, try mod.intValue(operand_scalar_ty, 0)); + const zero_inst = try sema.addConstant(operand_ty, zeros); const is_in_range = try block.addCmpVector(operand, zero_inst, .eq); const all_in_range = try block.addInst(.{ .tag = .reduce, @@ -9527,7 +9651,7 @@ fn intCast( }); break :ok all_in_range; } else ok: { - const zero_inst = try sema.addConstant(sema.typeOf(operand), Value.zero); + const zero_inst = try sema.addConstant(operand_ty, try mod.intValue(operand_ty, 0)); const is_in_range = try block.addBinOp(.cmp_lte, operand, zero_inst); break :ok is_in_range; }; @@ -9540,9 +9664,8 @@ fn intCast( try sema.requireRuntimeBlock(block, src, operand_src); if (runtime_safety and block.wantSafety()) { - const target = sema.mod.getTarget(); - const actual_info = operand_scalar_ty.intInfo(target); - const wanted_info = dest_scalar_ty.intInfo(target); + const actual_info = operand_scalar_ty.intInfo(mod); + const wanted_info = dest_scalar_ty.intInfo(mod); const actual_bits = actual_info.bits; const wanted_bits = wanted_info.bits; const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed); @@ -9551,26 +9674,24 @@ fn intCast( // range shrinkage // requirement: int value fits into target type if (wanted_value_bits < actual_value_bits) { - const dest_max_val_scalar = try dest_scalar_ty.maxInt(sema.arena, target); - const dest_max_val = if (is_vector) - try Value.Tag.repeated.create(sema.arena, dest_max_val_scalar) - else - dest_max_val_scalar; + const dest_max_val_scalar = try dest_scalar_ty.maxIntScalar(mod, operand_scalar_ty); + const dest_max_val = try sema.splat(operand_ty, dest_max_val_scalar); const dest_max = try sema.addConstant(operand_ty, dest_max_val); const diff = try block.addBinOp(.subwrap, dest_max, operand); if (actual_info.signedness == .signed) { // Reinterpret the sign-bit as part of the value. This will make // negative differences (`operand` > `dest_max`) appear too big. - const unsigned_operand_ty = try Type.Tag.int_unsigned.create(sema.arena, actual_bits); + const unsigned_operand_ty = try mod.intType(.unsigned, actual_bits); const diff_unsigned = try block.addBitCast(unsigned_operand_ty, diff); // If the destination type is signed, then we need to double its // range to account for negative values. const dest_range_val = if (wanted_info.signedness == .signed) range_val: { - const range_minus_one = try dest_max_val.shl(Value.one, unsigned_operand_ty, sema.arena, sema.mod); - break :range_val try sema.intAdd(range_minus_one, Value.one, unsigned_operand_ty); - } else dest_max_val; + const one = try mod.intValue(unsigned_operand_ty, 1); + const range_minus_one = try dest_max_val.shl(one, unsigned_operand_ty, sema.arena, mod); + break :range_val try sema.intAdd(range_minus_one, one, unsigned_operand_ty, undefined); + } else try mod.getCoerced(dest_max_val, unsigned_operand_ty); const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val); const ok = if (is_vector) ok: { @@ -9610,7 +9731,8 @@ fn intCast( // no shrinkage, yes sign loss // requirement: signed to unsigned >= 0 const ok = if (is_vector) ok: { - const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); + const scalar_zero = try mod.intValue(operand_scalar_ty, 0); + const zero_val = try sema.splat(operand_ty, scalar_zero); const zero_inst = try sema.addConstant(operand_ty, zero_val); const is_in_range = try block.addCmpVector(operand, zero_inst, .gte); const all_in_range = try block.addInst(.{ @@ -9622,7 +9744,7 @@ fn intCast( }); break :ok all_in_range; } else ok: { - const zero_inst = try sema.addConstant(operand_ty, Value.zero); + const zero_inst = try sema.addConstant(operand_ty, try mod.intValue(operand_ty, 0)); const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst); break :ok is_in_range; }; @@ -9636,6 +9758,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; @@ -9644,7 +9767,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = try sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); - switch (dest_ty.zigTypeTag()) { + switch (dest_ty.zigTypeTag(mod)) { .AnyFrame, .ComptimeFloat, .ComptimeInt, @@ -9660,14 +9783,14 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Type, .Undefined, .Void, - => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)}), + => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)}), .Enum => { const msg = msg: { - const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)}); + const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); - switch (operand_ty.zigTypeTag()) { - .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToEnum to cast from '{}'", .{operand_ty.fmt(sema.mod)}), + switch (operand_ty.zigTypeTag(mod)) { + .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToEnum to cast from '{}'", .{operand_ty.fmt(mod)}), else => {}, } @@ -9678,11 +9801,11 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Pointer => { const msg = msg: { - const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)}); + const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); - switch (operand_ty.zigTypeTag()) { - .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToPtr to cast from '{}'", .{operand_ty.fmt(sema.mod)}), - .Pointer => try sema.errNote(block, dest_ty_src, msg, "use @ptrCast to cast from '{}'", .{operand_ty.fmt(sema.mod)}), + switch (operand_ty.zigTypeTag(mod)) { + .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToPtr to cast from '{}'", .{operand_ty.fmt(mod)}), + .Pointer => try sema.errNote(block, dest_ty_src, msg, "use @ptrCast to cast from '{}'", .{operand_ty.fmt(mod)}), else => {}, } @@ -9690,14 +9813,14 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air }; return sema.failWithOwnedErrorMsg(msg); }, - .Struct, .Union => if (dest_ty.containerLayout() == .Auto) { - const container = switch (dest_ty.zigTypeTag()) { + .Struct, .Union => if (dest_ty.containerLayout(mod) == .Auto) { + const container = switch (dest_ty.zigTypeTag(mod)) { .Struct => "struct", .Union => "union", else => unreachable, }; return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}'; {s} does not have a guaranteed in-memory layout", .{ - dest_ty.fmt(sema.mod), container, + dest_ty.fmt(mod), container, }); }, @@ -9708,7 +9831,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Vector, => {}, } - switch (operand_ty.zigTypeTag()) { + switch (operand_ty.zigTypeTag(mod)) { .AnyFrame, .ComptimeFloat, .ComptimeInt, @@ -9724,14 +9847,14 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Type, .Undefined, .Void, - => return sema.fail(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(sema.mod)}), + => return sema.fail(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)}), .Enum => { const msg = msg: { - const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(sema.mod)}); + const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); - switch (dest_ty.zigTypeTag()) { - .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @enumToInt to cast to '{}'", .{dest_ty.fmt(sema.mod)}), + switch (dest_ty.zigTypeTag(mod)) { + .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @enumToInt to cast to '{}'", .{dest_ty.fmt(mod)}), else => {}, } @@ -9741,11 +9864,11 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air }, .Pointer => { const msg = msg: { - const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(sema.mod)}); + const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); - switch (dest_ty.zigTypeTag()) { - .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @ptrToInt to cast to '{}'", .{dest_ty.fmt(sema.mod)}), - .Pointer => try sema.errNote(block, operand_src, msg, "use @ptrCast to cast to '{}'", .{dest_ty.fmt(sema.mod)}), + switch (dest_ty.zigTypeTag(mod)) { + .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @ptrToInt to cast to '{}'", .{dest_ty.fmt(mod)}), + .Pointer => try sema.errNote(block, operand_src, msg, "use @ptrCast to cast to '{}'", .{dest_ty.fmt(mod)}), else => {}, } @@ -9753,14 +9876,14 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air }; return sema.failWithOwnedErrorMsg(msg); }, - .Struct, .Union => if (operand_ty.containerLayout() == .Auto) { - const container = switch (operand_ty.zigTypeTag()) { + .Struct, .Union => if (operand_ty.containerLayout(mod) == .Auto) { + const container = switch (operand_ty.zigTypeTag(mod)) { .Struct => "struct", .Union => "union", else => unreachable, }; return sema.fail(block, operand_src, "cannot @bitCast from '{}'; {s} does not have a guaranteed in-memory layout", .{ - operand_ty.fmt(sema.mod), container, + operand_ty.fmt(mod), container, }); }, @@ -9778,6 +9901,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; @@ -9786,31 +9910,31 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = try sema.resolveInst(extra.rhs); - const target = sema.mod.getTarget(); - const dest_is_comptime_float = switch (dest_ty.zigTypeTag()) { + const target = mod.getTarget(); + const dest_is_comptime_float = switch (dest_ty.zigTypeTag(mod)) { .ComptimeFloat => true, .Float => false, else => return sema.fail( block, dest_ty_src, "expected float type, found '{}'", - .{dest_ty.fmt(sema.mod)}, + .{dest_ty.fmt(mod)}, ), }; const operand_ty = sema.typeOf(operand); - switch (operand_ty.zigTypeTag()) { + switch (operand_ty.zigTypeTag(mod)) { .ComptimeFloat, .Float, .ComptimeInt => {}, else => return sema.fail( block, operand_src, "expected float type, found '{}'", - .{operand_ty.fmt(sema.mod)}, + .{operand_ty.fmt(mod)}, ), } if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { - return sema.addConstant(dest_ty, try operand_val.floatCast(sema.arena, dest_ty, target)); + return sema.addConstant(dest_ty, try operand_val.floatCast(dest_ty, mod)); } if (dest_is_comptime_float) { return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{}); @@ -9853,20 +9977,21 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const array_ptr = try sema.resolveInst(extra.lhs); const elem_index = try sema.resolveInst(extra.rhs); const indexable_ty = sema.typeOf(array_ptr); - if (indexable_ty.zigTypeTag() != .Pointer) { + if (indexable_ty.zigTypeTag(mod) != .Pointer) { const capture_src: LazySrcLoc = .{ .for_capture_from_input = inst_data.src_node }; const msg = msg: { const msg = try sema.errMsg(block, capture_src, "pointer capture of non pointer type '{}'", .{ - indexable_ty.fmt(sema.mod), + indexable_ty.fmt(mod), }); errdefer msg.destroy(sema.gpa); - if (indexable_ty.zigTypeTag() == .Array) { + if (indexable_ty.isIndexable(mod)) { try sema.errNote(block, src, msg, "consider using '&' here", .{}); } break :msg msg; @@ -9914,7 +10039,7 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; - return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src); + return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src, false); } fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -9931,7 +10056,7 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; - return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src); + return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src, false); } fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -9944,261 +10069,576 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data; const array_ptr = try sema.resolveInst(extra.lhs); const start = try sema.resolveInst(extra.start); - const end = try sema.resolveInst(extra.end); + const end: Air.Inst.Ref = if (extra.end == .none) .none else try sema.resolveInst(extra.end); const sentinel = try sema.resolveInst(extra.sentinel); const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node }; const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; - return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src); + return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false); } -fn zirSwitchCapture( - sema: *Sema, - block: *Block, - inst: Zir.Inst.Index, - is_multi: bool, - is_ref: bool, -) CompileError!Air.Inst.Ref { +fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); - const zir_datas = sema.code.instructions.items(.data); - const capture_info = zir_datas[inst].switch_capture; - const switch_info = zir_datas[capture_info.switch_inst].pl_node; - const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index); - const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node }; - const cond_inst = Zir.refToIndex(switch_extra.data.operand).?; - const cond_info = zir_datas[cond_inst].un_node; - const cond_tag = sema.code.instructions.items(.tag)[cond_inst]; - const operand_is_ref = cond_tag == .switch_cond_ref; - const operand_ptr = try sema.resolveInst(cond_info.operand); - const operand_ptr_ty = sema.typeOf(operand_ptr); - const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty; - - if (block.inline_case_capture != .none) { - const item_val = sema.resolveConstValue(block, .unneeded, block.inline_case_capture, undefined) catch unreachable; - if (operand_ty.zigTypeTag() == .Union) { - const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, sema.mod).?); - const union_obj = operand_ty.cast(Type.Payload.Union).?.data; - const field_ty = union_obj.fields.values()[field_index].ty; - if (try sema.resolveDefinedValue(block, sema.src, operand_ptr)) |union_val| { - if (is_ref) { - const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{ + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data; + const array_ptr = try sema.resolveInst(extra.lhs); + const start = try sema.resolveInst(extra.start); + const len = try sema.resolveInst(extra.len); + const sentinel = if (extra.sentinel == .none) .none else try sema.resolveInst(extra.sentinel); + const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node }; + const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset }; + const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; + const sentinel_src: LazySrcLoc = if (sentinel == .none) + .unneeded + else + .{ .node_offset_slice_sentinel = inst_data.src_node }; + + return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true); +} + +/// Holds common data used when analyzing or resolving switch prong bodies, +/// including setting up captures. +const SwitchProngAnalysis = struct { + sema: *Sema, + /// The block containing the `switch_block` itself. + parent_block: *Block, + /// The raw switch operand value (*not* the condition). Always defined. + operand: Air.Inst.Ref, + /// May be `undefined` if no prong has a by-ref capture. + operand_ptr: Air.Inst.Ref, + /// The switch condition value. For unions, `operand` is the union and `cond` is its tag. + cond: Air.Inst.Ref, + /// If this switch is on an error set, this is the type to assign to the + /// `else` prong. If `null`, the prong should be unreachable. + else_error_ty: ?Type, + /// The index of the `switch_block` instruction itself. + switch_block_inst: Zir.Inst.Index, + /// The dummy index into which inline tag captures should be placed. May be + /// undefined if no prong has a tag capture. + tag_capture_inst: Zir.Inst.Index, + + /// Resolve a switch prong which is determined at comptime to have no peers. + /// Uses `resolveBlockBody`. Sets up captures as needed. + fn resolveProngComptime( + spa: SwitchProngAnalysis, + child_block: *Block, + prong_type: enum { normal, special }, + prong_body: []const Zir.Inst.Index, + capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, + /// Must use the `scalar_capture`, `special_capture`, or `multi_capture` union field. + raw_capture_src: Module.SwitchProngSrc, + /// The set of all values which can reach this prong. May be undefined + /// if the prong is special or contains ranges. + case_vals: []const Air.Inst.Ref, + /// The inline capture of this prong. If this is not an inline prong, + /// this is `.none`. + inline_case_capture: Air.Inst.Ref, + /// Whether this prong has an inline tag capture. If `true`, then + /// `inline_case_capture` cannot be `.none`. + has_tag_capture: bool, + merges: *Block.Merges, + ) CompileError!Air.Inst.Ref { + const sema = spa.sema; + const src = sema.code.instructions.items(.data)[spa.switch_block_inst].pl_node.src(); + + if (has_tag_capture) { + const tag_ref = try spa.analyzeTagCapture(child_block, raw_capture_src, inline_case_capture); + sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); + } + defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); + + switch (capture) { + .none => { + return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges); + }, + + .by_val, .by_ref => { + const capture_ref = try spa.analyzeCapture( + child_block, + capture == .by_ref, + prong_type == .special, + raw_capture_src, + case_vals, + inline_case_capture, + ); + + if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) { + // This prong should be unreachable! + return Air.Inst.Ref.unreachable_value; + } + + sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref); + defer assert(sema.inst_map.remove(spa.switch_block_inst)); + + return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges); + }, + } + } + + /// Analyze a switch prong which may have peers at runtime. + /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed. + fn analyzeProngRuntime( + spa: SwitchProngAnalysis, + case_block: *Block, + prong_type: enum { normal, special }, + prong_body: []const Zir.Inst.Index, + capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, + /// Must use the `scalar`, `special`, or `multi_capture` union field. + raw_capture_src: Module.SwitchProngSrc, + /// The set of all values which can reach this prong. May be undefined + /// if the prong is special or contains ranges. + case_vals: []const Air.Inst.Ref, + /// The inline capture of this prong. If this is not an inline prong, + /// this is `.none`. + inline_case_capture: Air.Inst.Ref, + /// Whether this prong has an inline tag capture. If `true`, then + /// `inline_case_capture` cannot be `.none`. + has_tag_capture: bool, + ) CompileError!void { + const sema = spa.sema; + + if (has_tag_capture) { + const tag_ref = try spa.analyzeTagCapture(case_block, raw_capture_src, inline_case_capture); + sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); + } + defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); + + switch (capture) { + .none => { + return sema.analyzeBodyRuntimeBreak(case_block, prong_body); + }, + + .by_val, .by_ref => { + const capture_ref = try spa.analyzeCapture( + case_block, + capture == .by_ref, + prong_type == .special, + raw_capture_src, + case_vals, + inline_case_capture, + ); + + if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) { + // No need to analyze any further, the prong is unreachable + return; + } + + sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref); + defer assert(sema.inst_map.remove(spa.switch_block_inst)); + + return sema.analyzeBodyRuntimeBreak(case_block, prong_body); + }, + } + } + + fn analyzeTagCapture( + spa: SwitchProngAnalysis, + block: *Block, + raw_capture_src: Module.SwitchProngSrc, + inline_case_capture: Air.Inst.Ref, + ) CompileError!Air.Inst.Ref { + const sema = spa.sema; + const mod = sema.mod; + const operand_ty = sema.typeOf(spa.operand); + if (operand_ty.zigTypeTag(mod) != .Union) { + const zir_datas = sema.code.instructions.items(.data); + const switch_node_offset = zir_datas[spa.switch_block_inst].pl_node.src_node; + const raw_tag_capture_src: Module.SwitchProngSrc = switch (raw_capture_src) { + .scalar_capture => |i| .{ .scalar_tag_capture = i }, + .multi_capture => |i| .{ .multi_tag_capture = i }, + .special_capture => .special_tag_capture, + else => unreachable, + }; + const capture_src = raw_tag_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none); + const msg = msg: { + const msg = try sema.errMsg(block, capture_src, "cannot capture tag of non-union type '{}'", .{ + operand_ty.fmt(mod), + }); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, operand_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + assert(inline_case_capture != .none); + return inline_case_capture; + } + + fn analyzeCapture( + spa: SwitchProngAnalysis, + block: *Block, + capture_byref: bool, + is_special_prong: bool, + raw_capture_src: Module.SwitchProngSrc, + case_vals: []const Air.Inst.Ref, + inline_case_capture: Air.Inst.Ref, + ) CompileError!Air.Inst.Ref { + const sema = spa.sema; + const mod = sema.mod; + + const zir_datas = sema.code.instructions.items(.data); + const switch_node_offset = zir_datas[spa.switch_block_inst].pl_node.src_node; + + const operand_ty = sema.typeOf(spa.operand); + const operand_ptr_ty = if (capture_byref) sema.typeOf(spa.operand_ptr) else undefined; + const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_node_offset }; + + if (inline_case_capture != .none) { + const item_val = sema.resolveConstValue(block, .unneeded, inline_case_capture, "") catch unreachable; + if (operand_ty.zigTypeTag(mod) == .Union) { + const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, mod).?); + const union_obj = mod.typeToUnion(operand_ty).?; + const field_ty = union_obj.fields.values()[field_index].ty; + if (capture_byref) { + const ptr_field_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = field_ty, - .mutable = operand_ptr_ty.ptrIsMutable(), - .@"volatile" = operand_ptr_ty.isVolatilePtr(), - .@"addrspace" = operand_ptr_ty.ptrAddressSpace(), + .mutable = operand_ptr_ty.ptrIsMutable(mod), + .@"volatile" = operand_ptr_ty.isVolatilePtr(mod), + .@"addrspace" = operand_ptr_ty.ptrAddressSpace(mod), }); - return sema.addConstant( - ptr_field_ty, - try Value.Tag.field_ptr.create(sema.arena, .{ - .container_ptr = union_val, - .container_ty = operand_ty, - .field_index = field_index, - }), - ); + if (try sema.resolveDefinedValue(block, sema.src, spa.operand_ptr)) |union_ptr| { + return sema.addConstant( + ptr_field_ty, + (try mod.intern(.{ .ptr = .{ + .ty = ptr_field_ty.toIntern(), + .addr = .{ .field = .{ + .base = union_ptr.toIntern(), + .index = field_index, + } }, + } })).toValue(), + ); + } + return block.addStructFieldPtr(spa.operand_ptr, field_index, ptr_field_ty); + } else { + if (try sema.resolveDefinedValue(block, sema.src, spa.operand)) |union_val| { + const tag_and_val = mod.intern_pool.indexToKey(union_val.toIntern()).un; + return sema.addConstant(field_ty, tag_and_val.val.toValue()); + } + return block.addStructFieldVal(spa.operand, field_index, field_ty); } - const tag_and_val = union_val.castTag(.@"union").?.data; - return sema.addConstant(field_ty, tag_and_val.val); - } - if (is_ref) { - const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = field_ty, - .mutable = operand_ptr_ty.ptrIsMutable(), - .@"volatile" = operand_ptr_ty.isVolatilePtr(), - .@"addrspace" = operand_ptr_ty.ptrAddressSpace(), - }); - return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty); + } else if (capture_byref) { + return sema.addConstantMaybeRef(block, operand_ty, item_val, true); } else { - return block.addStructFieldVal(operand_ptr, field_index, field_ty); + return inline_case_capture; } - } else if (is_ref) { - return sema.addConstantMaybeRef(block, operand_ty, item_val, true); - } else { - return block.inline_case_capture; } - } - const operand = if (operand_is_ref) - try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) - else - operand_ptr; + if (is_special_prong) { + if (capture_byref) { + return spa.operand_ptr; + } - if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) { - // It is the else/`_` prong. - if (is_ref) { - return operand_ptr; + switch (operand_ty.zigTypeTag(mod)) { + .ErrorSet => if (spa.else_error_ty) |ty| { + return sema.bitCast(block, ty, spa.operand, operand_src, null); + } else { + try block.addUnreachable(false); + return Air.Inst.Ref.unreachable_value; + }, + else => return spa.operand, + } } - switch (operand_ty.zigTypeTag()) { - .ErrorSet => if (block.switch_else_err_ty) |some| { - return sema.bitCast(block, some, operand, operand_src, null); - } else { - try block.addUnreachable(false); - return Air.Inst.Ref.unreachable_value; - }, - else => return operand, - } - } + switch (operand_ty.zigTypeTag(mod)) { + .Union => { + const union_obj = mod.typeToUnion(operand_ty).?; + const first_item_val = sema.resolveConstValue(block, .unneeded, case_vals[0], "") catch unreachable; - const items = if (is_multi) - switch_extra.data.getMultiProng(sema.code, switch_extra.end, capture_info.prong_index).items - else - &[_]Zir.Inst.Ref{ - switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index).item, - }; + const first_field_index = @intCast(u32, operand_ty.unionTagFieldIndex(first_item_val, mod).?); + const first_field = union_obj.fields.values()[first_field_index]; - switch (operand_ty.zigTypeTag()) { - .Union => { - const union_obj = operand_ty.cast(Type.Payload.Union).?.data; - const first_item = try sema.resolveInst(items[0]); - // Previous switch validation ensured this will succeed - const first_item_val = sema.resolveConstValue(block, .unneeded, first_item, "") catch unreachable; - - const first_field_index = @intCast(u32, operand_ty.unionTagFieldIndex(first_item_val, sema.mod).?); - const first_field = union_obj.fields.values()[first_field_index]; - - for (items[1..], 0..) |item, i| { - const item_ref = try sema.resolveInst(item); - // Previous switch validation ensured this will succeed - const item_val = sema.resolveConstValue(block, .unneeded, item_ref, "") catch unreachable; - - const field_index = operand_ty.unionTagFieldIndex(item_val, sema.mod).?; - const field = union_obj.fields.values()[field_index]; - if (!field.ty.eql(first_field.ty, sema.mod)) { - const msg = msg: { - const raw_capture_src = Module.SwitchProngSrc{ .multi_capture = capture_info.prong_index }; - const capture_src = raw_capture_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first); + const field_tys = try sema.arena.alloc(Type, case_vals.len); + for (case_vals, field_tys) |item, *field_ty| { + const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable; + const field_idx = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, sema.mod).?); + field_ty.* = union_obj.fields.values()[field_idx].ty; + } - const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{}); - errdefer msg.destroy(sema.gpa); + // Fast path: if all the operands are the same type already, we don't need to hit + // PTR! This will also allow us to emit simpler code. + const same_types = for (field_tys[1..]) |field_ty| { + if (!field_ty.eql(field_tys[0], sema.mod)) break false; + } else true; - const raw_first_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = capture_info.prong_index, .item = 0 } }; - const first_item_src = raw_first_item_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first); - const raw_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = capture_info.prong_index, .item = 1 + @intCast(u32, i) } }; - const item_src = raw_item_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first); - try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(sema.mod)}); - try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(sema.mod)}); - break :msg msg; + const capture_ty = if (same_types) field_tys[0] else capture_ty: { + // We need values to run PTR on, so make a bunch of undef constants. + const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); + for (dummy_captures, field_tys) |*dummy, field_ty| { + dummy.* = try sema.addConstUndef(field_ty); + } + + const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); + @memset(case_srcs, .unneeded); + + break :capture_ty sema.resolvePeerTypes(block, .unneeded, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) { + error.NeededSourceLocation => { + // This must be a multi-prong so this must be a `multi_capture` src + const multi_idx = raw_capture_src.multi_capture; + const src_decl_ptr = sema.mod.declPtr(block.src_decl); + for (case_srcs, 0..) |*case_src, i| { + const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(u32, i) } }; + case_src.* = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); + } + const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); + _ = sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err1| switch (err1) { + error.AnalysisFail => { + const msg = sema.err orelse return error.AnalysisFail; + try sema.reparentOwnedErrorMsg(block, capture_src, msg, "capture group with incompatible types", .{}); + return error.AnalysisFail; + }, + else => |e| return e, + }; + unreachable; + }, + else => |e| return e, }; - return sema.failWithOwnedErrorMsg(msg); - } - } + }; - if (is_ref) { - const field_ty_ptr = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = first_field.ty, - .@"addrspace" = .generic, - .mutable = operand_ptr_ty.ptrIsMutable(), - }); + // By-reference captures have some further restrictions which make them easier to emit + if (capture_byref) { + const operand_ptr_info = operand_ptr_ty.ptrInfo(mod); + const capture_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = capture_ty, + .@"addrspace" = operand_ptr_info.@"addrspace", + .mutable = operand_ptr_info.mutable, + .@"volatile" = operand_ptr_info.@"volatile", + // TODO: alignment! + }); - if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| { - return sema.addConstant( - field_ty_ptr, - try Value.Tag.field_ptr.create(sema.arena, .{ - .container_ptr = op_ptr_val, - .container_ty = operand_ty, - .field_index = first_field_index, - }), - ); + // By-ref captures of hetereogeneous types are only allowed if each field + // pointer type is in-memory coercible to the capture pointer type. + if (!same_types) { + for (field_tys, 0..) |field_ty, i| { + const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = field_ty, + .@"addrspace" = operand_ptr_info.@"addrspace", + .mutable = operand_ptr_info.mutable, + .@"volatile" = operand_ptr_info.@"volatile", + // TODO: alignment! + }); + if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { + const multi_idx = raw_capture_src.multi_capture; + const src_decl_ptr = sema.mod.declPtr(block.src_decl); + const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); + const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(u32, i) } }; + const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); + const msg = msg: { + const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, case_src, msg, "pointer type child '{}' cannot cast into resolved pointer type child '{}'", .{ + field_ty.fmt(sema.mod), + capture_ty.fmt(sema.mod), + }); + try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + } + } + + if (try sema.resolveDefinedValue(block, operand_src, spa.operand_ptr)) |op_ptr_val| { + if (op_ptr_val.isUndef(mod)) return sema.addConstUndef(capture_ptr_ty); + return sema.addConstant( + capture_ptr_ty, + (try mod.intern(.{ .ptr = .{ + .ty = capture_ptr_ty.toIntern(), + .addr = .{ .field = .{ + .base = op_ptr_val.toIntern(), + .index = first_field_index, + } }, + } })).toValue(), + ); + } + + try sema.requireRuntimeBlock(block, operand_src, null); + return block.addStructFieldPtr(spa.operand_ptr, first_field_index, capture_ptr_ty); + } + + if (try sema.resolveDefinedValue(block, operand_src, spa.operand)) |operand_val| { + if (operand_val.isUndef(mod)) return sema.addConstUndef(capture_ty); + const union_val = mod.intern_pool.indexToKey(operand_val.toIntern()).un; + if (union_val.tag.toValue().isUndef(mod)) return sema.addConstUndef(capture_ty); + const active_field_idx = @intCast(u32, operand_ty.unionTagFieldIndex(union_val.tag.toValue(), sema.mod).?); + const field_ty = union_obj.fields.values()[active_field_idx].ty; + const uncoerced = try sema.addConstant(field_ty, union_val.val.toValue()); + return sema.coerce(block, capture_ty, uncoerced, operand_src); } + try sema.requireRuntimeBlock(block, operand_src, null); - return block.addStructFieldPtr(operand_ptr, first_field_index, field_ty_ptr); - } - if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| { - return sema.addConstant( - first_field.ty, - operand_val.castTag(.@"union").?.data.val, - ); - } - try sema.requireRuntimeBlock(block, operand_src, null); - return block.addStructFieldVal(operand, first_field_index, first_field.ty); - }, - .ErrorSet => { - if (is_multi) { - var names: Module.ErrorSet.NameMap = .{}; - try names.ensureUnusedCapacity(sema.arena, items.len); - for (items) |item| { - const item_ref = try sema.resolveInst(item); - // Previous switch validation ensured this will succeed - const item_val = sema.resolveConstValue(block, .unneeded, item_ref, "") catch unreachable; - names.putAssumeCapacityNoClobber( - item_val.getError().?, - {}, - ); + if (same_types) { + return block.addStructFieldVal(spa.operand, first_field_index, capture_ty); } - // names must be sorted - Module.ErrorSet.sortNames(&names); - const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); - return sema.bitCast(block, else_error_ty, operand, operand_src, null); - } else { - const item_ref = try sema.resolveInst(items[0]); - // Previous switch validation ensured this will succeed - const item_val = sema.resolveConstValue(block, .unneeded, item_ref, "") catch unreachable; + // We may have to emit a switch block which coerces the operand to the capture type. + // If we can, try to avoid that using in-memory coercions. + const first_non_imc = in_mem: { + for (field_tys, 0..) |field_ty, i| { + if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { + break :in_mem i; + } + } + // All fields are in-memory coercible to the resolved type! + // Just take the first field and bitcast the result. + const uncoerced = try block.addStructFieldVal(spa.operand, first_field_index, first_field.ty); + return block.addBitCast(capture_ty, uncoerced); + }; - const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?); - return sema.bitCast(block, item_ty, operand, operand_src, null); - } - }, - else => { - // In this case the capture value is just the passed-through value of the - // switch condition. - if (is_ref) { - return operand_ptr; - } else { - return operand; - } - }, - } -} + // By-val capture with heterogeneous types which are not all in-memory coercible to + // the resolved capture type. We finally have to fall back to the ugly method. -fn zirSwitchCaptureTag(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const zir_datas = sema.code.instructions.items(.data); - const inst_data = zir_datas[inst].un_tok; - const src = inst_data.src(); + // However, let's first track which operands are in-memory coercible. There may well + // be several, and we can squash all of these cases into the same switch prong using + // a simple bitcast. We'll make this the 'else' prong. - const switch_tag = sema.code.instructions.items(.tag)[Zir.refToIndex(inst_data.operand).?]; - const is_ref = switch_tag == .switch_cond_ref; - const cond_data = zir_datas[Zir.refToIndex(inst_data.operand).?].un_node; - const operand_ptr = try sema.resolveInst(cond_data.operand); - const operand_ptr_ty = sema.typeOf(operand_ptr); - const operand_ty = if (is_ref) operand_ptr_ty.childType() else operand_ptr_ty; + var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_tys.len); + in_mem_coercible.unset(first_non_imc); + { + const next = first_non_imc + 1; + for (field_tys[next..], next..) |field_ty, i| { + if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { + in_mem_coercible.unset(i); + } + } + } - if (operand_ty.zigTypeTag() != .Union) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "cannot capture tag of non-union type '{}'", .{ - operand_ty.fmt(sema.mod), - }); - errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, operand_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(msg); - } + const capture_block_inst = try block.addInstAsIndex(.{ + .tag = .block, + .data = .{ + .ty_pl = .{ + .ty = try sema.addType(capture_ty), + .payload = undefined, // updated below + }, + }, + }); - return block.inline_case_capture; -} + const prong_count = field_tys.len - in_mem_coercible.count(); + + const estimated_extra = prong_count * 6; // 2 for Case, 1 item, probably 3 insts + var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra); + defer cases_extra.deinit(); + + { + // Non-bitcast cases + var it = in_mem_coercible.iterator(.{ .kind = .unset }); + while (it.next()) |idx| { + var coerce_block = block.makeSubBlock(); + defer coerce_block.instructions.deinit(sema.gpa); + + const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @intCast(u32, idx), field_tys[idx]); + const coerced = sema.coerce(&coerce_block, capture_ty, uncoerced, .unneeded) catch |err| switch (err) { + error.NeededSourceLocation => { + const multi_idx = raw_capture_src.multi_capture; + const src_decl_ptr = sema.mod.declPtr(block.src_decl); + const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(u32, idx) } }; + const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); + _ = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); + unreachable; + }, + else => |e| return e, + }; + _ = try coerce_block.addBr(capture_block_inst, coerced); + + try cases_extra.ensureUnusedCapacity(3 + coerce_block.instructions.items.len); + cases_extra.appendAssumeCapacity(1); // items_len + cases_extra.appendAssumeCapacity(@intCast(u32, coerce_block.instructions.items.len)); // body_len + cases_extra.appendAssumeCapacity(@enumToInt(case_vals[idx])); // item + cases_extra.appendSliceAssumeCapacity(coerce_block.instructions.items); // body + } + } + const else_body_len = len: { + // 'else' prong uses a bitcast + var coerce_block = block.makeSubBlock(); + defer coerce_block.instructions.deinit(sema.gpa); + + const first_imc = in_mem_coercible.findFirstSet().?; + const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @intCast(u32, first_imc), field_tys[first_imc]); + const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); + _ = try coerce_block.addBr(capture_block_inst, coerced); + + try cases_extra.appendSlice(coerce_block.instructions.items); + break :len coerce_block.instructions.items.len; + }; + + try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).Struct.fields.len + + cases_extra.items.len + + @typeInfo(Air.Block).Struct.fields.len + + 1); + + const switch_br_inst = @intCast(u32, sema.air_instructions.len); + try sema.air_instructions.append(sema.gpa, .{ + .tag = .switch_br, + .data = .{ .pl_op = .{ + .operand = spa.cond, + .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ + .cases_len = @intCast(u32, prong_count), + .else_body_len = @intCast(u32, else_body_len), + }), + } }, + }); + sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); + + // Set up block body + sema.air_instructions.items(.data)[capture_block_inst].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ + .body_len = 1, + }); + sema.air_extra.appendAssumeCapacity(switch_br_inst); + + return Air.indexToRef(capture_block_inst); + }, + .ErrorSet => { + if (capture_byref) { + const capture_src = raw_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none); + return sema.fail( + block, + capture_src, + "error set cannot be captured by reference", + .{}, + ); + } + + if (case_vals.len == 1) { + const item_val = sema.resolveConstValue(block, .unneeded, case_vals[0], "") catch unreachable; + const item_ty = try mod.singleErrorSetType(item_val.getErrorName(mod).unwrap().?); + return sema.bitCast(block, item_ty, spa.operand, operand_src, null); + } + + var names: Module.Fn.InferredErrorSet.NameMap = .{}; + try names.ensureUnusedCapacity(sema.arena, case_vals.len); + for (case_vals) |err| { + const err_val = sema.resolveConstValue(block, .unneeded, err, "") catch unreachable; + names.putAssumeCapacityNoClobber(err_val.getErrorName(mod).unwrap().?, {}); + } + const error_ty = try mod.errorSetFromUnsortedNames(names.keys()); + return sema.bitCast(block, error_ty, spa.operand, operand_src, null); + }, + else => { + // In this case the capture value is just the passed-through value + // of the switch condition. + if (capture_byref) { + return spa.operand_ptr; + } else { + return spa.operand; + } + }, + } + } +}; -fn zirSwitchCond( +fn switchCond( sema: *Sema, block: *Block, - inst: Zir.Inst.Index, - is_ref: bool, + src: LazySrcLoc, + operand: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { - const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); - const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node }; - const operand_ptr = try sema.resolveInst(inst_data.operand); - const operand = if (is_ref) - try sema.analyzeLoad(block, src, operand_ptr, operand_src) - else - operand_ptr; + const mod = sema.mod; const operand_ty = sema.typeOf(operand); - - switch (operand_ty.zigTypeTag()) { + switch (operand_ty.zigTypeTag(mod)) { .Type, .Void, .Bool, @@ -10212,8 +10652,8 @@ fn zirSwitchCond( .ErrorSet, .Enum, => { - if (operand_ty.isSlice()) { - return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(sema.mod)}); + if (operand_ty.isSlice(mod)) { + return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(mod)}); } if ((try sema.typeHasOnePossibleValue(operand_ty))) |opv| { return sema.addConstant(operand_ty, opv); @@ -10223,12 +10663,12 @@ fn zirSwitchCond( .Union => { const union_ty = try sema.resolveTypeFields(operand_ty); - const enum_ty = union_ty.unionTagType() orelse { + const enum_ty = union_ty.unionTagType(mod) orelse { const msg = msg: { const msg = try sema.errMsg(block, src, "switch on union with no attached enum", .{}); errdefer msg.destroy(sema.gpa); - if (union_ty.declSrcLocOrNull(sema.mod)) |union_src| { - try sema.mod.errNoteNonLazy(union_src, msg, "consider 'union(enum)' here", .{}); + if (union_ty.declSrcLocOrNull(mod)) |union_src| { + try mod.errNoteNonLazy(union_src, msg, "consider 'union(enum)' here", .{}); } break :msg msg; }; @@ -10248,17 +10688,19 @@ fn zirSwitchCond( .Vector, .Frame, .AnyFrame, - => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(sema.mod)}), + => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(mod)}), } } -const SwitchErrorSet = std.StringHashMap(Module.SwitchProngSrc); +const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, Module.SwitchProngSrc); -fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const gpa = sema.gpa; + const ip = &mod.intern_pool; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const src_node_offset = inst_data.src_node; @@ -10266,10 +10708,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset }; const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); - const operand = try sema.resolveInst(extra.data.operand); - // AstGen guarantees that the instruction immediately following - // switch_cond(_ref) is a dbg_stmt - const cond_dbg_node_index = Zir.refToIndex(extra.data.operand).? + 1; + const raw_operand: struct { val: Air.Inst.Ref, ptr: Air.Inst.Ref } = blk: { + const maybe_ptr = try sema.resolveInst(extra.data.operand); + if (operand_is_ref) { + const val = try sema.analyzeLoad(block, src, maybe_ptr, operand_src); + break :blk .{ .val = val, .ptr = maybe_ptr }; + } else { + break :blk .{ .val = maybe_ptr, .ptr = undefined }; + } + }; + + const operand = try sema.switchCond(block, operand_src, raw_operand.val); + + // AstGen guarantees that the instruction immediately preceding + // switch_block(_ref) is a dbg_stmt + const cond_dbg_node_index = inst - 1; var header_extra_index: usize = extra.end; @@ -10280,34 +10733,56 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError break :blk multi_cases_len; } else 0; + const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: { + const tag_capture_inst = sema.code.extra[header_extra_index]; + header_extra_index += 1; + // SwitchProngAnalysis wants inst_map to have space for the tag capture. + // Note that the normal capture is referred to via the switch block + // index, which there is already necessarily space for. + try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst}); + break :blk tag_capture_inst; + } else undefined; + + var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); + defer case_vals.deinit(gpa); + + const Special = struct { + body: []const Zir.Inst.Index, + end: usize, + capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, + is_inline: bool, + has_tag_capture: bool, + }; + const special_prong = extra.data.bits.specialProng(); - const special: struct { body: []const Zir.Inst.Index, end: usize, is_inline: bool } = switch (special_prong) { - .none => .{ .body = &.{}, .end = header_extra_index, .is_inline = false }, + const special: Special = switch (special_prong) { + .none => .{ + .body = &.{}, + .end = header_extra_index, + .capture = .none, + .is_inline = false, + .has_tag_capture = false, + }, .under, .@"else" => blk: { - const body_len = @truncate(u31, sema.code.extra[header_extra_index]); + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[header_extra_index]); const extra_body_start = header_extra_index + 1; break :blk .{ - .body = sema.code.extra[extra_body_start..][0..body_len], - .end = extra_body_start + body_len, - .is_inline = sema.code.extra[header_extra_index] >> 31 != 0, + .body = sema.code.extra[extra_body_start..][0..info.body_len], + .end = extra_body_start + info.body_len, + .capture = info.capture, + .is_inline = info.is_inline, + .has_tag_capture = info.has_tag_capture, }; }, }; - const maybe_union_ty = blk: { - const zir_tags = sema.code.instructions.items(.tag); - const zir_data = sema.code.instructions.items(.data); - const cond_index = Zir.refToIndex(extra.data.operand).?; - const raw_operand = sema.resolveInst(zir_data[cond_index].un_node.operand) catch unreachable; - const target_ty = sema.typeOf(raw_operand); - break :blk if (zir_tags[cond_index] == .switch_cond_ref) target_ty.elemType() else target_ty; - }; - const union_originally = maybe_union_ty.zigTypeTag() == .Union; + const maybe_union_ty = sema.typeOf(raw_operand.val); + const union_originally = maybe_union_ty.zigTypeTag(mod) == .Union; // Duplicate checking variables later also used for `inline else`. var seen_enum_fields: []?Module.SwitchProngSrc = &.{}; var seen_errors = SwitchErrorSet.init(gpa); - var range_set = RangeSet.init(gpa, sema.mod); + var range_set = RangeSet.init(gpa, mod); var true_count: u8 = 0; var false_count: u8 = 0; @@ -10320,12 +10795,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var empty_enum = false; const operand_ty = sema.typeOf(operand); - const err_set = operand_ty.zigTypeTag() == .ErrorSet; + const err_set = operand_ty.zigTypeTag(mod) == .ErrorSet; var else_error_ty: ?Type = null; // Validate usage of '_' prongs. - if (special_prong == .under and (!operand_ty.isNonexhaustiveEnum() or union_originally)) { + if (special_prong == .under and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) { const msg = msg: { const msg = try sema.errMsg( block, @@ -10346,14 +10821,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.failWithOwnedErrorMsg(msg); } - const target = sema.mod.getTarget(); - // Validate for duplicate items, missing else prong, and invalid range. - switch (operand_ty.zigTypeTag()) { + switch (operand_ty.zigTypeTag(mod)) { .Union => unreachable, // handled in zirSwitchCond .Enum => { - seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount()); - empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum(); + seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount(mod)); + empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum(mod); @memset(seen_enum_fields, null); // `range_set` is used for non-exhaustive enum values that do not correspond to any tags. @@ -10363,18 +10836,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError while (scalar_i < scalar_cases_len) : (scalar_i += 1) { const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); - extra_index += 1; - extra_index += body_len; + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); + extra_index += 1 + info.body_len; - try sema.validateSwitchItemEnum( + case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum( block, seen_enum_fields, &range_set, item_ref, + operand_ty, src_node_offset, .{ .scalar = scalar_i }, - ); + )); } } { @@ -10384,20 +10857,22 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError extra_index += 1; const ranges_len = sema.code.extra[extra_index]; extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); extra_index += 1; const items = sema.code.refSlice(extra_index, items_len); - extra_index += items_len + body_len; + extra_index += items_len + info.body_len; + try case_vals.ensureUnusedCapacity(gpa, items.len); for (items, 0..) |item_ref, item_i| { - try sema.validateSwitchItemEnum( + case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum( block, seen_enum_fields, &range_set, item_ref, + operand_ty, src_node_offset, .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, - ); + )); } try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); @@ -10408,7 +10883,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } else true; if (special_prong == .@"else") { - if (all_tags_handled and !operand_ty.isNonexhaustiveEnum()) return sema.fail( + if (all_tags_handled and !operand_ty.isNonexhaustiveEnum(mod)) return sema.fail( block, special_prong_src, "unreachable else prong; all cases already handled", @@ -10426,25 +10901,25 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError for (seen_enum_fields, 0..) |seen_src, i| { if (seen_src != null) continue; - const field_name = operand_ty.enumFieldName(i); + const field_name = operand_ty.enumFieldName(i, mod); try sema.addFieldErrNote( operand_ty, i, msg, - "unhandled enumeration value: '{s}'", - .{field_name}, + "unhandled enumeration value: '{}'", + .{field_name.fmt(&mod.intern_pool)}, ); } - try sema.mod.errNoteNonLazy( - operand_ty.declSrcLoc(sema.mod), + try mod.errNoteNonLazy( + operand_ty.declSrcLoc(mod), msg, "enum '{}' declared here", - .{operand_ty.fmt(sema.mod)}, + .{operand_ty.fmt(mod)}, ); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum() and !union_originally) { + } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum(mod) and !union_originally) { return sema.fail( block, src, @@ -10460,17 +10935,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError while (scalar_i < scalar_cases_len) : (scalar_i += 1) { const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); - extra_index += 1; - extra_index += body_len; + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); + extra_index += 1 + info.body_len; - try sema.validateSwitchItemError( + case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( block, &seen_errors, item_ref, + operand_ty, src_node_offset, .{ .scalar = scalar_i }, - ); + )); } } { @@ -10480,19 +10955,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError extra_index += 1; const ranges_len = sema.code.extra[extra_index]; extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); extra_index += 1; const items = sema.code.refSlice(extra_index, items_len); - extra_index += items_len + body_len; + extra_index += items_len + info.body_len; + try case_vals.ensureUnusedCapacity(gpa, items.len); for (items, 0..) |item_ref, item_i| { - try sema.validateSwitchItemError( + case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( block, &seen_errors, item_ref, + operand_ty, src_node_offset, .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, - ); + )); } try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); @@ -10501,7 +10978,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError try sema.resolveInferredErrorSetTy(block, src, operand_ty); - if (operand_ty.isAnyError()) { + if (operand_ty.isAnyError(mod)) { if (special_prong != .@"else") { return sema.fail( block, @@ -10515,7 +10992,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var maybe_msg: ?*Module.ErrorMsg = null; errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa); - for (operand_ty.errorSetNames()) |error_name| { + for (operand_ty.errorSetNames(mod)) |error_name| { if (!seen_errors.contains(error_name) and special_prong != .@"else") { const msg = maybe_msg orelse blk: { maybe_msg = try sema.errMsg( @@ -10531,8 +11008,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError block, src, msg, - "unhandled error value: 'error.{s}'", - .{error_name}, + "unhandled error value: 'error.{}'", + .{error_name.fmt(ip)}, ); } } @@ -10543,7 +11020,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.failWithOwnedErrorMsg(msg); } - if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames().len) { + if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames(mod).len) { // In order to enable common patterns for generic code allow simple else bodies // else => unreachable, // else => return, @@ -10555,7 +11032,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError .dbg_block_end, .dbg_stmt, .dbg_var_val, - .switch_capture, .ret_type, .as_node, .ret_node, @@ -10580,18 +11056,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError ); } - const error_names = operand_ty.errorSetNames(); - var names: Module.ErrorSet.NameMap = .{}; + const error_names = operand_ty.errorSetNames(mod); + var names: Module.Fn.InferredErrorSet.NameMap = .{}; try names.ensureUnusedCapacity(sema.arena, error_names.len); for (error_names) |error_name| { if (seen_errors.contains(error_name)) continue; names.putAssumeCapacityNoClobber(error_name, {}); } - - // names must be sorted - Module.ErrorSet.sortNames(&names); - else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); + // No need to keep the hash map metadata correct; here we + // extract the (sorted) keys only. + else_error_ty = try mod.errorSetFromUnsortedNames(names.keys()); } }, .Int, .ComptimeInt => { @@ -10601,18 +11076,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError while (scalar_i < scalar_cases_len) : (scalar_i += 1) { const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); - extra_index += 1; - extra_index += body_len; + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); + extra_index += 1 + info.body_len; - try sema.validateSwitchItem( + case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt( block, &range_set, item_ref, operand_ty, src_node_offset, .{ .scalar = scalar_i }, - ); + )); } } { @@ -10622,22 +11096,24 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError extra_index += 1; const ranges_len = sema.code.extra[extra_index]; extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); extra_index += 1; const items = sema.code.refSlice(extra_index, items_len); extra_index += items_len; + try case_vals.ensureUnusedCapacity(gpa, items.len); for (items, 0..) |item_ref, item_i| { - try sema.validateSwitchItem( + case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt( block, &range_set, item_ref, operand_ty, src_node_offset, .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, - ); + )); } + try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len); var range_i: u32 = 0; while (range_i < ranges_len) : (range_i += 1) { const item_first = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); @@ -10645,7 +11121,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const item_last = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - try sema.validateSwitchRange( + const vals = try sema.validateSwitchRange( block, &range_set, item_first, @@ -10654,20 +11130,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError src_node_offset, .{ .range = .{ .prong = multi_i, .item = range_i } }, ); + case_vals.appendAssumeCapacity(vals[0]); + case_vals.appendAssumeCapacity(vals[1]); } - extra_index += body_len; + extra_index += info.body_len; } } check_range: { - if (operand_ty.zigTypeTag() == .Int) { - var arena = std.heap.ArenaAllocator.init(gpa); - defer arena.deinit(); - - const min_int = try operand_ty.minInt(arena.allocator(), target); - const max_int = try operand_ty.maxInt(arena.allocator(), target); - if (try range_set.spans(min_int, max_int, operand_ty)) { + if (operand_ty.zigTypeTag(mod) == .Int) { + const min_int = try operand_ty.minInt(mod, operand_ty); + const max_int = try operand_ty.maxInt(mod, operand_ty); + if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) { if (special_prong == .@"else") { return sema.fail( block, @@ -10696,18 +11171,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError while (scalar_i < scalar_cases_len) : (scalar_i += 1) { const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); - extra_index += 1; - extra_index += body_len; + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); + extra_index += 1 + info.body_len; - try sema.validateSwitchItemBool( + case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool( block, &true_count, &false_count, item_ref, src_node_offset, .{ .scalar = scalar_i }, - ); + )); } } { @@ -10717,20 +11191,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError extra_index += 1; const ranges_len = sema.code.extra[extra_index]; extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); extra_index += 1; const items = sema.code.refSlice(extra_index, items_len); - extra_index += items_len + body_len; + extra_index += items_len + info.body_len; + try case_vals.ensureUnusedCapacity(gpa, items.len); for (items, 0..) |item_ref, item_i| { - try sema.validateSwitchItemBool( + case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool( block, &true_count, &false_count, item_ref, src_node_offset, .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, - ); + )); } try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); @@ -10765,15 +11240,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError block, src, "else prong required when switching on type '{}'", - .{operand_ty.fmt(sema.mod)}, + .{operand_ty.fmt(mod)}, ); } - var seen_values = ValueSrcMap.initContext(gpa, .{ - .ty = operand_ty, - .mod = sema.mod, - }); - defer seen_values.deinit(); + var seen_values = ValueSrcMap{}; + defer seen_values.deinit(gpa); var extra_index: usize = special.end; { @@ -10781,17 +11253,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError while (scalar_i < scalar_cases_len) : (scalar_i += 1) { const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); extra_index += 1; - extra_index += body_len; + extra_index += info.body_len; - try sema.validateSwitchItemSparse( + case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse( block, &seen_values, item_ref, + operand_ty, src_node_offset, .{ .scalar = scalar_i }, - ); + )); } } { @@ -10801,19 +11274,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError extra_index += 1; const ranges_len = sema.code.extra[extra_index]; extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); extra_index += 1; const items = sema.code.refSlice(extra_index, items_len); - extra_index += items_len + body_len; + extra_index += items_len + info.body_len; + try case_vals.ensureUnusedCapacity(gpa, items.len); for (items, 0..) |item_ref, item_i| { - try sema.validateSwitchItemSparse( + case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse( block, &seen_values, item_ref, + operand_ty, src_node_offset, .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, - ); + )); } try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); @@ -10835,10 +11310,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError .ComptimeFloat, .Float, => return sema.fail(block, operand_src, "invalid switch operand type '{}'", .{ - operand_ty.fmt(sema.mod), + operand_ty.fmt(mod), }), } + const spa: SwitchProngAnalysis = .{ + .sema = sema, + .parent_block = block, + .operand = raw_operand.val, + .operand_ptr = raw_operand.ptr, + .cond = operand, + .else_error_ty = else_error_ty, + .switch_block_inst = inst, + .tag_capture_inst = tag_capture_inst, + }; + const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len); try sema.air_instructions.append(gpa, .{ .tag = .block, @@ -10866,7 +11352,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError .is_comptime = block.is_comptime, .comptime_reason = block.comptime_reason, .is_typeof = block.is_typeof, - .switch_else_err_ty = else_error_ty, .c_import_buf = block.c_import_buf, .runtime_cond = block.runtime_cond, .runtime_loop = block.runtime_loop, @@ -10878,83 +11363,115 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError defer merges.deinit(gpa); if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| { + const resolved_operand_val = try sema.resolveLazyValue(operand_val); var extra_index: usize = special.end; { var scalar_i: usize = 0; while (scalar_i < scalar_cases_len) : (scalar_i += 1) { - const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); - const is_inline = sema.code.extra[extra_index] >> 31 != 0; + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); extra_index += 1; - const body = sema.code.extra[extra_index..][0..body_len]; - extra_index += body_len; + const body = sema.code.extra[extra_index..][0..info.body_len]; + extra_index += info.body_len; - const item = try sema.resolveInst(item_ref); - // Validation above ensured these will succeed. + const item = case_vals.items[scalar_i]; const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable; if (operand_val.eql(item_val, operand_ty, sema.mod)) { - if (is_inline) child_block.inline_case_capture = operand; - if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); - return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); + return spa.resolveProngComptime( + &child_block, + .normal, + body, + info.capture, + .{ .scalar_capture = @intCast(u32, scalar_i) }, + &.{item}, + if (info.is_inline) operand else .none, + info.has_tag_capture, + merges, + ); } } } { var multi_i: usize = 0; + var case_val_idx: usize = scalar_cases_len; while (multi_i < multi_cases_len) : (multi_i += 1) { const items_len = sema.code.extra[extra_index]; extra_index += 1; const ranges_len = sema.code.extra[extra_index]; extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); - const is_inline = sema.code.extra[extra_index] >> 31 != 0; - extra_index += 1; - const items = sema.code.refSlice(extra_index, items_len); - extra_index += items_len; - const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len]; + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); + extra_index += 1 + items_len; + const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..info.body_len]; + + const items = case_vals.items[case_val_idx..][0..items_len]; + case_val_idx += items_len; - for (items) |item_ref| { - const item = try sema.resolveInst(item_ref); + for (items) |item| { // Validation above ensured these will succeed. const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable; if (operand_val.eql(item_val, operand_ty, sema.mod)) { - if (is_inline) child_block.inline_case_capture = operand; - if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); - return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); + return spa.resolveProngComptime( + &child_block, + .normal, + body, + info.capture, + .{ .multi_capture = @intCast(u32, multi_i) }, + items, + if (info.is_inline) operand else .none, + info.has_tag_capture, + merges, + ); } } var range_i: usize = 0; while (range_i < ranges_len) : (range_i += 1) { - const item_first = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); - extra_index += 1; - const item_last = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); - extra_index += 1; + const range_items = case_vals.items[case_val_idx..][0..2]; + extra_index += 2; + case_val_idx += 2; // Validation above ensured these will succeed. - const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first, "") catch unreachable; - const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last, "") catch unreachable; - if ((try sema.compareAll(operand_val, .gte, first_tv.val, operand_ty)) and - (try sema.compareAll(operand_val, .lte, last_tv.val, operand_ty))) + const first_val = sema.resolveConstValue(&child_block, .unneeded, range_items[0], "") catch unreachable; + const last_val = sema.resolveConstValue(&child_block, .unneeded, range_items[1], "") catch unreachable; + if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and + (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty))) { - if (is_inline) child_block.inline_case_capture = operand; if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); - return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); + return spa.resolveProngComptime( + &child_block, + .normal, + body, + info.capture, + .{ .multi_capture = @intCast(u32, multi_i) }, + undefined, // case_vals may be undefined for ranges + if (info.is_inline) operand else .none, + info.has_tag_capture, + merges, + ); } } - extra_index += body_len; + extra_index += info.body_len; } } if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, special.body, operand); - if (special.is_inline) child_block.inline_case_capture = operand; if (empty_enum) { return Air.Inst.Ref.void_value; } - return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges); + + return spa.resolveProngComptime( + &child_block, + .special, + special.body, + special.capture, + .special_capture, + undefined, // case_vals may be undefined for special prongs + if (special.is_inline) operand else .none, + special.has_tag_capture, + merges, + ); } if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) { @@ -10967,14 +11484,25 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) { return Air.Inst.Ref.unreachable_value; } - if (sema.mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag() == .Enum and - (!operand_ty.isNonexhaustiveEnum() or union_originally)) + if (mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and + (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) { try sema.zirDbgStmt(block, cond_dbg_node_index); const ok = try block.addUnOp(.is_named_enum_value, operand); try sema.addSafetyCheck(block, ok, .corrupt_switch); } - return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges); + + return spa.resolveProngComptime( + &child_block, + .special, + special.body, + special.capture, + .special_capture, + undefined, // case_vals may be undefined for special prongs + .none, + false, + merges, + ); } if (child_block.is_comptime) { @@ -11000,35 +11528,40 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var scalar_i: usize = 0; while (scalar_i < scalar_cases_len) : (scalar_i += 1) { - const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); - const is_inline = sema.code.extra[extra_index] >> 31 != 0; + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); extra_index += 1; - const body = sema.code.extra[extra_index..][0..body_len]; - extra_index += body_len; + const body = sema.code.extra[extra_index..][0..info.body_len]; + extra_index += info.body_len; - var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope); + var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope); defer wip_captures.deinit(); case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = wip_captures.scope; - case_block.inline_case_capture = .none; - const item = try sema.resolveInst(item_ref); - if (is_inline) case_block.inline_case_capture = item; + const item = case_vals.items[scalar_i]; // `item` is already guaranteed to be constant known. const analyze_body = if (union_originally) blk: { - const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable; - const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod); - break :blk field_ty.zigTypeTag() != .NoReturn; + const item_val = sema.resolveConstLazyValue(block, .unneeded, item, "") catch unreachable; + const field_ty = maybe_union_ty.unionFieldType(item_val, mod); + break :blk field_ty.zigTypeTag(mod) != .NoReturn; } else true; if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) { // nothing to do here } else if (analyze_body) { - try sema.analyzeBodyRuntimeBreak(&case_block, body); + try spa.analyzeProngRuntime( + &case_block, + .normal, + body, + info.capture, + .{ .scalar_capture = @intCast(u32, scalar_i) }, + &.{item}, + if (info.is_inline) item else .none, + info.has_tag_capture, + ); } else { _ = try case_block.addNoOp(.unreach); } @@ -11050,48 +11583,50 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError defer gpa.free(prev_then_body); var cases_len = scalar_cases_len; + var case_val_idx: usize = scalar_cases_len; var multi_i: u32 = 0; while (multi_i < multi_cases_len) : (multi_i += 1) { const items_len = sema.code.extra[extra_index]; extra_index += 1; const ranges_len = sema.code.extra[extra_index]; extra_index += 1; - const body_len = @truncate(u31, sema.code.extra[extra_index]); - const is_inline = sema.code.extra[extra_index] >> 31 != 0; - extra_index += 1; - const items = sema.code.refSlice(extra_index, items_len); - extra_index += items_len; + const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]); + extra_index += 1 + items_len; + + const items = case_vals.items[case_val_idx..][0..items_len]; + case_val_idx += items_len; case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = child_block.wip_capture_scope; - case_block.inline_case_capture = .none; // Generate all possible cases as scalar prongs. - if (is_inline) { + if (info.is_inline) { const body_start = extra_index + 2 * ranges_len; - const body = sema.code.extra[body_start..][0..body_len]; + const body = sema.code.extra[body_start..][0..info.body_len]; var emit_bb = false; var range_i: u32 = 0; while (range_i < ranges_len) : (range_i += 1) { - const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); - extra_index += 1; - const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); - extra_index += 1; + const range_items = case_vals.items[case_val_idx..][0..2]; + extra_index += 2; + case_val_idx += 2; + + const item_first_ref = range_items[0]; + const item_last_ref = range_items[1]; - const item_first_ref = try sema.resolveInst(first_ref); var item = sema.resolveConstValue(block, .unneeded, item_first_ref, undefined) catch unreachable; - const item_last_ref = try sema.resolveInst(last_ref); const item_last = sema.resolveConstValue(block, .unneeded, item_last_ref, undefined) catch unreachable; - while (item.compareAll(.lte, item_last, operand_ty, sema.mod)) : ({ + while (item.compareScalar(.lte, item_last, operand_ty, mod)) : ({ // Previous validation has resolved any possible lazy values. - item = try sema.intAddScalar(item, Value.one); + item = sema.intAddScalar(item, try mod.intValue(operand_ty, 1), operand_ty) catch |err| switch (err) { + error.Overflow => unreachable, + else => |e| return e, + }; }) { cases_len += 1; const item_ref = try sema.addConstant(operand_ty, item); - case_block.inline_case_capture = item_ref; case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = child_block.wip_capture_scope; @@ -11099,44 +11634,50 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) { error.NeededSourceLocation => { const case_src = Module.SwitchProngSrc{ .range = .{ .prong = multi_i, .item = range_i } }; - const decl = sema.mod.declPtr(case_block.src_decl); - try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none)); + const decl = mod.declPtr(case_block.src_decl); + try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none)); unreachable; }, else => return err, }; emit_bb = true; - try sema.analyzeBodyRuntimeBreak(&case_block, body); + try spa.analyzeProngRuntime( + &case_block, + .normal, + body, + info.capture, + .{ .multi_capture = multi_i }, + undefined, // case_vals may be undefined for ranges + item_ref, + info.has_tag_capture, + ); try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); cases_extra.appendAssumeCapacity(1); // items_len cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len)); - cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture)); + cases_extra.appendAssumeCapacity(@enumToInt(item_ref)); cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); } } - for (items, 0..) |item_ref, item_i| { + for (items, 0..) |item, item_i| { cases_len += 1; - const item = try sema.resolveInst(item_ref); - case_block.inline_case_capture = item; - case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = child_block.wip_capture_scope; const analyze_body = if (union_originally) blk: { const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; - const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod); - break :blk field_ty.zigTypeTag() != .NoReturn; + const field_ty = maybe_union_ty.unionFieldType(item_val, mod); + break :blk field_ty.zigTypeTag(mod) != .NoReturn; } else true; if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) { error.NeededSourceLocation => { const case_src = Module.SwitchProngSrc{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }; - const decl = sema.mod.declPtr(case_block.src_decl); - try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none)); + const decl = mod.declPtr(case_block.src_decl); + try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none)); unreachable; }, else => return err, @@ -11144,7 +11685,16 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError emit_bb = true; if (analyze_body) { - try sema.analyzeBodyRuntimeBreak(&case_block, body); + try spa.analyzeProngRuntime( + &case_block, + .normal, + body, + info.capture, + .{ .multi_capture = multi_i }, + &.{item}, + item, + info.has_tag_capture, + ); } else { _ = try case_block.addNoOp(.unreach); } @@ -11152,11 +11702,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); cases_extra.appendAssumeCapacity(1); // items_len cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len)); - cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture)); + cases_extra.appendAssumeCapacity(@enumToInt(item)); cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); } - extra_index += body_len; + extra_index += info.body_len; continue; } @@ -11169,21 +11719,29 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError cases_len += 1; const analyze_body = if (union_originally) - for (items) |item_ref| { - const item = try sema.resolveInst(item_ref); + for (items) |item| { const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable; - const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod); - if (field_ty.zigTypeTag() != .NoReturn) break true; + const field_ty = maybe_union_ty.unionFieldType(item_val, mod); + if (field_ty.zigTypeTag(mod) != .NoReturn) break true; } else false else true; - const body = sema.code.extra[extra_index..][0..body_len]; - extra_index += body_len; + const body = sema.code.extra[extra_index..][0..info.body_len]; + extra_index += info.body_len; if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) { // nothing to do here } else if (analyze_body) { - try sema.analyzeBodyRuntimeBreak(&case_block, body); + try spa.analyzeProngRuntime( + &case_block, + .normal, + body, + info.capture, + .{ .multi_capture = multi_i }, + items, + .none, + false, + ); } else { _ = try case_block.addNoOp(.unreach); } @@ -11194,15 +11752,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError cases_extra.appendAssumeCapacity(@intCast(u32, items.len)); cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len)); - for (items) |item_ref| { - const item = try sema.resolveInst(item_ref); + for (items) |item| { cases_extra.appendAssumeCapacity(@enumToInt(item)); } cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); } else { - for (items) |item_ref| { - const item = try sema.resolveInst(item_ref); + for (items) |item| { const cmp_ok = try case_block.addBinOp(if (case_block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, item); if (any_ok != .none) { any_ok = try case_block.addBinOp(.bool_or, any_ok, cmp_ok); @@ -11213,13 +11769,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var range_i: usize = 0; while (range_i < ranges_len) : (range_i += 1) { - const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); - extra_index += 1; - const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); - extra_index += 1; + const range_items = case_vals.items[case_val_idx..][0..2]; + extra_index += 2; + case_val_idx += 2; - const item_first = try sema.resolveInst(first_ref); - const item_last = try sema.resolveInst(last_ref); + const item_first = range_items[0]; + const item_last = range_items[1]; // operand >= first and operand <= last const range_first_ok = try case_block.addBinOp( @@ -11253,18 +11808,27 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var cond_body = try case_block.instructions.toOwnedSlice(gpa); defer gpa.free(cond_body); - var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope); + var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope); defer wip_captures.deinit(); case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = wip_captures.scope; - const body = sema.code.extra[extra_index..][0..body_len]; - extra_index += body_len; + const body = sema.code.extra[extra_index..][0..info.body_len]; + extra_index += info.body_len; if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) { // nothing to do here } else { - try sema.analyzeBodyRuntimeBreak(&case_block, body); + try spa.analyzeProngRuntime( + &case_block, + .normal, + body, + info.capture, + .{ .multi_capture = multi_i }, + items, + .none, + false, + ); } try wip_captures.finalize(); @@ -11296,34 +11860,42 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var final_else_body: []const Air.Inst.Index = &.{}; if (special.body.len != 0 or !is_first or case_block.wantSafety()) { var emit_bb = false; - if (special.is_inline) switch (operand_ty.zigTypeTag()) { + if (special.is_inline) switch (operand_ty.zigTypeTag(mod)) { .Enum => { - if (operand_ty.isNonexhaustiveEnum() and !union_originally) { + if (operand_ty.isNonexhaustiveEnum(mod) and !union_originally) { return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{ - operand_ty.fmt(sema.mod), + operand_ty.fmt(mod), }); } for (seen_enum_fields, 0..) |f, i| { if (f != null) continue; cases_len += 1; - const item_val = try Value.Tag.enum_field_index.create(sema.arena, @intCast(u32, i)); + const item_val = try mod.enumValueFieldIndex(operand_ty, @intCast(u32, i)); const item_ref = try sema.addConstant(operand_ty, item_val); - case_block.inline_case_capture = item_ref; case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = child_block.wip_capture_scope; const analyze_body = if (union_originally) blk: { - const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod); - break :blk field_ty.zigTypeTag() != .NoReturn; + const field_ty = maybe_union_ty.unionFieldType(item_val, mod); + break :blk field_ty.zigTypeTag(mod) != .NoReturn; } else true; if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); emit_bb = true; if (analyze_body) { - try sema.analyzeBodyRuntimeBreak(&case_block, special.body); + try spa.analyzeProngRuntime( + &case_block, + .special, + special.body, + special.capture, + .special_capture, + &.{item_ref}, + item_ref, + special.has_tag_capture, + ); } else { _ = try case_block.addNoOp(.unreach); } @@ -11331,23 +11903,26 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); cases_extra.appendAssumeCapacity(1); // items_len cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len)); - cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture)); + cases_extra.appendAssumeCapacity(@enumToInt(item_ref)); cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); } }, .ErrorSet => { - if (operand_ty.isAnyError()) { + if (operand_ty.isAnyError(mod)) { return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{ - operand_ty.fmt(sema.mod), + operand_ty.fmt(mod), }); } - for (operand_ty.errorSetNames()) |error_name| { + for (0..operand_ty.errorSetNames(mod).len) |i| { + const error_name = operand_ty.errorSetNames(mod)[i]; if (seen_errors.contains(error_name)) continue; cases_len += 1; - const item_val = try Value.Tag.@"error".create(sema.arena, .{ .name = error_name }); - const item_ref = try sema.addConstant(operand_ty, item_val); - case_block.inline_case_capture = item_ref; + const item_val = try mod.intern(.{ .err = .{ + .ty = operand_ty.toIntern(), + .name = error_name, + } }); + const item_ref = try sema.addConstant(operand_ty, item_val.toValue()); case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = child_block.wip_capture_scope; @@ -11355,12 +11930,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); emit_bb = true; - try sema.analyzeBodyRuntimeBreak(&case_block, special.body); + try spa.analyzeProngRuntime( + &case_block, + .special, + special.body, + special.capture, + .special_capture, + &.{item_ref}, + item_ref, + special.has_tag_capture, + ); try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); cases_extra.appendAssumeCapacity(1); // items_len cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len)); - cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture)); + cases_extra.appendAssumeCapacity(@enumToInt(item_ref)); cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); } }, @@ -11369,8 +11953,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError while (try it.next()) |cur| { cases_len += 1; - const item_ref = try sema.addConstant(operand_ty, cur); - case_block.inline_case_capture = item_ref; + const item_ref = try sema.addConstant(operand_ty, cur.toValue()); case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = child_block.wip_capture_scope; @@ -11378,19 +11961,27 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); emit_bb = true; - try sema.analyzeBodyRuntimeBreak(&case_block, special.body); + try spa.analyzeProngRuntime( + &case_block, + .special, + special.body, + special.capture, + .special_capture, + &.{item_ref}, + item_ref, + special.has_tag_capture, + ); try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); cases_extra.appendAssumeCapacity(1); // items_len cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len)); - cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture)); + cases_extra.appendAssumeCapacity(@enumToInt(item_ref)); cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); } }, .Bool => { if (true_count == 0) { cases_len += 1; - case_block.inline_case_capture = Air.Inst.Ref.bool_true; case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = child_block.wip_capture_scope; @@ -11398,17 +11989,25 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); emit_bb = true; - try sema.analyzeBodyRuntimeBreak(&case_block, special.body); + try spa.analyzeProngRuntime( + &case_block, + .special, + special.body, + special.capture, + .special_capture, + &.{Air.Inst.Ref.bool_true}, + Air.Inst.Ref.bool_true, + special.has_tag_capture, + ); try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); cases_extra.appendAssumeCapacity(1); // items_len cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len)); - cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture)); + cases_extra.appendAssumeCapacity(@enumToInt(Air.Inst.Ref.bool_true)); cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); } if (false_count == 0) { cases_len += 1; - case_block.inline_case_capture = Air.Inst.Ref.bool_false; case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = child_block.wip_capture_scope; @@ -11416,29 +12015,37 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); emit_bb = true; - try sema.analyzeBodyRuntimeBreak(&case_block, special.body); + try spa.analyzeProngRuntime( + &case_block, + .special, + special.body, + special.capture, + .special_capture, + &.{Air.Inst.Ref.bool_false}, + Air.Inst.Ref.bool_false, + special.has_tag_capture, + ); try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); cases_extra.appendAssumeCapacity(1); // items_len cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len)); - cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture)); + cases_extra.appendAssumeCapacity(@enumToInt(Air.Inst.Ref.bool_false)); cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); } }, else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{ - operand_ty.fmt(sema.mod), + operand_ty.fmt(mod), }), }; - var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope); + var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope); defer wip_captures.deinit(); case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = wip_captures.scope; - case_block.inline_case_capture = .none; - if (sema.mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and - operand_ty.zigTypeTag() == .Enum and (!operand_ty.isNonexhaustiveEnum() or union_originally)) + if (mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and + operand_ty.zigTypeTag(mod) == .Enum and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) { try sema.zirDbgStmt(&case_block, cond_dbg_node_index); const ok = try case_block.addUnOp(.is_named_enum_value, operand); @@ -11448,9 +12055,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const analyze_body = if (union_originally and !special.is_inline) for (seen_enum_fields, 0..) |seen_field, index| { if (seen_field != null) continue; - const union_obj = maybe_union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(maybe_union_ty).?; const field_ty = union_obj.fields.values()[index].ty; - if (field_ty.zigTypeTag() != .NoReturn) break true; + if (field_ty.zigTypeTag(mod) != .NoReturn) break true; } else false else true; @@ -11459,7 +12066,16 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError { // nothing to do here } else if (special.body.len != 0 and analyze_body and !special.is_inline) { - try sema.analyzeBodyRuntimeBreak(&case_block, special.body); + try spa.analyzeProngRuntime( + &case_block, + .special, + special.body, + special.capture, + .special_capture, + undefined, // case_vals may be undefined for special prongs + .none, + false, + ); } else { // We still need a terminator in this block, but we have proven // that it is unreachable. @@ -11507,74 +12123,118 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } const RangeSetUnhandledIterator = struct { - sema: *Sema, - ty: Type, - cur: Value, - max: Value, + mod: *Module, + cur: ?InternPool.Index, + max: InternPool.Index, + range_i: usize, ranges: []const RangeSet.Range, - range_i: usize = 0, - first: bool = true, + limbs: []math.big.Limb, - fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator { - const target = sema.mod.getTarget(); - const min = try ty.minInt(sema.arena, target); - const max = try ty.maxInt(sema.arena, target); + const preallocated_limbs = math.big.int.calcTwosCompLimbCount(128); - return RangeSetUnhandledIterator{ - .sema = sema, - .ty = ty, - .cur = min, - .max = max, + fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator { + const mod = sema.mod; + const int_type = mod.intern_pool.indexToKey(ty.toIntern()).int_type; + const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits); + return .{ + .mod = mod, + .cur = (try ty.minInt(mod, ty)).toIntern(), + .max = (try ty.maxInt(mod, ty)).toIntern(), + .range_i = 0, .ranges = range_set.ranges.items, + .limbs = if (needed_limbs > preallocated_limbs) + try sema.arena.alloc(math.big.Limb, needed_limbs) + else + &.{}, }; } - fn next(it: *RangeSetUnhandledIterator) !?Value { - while (it.range_i < it.ranges.len) : (it.range_i += 1) { - if (!it.first) { - it.cur = try it.sema.intAdd(it.cur, Value.one, it.ty); - } - it.first = false; - if (it.cur.compareAll(.lt, it.ranges[it.range_i].first, it.ty, it.sema.mod)) { - return it.cur; - } - it.cur = it.ranges[it.range_i].last; - } - if (!it.first) { - it.cur = try it.sema.intAdd(it.cur, Value.one, it.ty); + fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index { + if (val == it.max) return null; + const int = it.mod.intern_pool.indexToKey(val).int; + + switch (int.storage) { + inline .u64, .i64 => |val_int| { + const next_int = @addWithOverflow(val_int, 1); + if (next_int[1] == 0) + return (try it.mod.intValue(int.ty.toType(), next_int[0])).toIntern(); + }, + .big_int => {}, + .lazy_align, .lazy_size => unreachable, } - it.first = false; - if (it.cur.compareAll(.lte, it.max, it.ty, it.sema.mod)) { - return it.cur; + + var val_space: InternPool.Key.Int.Storage.BigIntSpace = undefined; + const val_bigint = int.storage.toBigInt(&val_space); + + var result_limbs: [preallocated_limbs]math.big.Limb = undefined; + var result_bigint = math.big.int.Mutable.init( + if (it.limbs.len > 0) it.limbs else &result_limbs, + 0, + ); + + result_bigint.addScalar(val_bigint, 1); + return (try it.mod.intValue_big(int.ty.toType(), result_bigint.toConst())).toIntern(); + } + + fn next(it: *RangeSetUnhandledIterator) !?InternPool.Index { + var cur = it.cur orelse return null; + while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) { + defer it.range_i += 1; + cur = (try it.addOne(it.ranges[it.range_i].last)) orelse { + it.cur = null; + return null; + }; } - return null; + it.cur = try it.addOne(cur); + return cur; } }; +const ResolvedSwitchItem = struct { + ref: Air.Inst.Ref, + val: InternPool.Index, +}; fn resolveSwitchItemVal( sema: *Sema, block: *Block, item_ref: Zir.Inst.Ref, + /// Coerce `item_ref` to this type. + coerce_ty: Type, switch_node_offset: i32, switch_prong_src: Module.SwitchProngSrc, range_expand: Module.SwitchProngSrc.RangeExpand, -) CompileError!TypedValue { - const item = try sema.resolveInst(item_ref); - const item_ty = sema.typeOf(item); +) CompileError!ResolvedSwitchItem { + const mod = sema.mod; + const uncoerced_item = try sema.resolveInst(item_ref); + // Constructing a LazySrcLoc is costly because we only have the switch AST node. // Only if we know for sure we need to report a compile error do we resolve the // full source locations. - if (sema.resolveConstValue(block, .unneeded, item, "")) |val| { - try sema.resolveLazyValue(val); - return TypedValue{ .ty = item_ty, .val = val }; - } else |err| switch (err) { + + const item = sema.coerce(block, coerce_ty, uncoerced_item, .unneeded) catch |err| switch (err) { + error.NeededSourceLocation => { + const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand); + _ = try sema.coerce(block, coerce_ty, uncoerced_item, src); + unreachable; + }, + else => |e| return e, + }; + + const maybe_lazy = sema.resolveConstValue(block, .unneeded, item, "") catch |err| switch (err) { error.NeededSourceLocation => { - const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_node_offset, range_expand); + const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand); _ = try sema.resolveConstValue(block, src, item, "switch prong values must be comptime-known"); unreachable; }, else => |e| return e, - } + }; + + const val = try sema.resolveLazyValue(maybe_lazy); + const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: { + break :blk try sema.addConstant(coerce_ty, val); + } else item; + + return .{ .ref = new_item, .val = val.toIntern() }; } fn validateSwitchRange( @@ -11586,18 +12246,20 @@ fn validateSwitchRange( operand_ty: Type, src_node_offset: i32, switch_prong_src: Module.SwitchProngSrc, -) CompileError!void { - const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val; - const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val; - if (first_val.compareAll(.gt, last_val, operand_ty, sema.mod)) { - const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), src_node_offset, .first); +) CompileError![2]Air.Inst.Ref { + const mod = sema.mod; + const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, src_node_offset, switch_prong_src, .first); + const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, src_node_offset, switch_prong_src, .last); + if (try first.val.toValue().compareAll(.gt, last.val.toValue(), operand_ty, mod)) { + const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), src_node_offset, .first); return sema.fail(block, src, "range start value is greater than the end value", .{}); } - const maybe_prev_src = try range_set.add(first_val, last_val, operand_ty, switch_prong_src); - return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); + const maybe_prev_src = try range_set.add(first.val, last.val, switch_prong_src); + try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); + return .{ first.ref, last.ref }; } -fn validateSwitchItem( +fn validateSwitchItemInt( sema: *Sema, block: *Block, range_set: *RangeSet, @@ -11605,10 +12267,11 @@ fn validateSwitchItem( operand_ty: Type, src_node_offset: i32, switch_prong_src: Module.SwitchProngSrc, -) CompileError!void { - const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val; - const maybe_prev_src = try range_set.add(item_val, item_val, operand_ty, switch_prong_src); - return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); +) CompileError!Air.Inst.Ref { + const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); + const maybe_prev_src = try range_set.add(item.val, item.val, switch_prong_src); + try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); + return item.ref; } fn validateSwitchItemEnum( @@ -11617,17 +12280,22 @@ fn validateSwitchItemEnum( seen_fields: []?Module.SwitchProngSrc, range_set: *RangeSet, item_ref: Zir.Inst.Ref, + operand_ty: Type, src_node_offset: i32, switch_prong_src: Module.SwitchProngSrc, -) CompileError!void { - const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); - const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val, sema.mod) orelse { - const maybe_prev_src = try range_set.add(item_tv.val, item_tv.val, item_tv.ty, switch_prong_src); - return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); +) CompileError!Air.Inst.Ref { + const ip = &sema.mod.intern_pool; + const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); + const int = ip.indexToKey(item.val).enum_tag.int; + const field_index = ip.indexToKey(ip.typeOf(item.val)).enum_type.tagValueIndex(ip, int) orelse { + const maybe_prev_src = try range_set.add(int, int, switch_prong_src); + try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); + return item.ref; }; const maybe_prev_src = seen_fields[field_index]; seen_fields[field_index] = switch_prong_src; - return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); + try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); + return item.ref; } fn validateSwitchItemError( @@ -11635,17 +12303,19 @@ fn validateSwitchItemError( block: *Block, seen_errors: *SwitchErrorSet, item_ref: Zir.Inst.Ref, + operand_ty: Type, src_node_offset: i32, switch_prong_src: Module.SwitchProngSrc, -) CompileError!void { - const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); - // TODO: Do i need to typecheck here? - const error_name = item_tv.val.castTag(.@"error").?.data.name; +) CompileError!Air.Inst.Ref { + const ip = &sema.mod.intern_pool; + const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); + const error_name = ip.indexToKey(item.val).err.name; const maybe_prev_src = if (try seen_errors.fetchPut(error_name, switch_prong_src)) |prev| prev.value else null; - return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); + try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); + return item.ref; } fn validateSwitchDupe( @@ -11656,10 +12326,10 @@ fn validateSwitchDupe( src_node_offset: i32, ) CompileError!void { const prev_prong_src = maybe_prev_src orelse return; - const gpa = sema.gpa; - const block_src_decl = sema.mod.declPtr(block.src_decl); - const src = switch_prong_src.resolve(gpa, block_src_decl, src_node_offset, .none); - const prev_src = prev_prong_src.resolve(gpa, block_src_decl, src_node_offset, .none); + const mod = sema.mod; + const block_src_decl = mod.declPtr(block.src_decl); + const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none); + const prev_src = prev_prong_src.resolve(mod, block_src_decl, src_node_offset, .none); const msg = msg: { const msg = try sema.errMsg( block, @@ -11688,33 +12358,37 @@ fn validateSwitchItemBool( item_ref: Zir.Inst.Ref, src_node_offset: i32, switch_prong_src: Module.SwitchProngSrc, -) CompileError!void { - const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val; - if (item_val.toBool()) { +) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const item = try sema.resolveSwitchItemVal(block, item_ref, Type.bool, src_node_offset, switch_prong_src, .none); + if (item.val.toValue().toBool()) { true_count.* += 1; } else { false_count.* += 1; } - if (true_count.* + false_count.* > 2) { + if (true_count.* > 1 or false_count.* > 1) { const block_src_decl = sema.mod.declPtr(block.src_decl); - const src = switch_prong_src.resolve(sema.gpa, block_src_decl, src_node_offset, .none); + const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none); return sema.fail(block, src, "duplicate switch value", .{}); } + return item.ref; } -const ValueSrcMap = std.HashMap(Value, Module.SwitchProngSrc, Value.HashContext, std.hash_map.default_max_load_percentage); +const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, Module.SwitchProngSrc); fn validateSwitchItemSparse( sema: *Sema, block: *Block, seen_values: *ValueSrcMap, item_ref: Zir.Inst.Ref, + operand_ty: Type, src_node_offset: i32, switch_prong_src: Module.SwitchProngSrc, -) CompileError!void { - const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val; - const kv = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return; - return sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset); +) CompileError!Air.Inst.Ref { + const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); + const kv = (try seen_values.fetchPut(sema.gpa, item.val, switch_prong_src)) orelse return item.ref; + try sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset); + unreachable; } fn validateSwitchNoRange( @@ -11751,16 +12425,17 @@ fn validateSwitchNoRange( } fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !bool { - if (!sema.mod.backendSupportsFeature(.panic_unwrap_error)) return false; + const mod = sema.mod; + if (!mod.backendSupportsFeature(.panic_unwrap_error)) return false; const tags = sema.code.instructions.items(.tag); for (body) |inst| { switch (tags[inst]) { + .@"unreachable" => if (!block.wantSafety()) return false, .save_err_ret_index, .dbg_block_begin, .dbg_block_end, .dbg_stmt, - .@"unreachable", .str, .as_node, .panic, @@ -11787,10 +12462,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op .as_node => try sema.zirAsNode(block, inst), .field_val => try sema.zirFieldVal(block, inst), .@"unreachable" => { - const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable"; - const src = inst_data.src(); - - if (!sema.mod.comp.formatted_panics) { + if (!mod.comp.formatted_panics) { try sema.safetyPanic(block, .unwrap_error); return true; } @@ -11798,23 +12470,22 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op const panic_fn = try sema.getBuiltin("panicUnwrapError"); const err_return_trace = try sema.getErrorReturnTrace(block); const args: [2]Air.Inst.Ref = .{ err_return_trace, operand }; - _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null); + try sema.callBuiltin(block, panic_fn, .auto, &args); return true; }, .panic => { const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); const msg_inst = try sema.resolveInst(inst_data.operand); const panic_fn = try sema.getBuiltin("panic"); const err_return_trace = try sema.getErrorReturnTrace(block); const args: [3]Air.Inst.Ref = .{ msg_inst, err_return_trace, .null_value }; - _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null); + try sema.callBuiltin(block, panic_fn, .auto, &args); return true; }, else => unreachable, }; - if (sema.typeOf(air_inst).isNoReturn()) + if (sema.typeOf(air_inst).isNoReturn(mod)) return true; sema.inst_map.putAssumeCapacity(inst, air_inst); } @@ -11822,19 +12493,20 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op } fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void { + const mod = sema.mod; const index = Zir.refToIndex(cond) orelse return; if (sema.code.instructions.items(.tag)[index] != .is_non_err) return; const err_inst_data = sema.code.instructions.items(.data)[index].un_node; const err_operand = try sema.resolveInst(err_inst_data.operand); const operand_ty = sema.typeOf(err_operand); - if (operand_ty.zigTypeTag() == .ErrorSet) { + if (operand_ty.zigTypeTag(mod) == .ErrorSet) { try sema.maybeErrorUnwrapComptime(block, body, err_operand); return; } if (try sema.resolveDefinedValue(block, cond_src, err_operand)) |val| { - if (!operand_ty.isError()) return; - if (val.getError() == null) return; + if (!operand_ty.isError(mod)) return; + if (val.getErrorName(mod) == .none) return; try sema.maybeErrorUnwrapComptime(block, body, err_operand); } } @@ -11856,45 +12528,60 @@ fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.I const src = inst_data.src(); if (try sema.resolveDefinedValue(block, src, operand)) |val| { - if (val.getError()) |name| { - return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); + if (val.getErrorName(sema.mod).unwrap()) |name| { + return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&sema.mod.intern_pool)}); } } } fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const unresolved_ty = try sema.resolveType(block, ty_src, extra.lhs); - const field_name = try sema.resolveConstString(block, name_src, extra.rhs, "field name must be comptime-known"); + const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, "field name must be comptime-known"); const ty = try sema.resolveTypeFields(unresolved_ty); + const ip = &mod.intern_pool; const has_field = hf: { - if (ty.isSlice()) { - if (mem.eql(u8, field_name, "ptr")) break :hf true; - if (mem.eql(u8, field_name, "len")) break :hf true; - break :hf false; - } - if (ty.castTag(.anon_struct)) |pl| { - break :hf for (pl.data.names) |name| { - if (mem.eql(u8, name, field_name)) break true; - } else false; - } - if (ty.isTuple()) { - const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch break :hf false; - break :hf field_index < ty.structFieldCount(); - } - break :hf switch (ty.zigTypeTag()) { - .Struct => ty.structFields().contains(field_name), - .Union => ty.unionFields().contains(field_name), - .Enum => ty.enumFields().contains(field_name), - .Array => mem.eql(u8, field_name, "len"), - else => return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{ - ty.fmt(sema.mod), - }), - }; + switch (ip.indexToKey(ty.toIntern())) { + .ptr_type => |ptr_type| switch (ptr_type.flags.size) { + .Slice => { + if (ip.stringEqlSlice(field_name, "ptr")) break :hf true; + if (ip.stringEqlSlice(field_name, "len")) break :hf true; + break :hf false; + }, + else => {}, + }, + .anon_struct_type => |anon_struct| { + if (anon_struct.names.len != 0) { + break :hf mem.indexOfScalar(InternPool.NullTerminatedString, anon_struct.names, field_name) != null; + } else { + const field_index = field_name.toUnsigned(ip) orelse break :hf false; + break :hf field_index < ty.structFieldCount(mod); + } + }, + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse break :hf false; + assert(struct_obj.haveFieldTypes()); + break :hf struct_obj.fields.contains(field_name); + }, + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + assert(union_obj.haveFieldTypes()); + break :hf union_obj.fields.contains(field_name); + }, + .enum_type => |enum_type| { + break :hf enum_type.nameIndex(ip, field_name) != null; + }, + .array_type => break :hf ip.stringEqlSlice(field_name, "len"), + else => {}, + } + return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{ + ty.fmt(mod), + }); }; if (has_field) { return Air.Inst.Ref.bool_true; @@ -11904,20 +12591,22 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const src = inst_data.src(); const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const container_type = try sema.resolveType(block, lhs_src, extra.lhs); - const decl_name = try sema.resolveConstString(block, rhs_src, extra.rhs, "decl name must be comptime-known"); + const decl_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, "decl name must be comptime-known"); try sema.checkNamespaceType(block, lhs_src, container_type); - const namespace = container_type.getNamespace() orelse return Air.Inst.Ref.bool_false; + const namespace = container_type.getNamespaceIndex(mod).unwrap() orelse + return Air.Inst.Ref.bool_false; if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| { - const decl = sema.mod.declPtr(decl_index); - if (decl.is_pub or decl.getFileScope() == block.getFileScope()) { + const decl = mod.declPtr(decl_index); + if (decl.is_pub or decl.getFileScope(mod) == block.getFileScope(mod)) { return Air.Inst.Ref.bool_true; } } @@ -11933,12 +12622,12 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const operand_src = inst_data.src(); const operand = inst_data.get(sema.code); - const result = mod.importFile(block.getFileScope(), operand) catch |err| switch (err) { + const result = mod.importFile(block.getFileScope(mod), operand) catch |err| switch (err) { error.ImportOutsidePkgPath => { return sema.fail(block, operand_src, "import of file outside package path: '{s}'", .{operand}); }, error.PackageNotFound => { - const name = try block.getFileScope().pkg.getName(sema.gpa, mod.*); + const name = try block.getFileScope(mod).pkg.getName(sema.gpa, mod.*); defer sema.gpa.free(name); return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, name }); }, @@ -11964,7 +12653,7 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const name = try sema.resolveConstString(block, operand_src, inst_data.operand, "file path name must be comptime-known"); - const embed_file = mod.embedFile(block.getFileScope(), name) catch |err| switch (err) { + const embed_file = mod.embedFile(block.getFileScope(mod), name) catch |err| switch (err) { error.ImportOutsidePkgPath => { return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name}); }, @@ -11978,17 +12667,23 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); - const bytes_including_null = embed_file.bytes[0 .. embed_file.bytes.len + 1]; - - // TODO instead of using `Value.Tag.bytes`, create a new value tag for pointing at + // TODO instead of using `.bytes`, create a new value tag for pointing at // a `*Module.EmbedFile`. The purpose of this would be: // - If only the length is read and the bytes are not inspected by comptime code, // there can be an optimization where the codegen backend does a copy_file_range // into the final binary, and never loads the data into memory. // - When a Decl is destroyed, it can free the `*Module.EmbedFile`. + const ty = try mod.arrayType(.{ + .len = embed_file.bytes.len, + .child = .u8_type, + .sentinel = .zero_u8, + }); embed_file.owner_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), embed_file.bytes.len), - try Value.Tag.bytes.create(anon_decl.arena(), bytes_including_null), + ty, + (try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .bytes = embed_file.bytes }, + } })).toValue(), 0, // default alignment ); @@ -11996,16 +12691,15 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].str_tok; - const err_name = inst_data.get(sema.code); - - // Return the error code from the function. - const kv = try sema.mod.getErrorValue(err_name); - const result_inst = try sema.addConstant( - try Type.Tag.error_set_single.create(sema.arena, kv.key), - try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }), - ); - return result_inst; + const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); + _ = try mod.getErrorValue(name); + const error_set_type = try mod.singleErrorSetType(name); + return sema.addConstant(error_set_type, (try mod.intern(.{ .err = .{ + .ty = error_set_type.toIntern(), + .name = name, + } })).toValue()); } fn zirShl( @@ -12017,6 +12711,7 @@ fn zirShl( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); sema.src = src; @@ -12027,11 +12722,10 @@ fn zirShl( const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - const target = sema.mod.getTarget(); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); - const scalar_ty = lhs_ty.scalarType(); - const scalar_rhs_ty = rhs_ty.scalarType(); + const scalar_ty = lhs_ty.scalarType(mod); + const scalar_rhs_ty = rhs_ty.scalarType(mod); // TODO coerce rhs if air_tag is not shl_sat const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, scalar_rhs_ty); @@ -12040,62 +12734,56 @@ fn zirShl( const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(rhs); if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.addConstUndef(sema.typeOf(lhs)); } // If rhs is 0, return lhs without doing any calculations. if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { return lhs; } - if (scalar_ty.zigTypeTag() != .ComptimeInt and air_tag != .shl_sat) { - var bits_payload = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = scalar_ty.intInfo(target).bits, - }; - const bit_value = Value.initPayload(&bits_payload.base); - if (rhs_ty.zigTypeTag() == .Vector) { + if (scalar_ty.zigTypeTag(mod) != .ComptimeInt and air_tag != .shl_sat) { + const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits); + if (rhs_ty.zigTypeTag(mod) == .Vector) { var i: usize = 0; - while (i < rhs_ty.vectorLen()) : (i += 1) { - var elem_value_buf: Value.ElemValueBuffer = undefined; - const rhs_elem = rhs_val.elemValueBuffer(sema.mod, i, &elem_value_buf); - if (rhs_elem.compareHetero(.gte, bit_value, target)) { + while (i < rhs_ty.vectorLen(mod)) : (i += 1) { + const rhs_elem = try rhs_val.elemValue(mod, i); + if (rhs_elem.compareHetero(.gte, bit_value, mod)) { return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{ - rhs_elem.fmtValue(scalar_ty, sema.mod), + rhs_elem.fmtValue(scalar_ty, mod), i, - scalar_ty.fmt(sema.mod), + scalar_ty.fmt(mod), }); } } - } else if (rhs_val.compareHetero(.gte, bit_value, target)) { + } else if (rhs_val.compareHetero(.gte, bit_value, mod)) { return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{ - rhs_val.fmtValue(scalar_ty, sema.mod), - scalar_ty.fmt(sema.mod), + rhs_val.fmtValue(scalar_ty, mod), + scalar_ty.fmt(mod), }); } } - if (rhs_ty.zigTypeTag() == .Vector) { + if (rhs_ty.zigTypeTag(mod) == .Vector) { var i: usize = 0; - while (i < rhs_ty.vectorLen()) : (i += 1) { - var elem_value_buf: Value.ElemValueBuffer = undefined; - const rhs_elem = rhs_val.elemValueBuffer(sema.mod, i, &elem_value_buf); - if (rhs_elem.compareHetero(.lt, Value.zero, target)) { + while (i < rhs_ty.vectorLen(mod)) : (i += 1) { + const rhs_elem = try rhs_val.elemValue(mod, i); + if (rhs_elem.compareHetero(.lt, try mod.intValue(scalar_rhs_ty, 0), mod)) { return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{ - rhs_elem.fmtValue(scalar_ty, sema.mod), + rhs_elem.fmtValue(scalar_ty, mod), i, }); } } - } else if (rhs_val.compareHetero(.lt, Value.zero, target)) { + } else if (rhs_val.compareHetero(.lt, try mod.intValue(rhs_ty, 0), mod)) { return sema.fail(block, rhs_src, "shift by negative amount '{}'", .{ - rhs_val.fmtValue(scalar_ty, sema.mod), + rhs_val.fmtValue(scalar_ty, mod), }); } } const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { - if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty); + if (lhs_val.isUndef(mod)) return sema.addConstUndef(lhs_ty); const rhs_val = maybe_rhs_val orelse { - if (scalar_ty.zigTypeTag() == .ComptimeInt) { + if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) { return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); } break :rs rhs_src; @@ -12103,25 +12791,25 @@ fn zirShl( const val = switch (air_tag) { .shl_exact => val: { - const shifted = try lhs_val.shlWithOverflow(rhs_val, lhs_ty, sema.arena, sema.mod); - if (scalar_ty.zigTypeTag() == .ComptimeInt) { + const shifted = try lhs_val.shlWithOverflow(rhs_val, lhs_ty, sema.arena, mod); + if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) { break :val shifted.wrapped_result; } - if (shifted.overflow_bit.compareAllWithZero(.eq, sema.mod)) { + if (shifted.overflow_bit.compareAllWithZero(.eq, mod)) { break :val shifted.wrapped_result; } return sema.fail(block, src, "operation caused overflow", .{}); }, - .shl_sat => if (scalar_ty.zigTypeTag() == .ComptimeInt) - try lhs_val.shl(rhs_val, lhs_ty, sema.arena, sema.mod) + .shl_sat => if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) + try lhs_val.shl(rhs_val, lhs_ty, sema.arena, mod) else - try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, sema.mod), + try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, mod), - .shl => if (scalar_ty.zigTypeTag() == .ComptimeInt) - try lhs_val.shl(rhs_val, lhs_ty, sema.arena, sema.mod) + .shl => if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) + try lhs_val.shl(rhs_val, lhs_ty, sema.arena, mod) else - try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, sema.mod), + try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, mod), else => unreachable, }; @@ -12132,13 +12820,13 @@ fn zirShl( const new_rhs = if (air_tag == .shl_sat) rhs: { // Limit the RHS type for saturating shl to be an integer as small as the LHS. if (rhs_is_comptime_int or - scalar_rhs_ty.intInfo(target).bits > scalar_ty.intInfo(target).bits) + scalar_rhs_ty.intInfo(mod).bits > scalar_ty.intInfo(mod).bits) { const max_int = try sema.addConstant( lhs_ty, - try lhs_ty.maxInt(sema.arena, target), + try lhs_ty.maxInt(mod, lhs_ty), ); - const rhs_limited = try sema.analyzeMinMax(block, rhs_src, rhs, max_int, .min, rhs_src, rhs_src); + const rhs_limited = try sema.analyzeMinMax(block, rhs_src, .min, &.{ rhs, max_int }, &.{ rhs_src, rhs_src }); break :rhs try sema.intCast(block, src, lhs_ty, rhs_src, rhs_limited, rhs_src, false); } else { break :rhs rhs; @@ -12147,12 +12835,11 @@ fn zirShl( try sema.requireRuntimeBlock(block, src, runtime_src); if (block.wantSafety()) { - const bit_count = scalar_ty.intInfo(target).bits; + const bit_count = scalar_ty.intInfo(mod).bits; if (!std.math.isPowerOfTwo(bit_count)) { - const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count); - - const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { - const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val)); + const bit_count_val = try mod.intValue(scalar_rhs_ty, bit_count); + const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: { + const bit_count_inst = try sema.addConstant(rhs_ty, try sema.splat(rhs_ty, bit_count_val)); const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); break :ok try block.addInst(.{ .tag = .reduce, @@ -12181,7 +12868,7 @@ fn zirShl( } }, }); const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); - const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector) + const any_ov_bit = if (lhs_ty.zigTypeTag(mod) == .Vector) try block.addInst(.{ .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, .data = .{ .reduce = .{ @@ -12191,7 +12878,7 @@ fn zirShl( }) else ov_bit; - const zero_ov = try sema.addConstant(Type.u1, Value.zero); + const zero_ov = try sema.addConstant(Type.u1, try mod.intValue(Type.u1, 0)); const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov); try sema.addSafetyCheck(block, no_ov, .shl_overflow); @@ -12210,6 +12897,7 @@ fn zirShr( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); sema.src = src; @@ -12221,94 +12909,87 @@ fn zirShr( const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); - const target = sema.mod.getTarget(); - const scalar_ty = lhs_ty.scalarType(); + const scalar_ty = lhs_ty.scalarType(mod); const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(lhs); const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(rhs); const runtime_src = if (maybe_rhs_val) |rhs_val| rs: { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.addConstUndef(lhs_ty); } // If rhs is 0, return lhs without doing any calculations. if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { return lhs; } - if (scalar_ty.zigTypeTag() != .ComptimeInt) { - var bits_payload = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = scalar_ty.intInfo(target).bits, - }; - const bit_value = Value.initPayload(&bits_payload.base); - if (rhs_ty.zigTypeTag() == .Vector) { + if (scalar_ty.zigTypeTag(mod) != .ComptimeInt) { + const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits); + if (rhs_ty.zigTypeTag(mod) == .Vector) { var i: usize = 0; - while (i < rhs_ty.vectorLen()) : (i += 1) { - var elem_value_buf: Value.ElemValueBuffer = undefined; - const rhs_elem = rhs_val.elemValueBuffer(sema.mod, i, &elem_value_buf); - if (rhs_elem.compareHetero(.gte, bit_value, target)) { + while (i < rhs_ty.vectorLen(mod)) : (i += 1) { + const rhs_elem = try rhs_val.elemValue(mod, i); + if (rhs_elem.compareHetero(.gte, bit_value, mod)) { return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{ - rhs_elem.fmtValue(scalar_ty, sema.mod), + rhs_elem.fmtValue(scalar_ty, mod), i, - scalar_ty.fmt(sema.mod), + scalar_ty.fmt(mod), }); } } - } else if (rhs_val.compareHetero(.gte, bit_value, target)) { + } else if (rhs_val.compareHetero(.gte, bit_value, mod)) { return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{ - rhs_val.fmtValue(scalar_ty, sema.mod), - scalar_ty.fmt(sema.mod), + rhs_val.fmtValue(scalar_ty, mod), + scalar_ty.fmt(mod), }); } } - if (rhs_ty.zigTypeTag() == .Vector) { + if (rhs_ty.zigTypeTag(mod) == .Vector) { var i: usize = 0; - while (i < rhs_ty.vectorLen()) : (i += 1) { - var elem_value_buf: Value.ElemValueBuffer = undefined; - const rhs_elem = rhs_val.elemValueBuffer(sema.mod, i, &elem_value_buf); - if (rhs_elem.compareHetero(.lt, Value.zero, target)) { + while (i < rhs_ty.vectorLen(mod)) : (i += 1) { + const rhs_elem = try rhs_val.elemValue(mod, i); + if (rhs_elem.compareHetero(.lt, try mod.intValue(rhs_ty.childType(mod), 0), mod)) { return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{ - rhs_elem.fmtValue(scalar_ty, sema.mod), + rhs_elem.fmtValue(scalar_ty, mod), i, }); } } - } else if (rhs_val.compareHetero(.lt, Value.zero, target)) { + } else if (rhs_val.compareHetero(.lt, try mod.intValue(rhs_ty, 0), mod)) { return sema.fail(block, rhs_src, "shift by negative amount '{}'", .{ - rhs_val.fmtValue(scalar_ty, sema.mod), + rhs_val.fmtValue(scalar_ty, mod), }); } if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { return sema.addConstUndef(lhs_ty); } if (air_tag == .shr_exact) { // Detect if any ones would be shifted out. - const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, sema.mod); + const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, mod); if (!(try truncated.compareAllWithZeroAdvanced(.eq, sema))) { return sema.fail(block, src, "exact shift shifted out 1 bits", .{}); } } - const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, sema.mod); + const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, mod); return sema.addConstant(lhs_ty, val); } else { break :rs lhs_src; } } else rhs_src; - if (maybe_rhs_val == null and scalar_ty.zigTypeTag() == .ComptimeInt) { + if (maybe_rhs_val == null and scalar_ty.zigTypeTag(mod) == .ComptimeInt) { return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); } try sema.requireRuntimeBlock(block, src, runtime_src); const result = try block.addBinOp(air_tag, lhs, rhs); if (block.wantSafety()) { - const bit_count = scalar_ty.intInfo(target).bits; + const bit_count = scalar_ty.intInfo(mod).bits; if (!std.math.isPowerOfTwo(bit_count)) { - const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count); + const bit_count_val = try mod.intValue(rhs_ty.scalarType(mod), bit_count); - const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { - const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val)); + const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: { + const bit_count_inst = try sema.addConstant(rhs_ty, try sema.splat(rhs_ty, bit_count_val)); const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); break :ok try block.addInst(.{ .tag = .reduce, @@ -12327,7 +13008,7 @@ fn zirShr( if (air_tag == .shr_exact) { const back = try block.addBinOp(.shl, result, rhs); - const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { + const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: { const eql = try block.addCmpVector(lhs, back, .eq); break :ok try block.addInst(.{ .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, @@ -12352,6 +13033,7 @@ fn zirBitwise( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; sema.src = src; @@ -12366,8 +13048,8 @@ fn zirBitwise( const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); - const scalar_type = resolved_type.scalarType(); - const scalar_tag = scalar_type.zigTypeTag(); + const scalar_type = resolved_type.scalarType(mod); + const scalar_tag = scalar_type.zigTypeTag(mod); const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); @@ -12375,7 +13057,7 @@ fn zirBitwise( const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; if (!is_int) { - return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag()), @tagName(rhs_ty.zigTypeTag()) }); + return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag(mod)), @tagName(rhs_ty.zigTypeTag(mod)) }); } const runtime_src = runtime: { @@ -12384,9 +13066,9 @@ fn zirBitwise( if (try sema.resolveMaybeUndefValIntable(casted_lhs)) |lhs_val| { if (try sema.resolveMaybeUndefValIntable(casted_rhs)) |rhs_val| { const result_val = switch (air_tag) { - .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, sema.mod), - .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, sema.mod), - .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, sema.mod), + .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, mod), + .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, mod), + .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, mod), else => unreachable, }; return sema.addConstant(resolved_type, result_val); @@ -12406,37 +13088,37 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; const operand = try sema.resolveInst(inst_data.operand); const operand_type = sema.typeOf(operand); - const scalar_type = operand_type.scalarType(); + const scalar_type = operand_type.scalarType(mod); - if (scalar_type.zigTypeTag() != .Int) { + if (scalar_type.zigTypeTag(mod) != .Int) { return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{ - operand_type.fmt(sema.mod), + operand_type.fmt(mod), }); } if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef()) { + if (val.isUndef(mod)) { return sema.addConstUndef(operand_type); - } else if (operand_type.zigTypeTag() == .Vector) { - const vec_len = try sema.usizeCast(block, operand_src, operand_type.vectorLen()); - var elem_val_buf: Value.ElemValueBuffer = undefined; - const elems = try sema.arena.alloc(Value, vec_len); + } else if (operand_type.zigTypeTag(mod) == .Vector) { + const vec_len = try sema.usizeCast(block, operand_src, operand_type.vectorLen(mod)); + const elems = try sema.arena.alloc(InternPool.Index, vec_len); for (elems, 0..) |*elem, i| { - const elem_val = val.elemValueBuffer(sema.mod, i, &elem_val_buf); - elem.* = try elem_val.bitwiseNot(scalar_type, sema.arena, sema.mod); + const elem_val = try val.elemValue(mod, i); + elem.* = try (try elem_val.bitwiseNot(scalar_type, sema.arena, mod)).intern(scalar_type, mod); } - return sema.addConstant( - operand_type, - try Value.Tag.aggregate.create(sema.arena, elems), - ); + return sema.addConstant(operand_type, (try mod.intern(.{ .aggregate = .{ + .ty = operand_type.toIntern(), + .storage = .{ .elems = elems }, + } })).toValue()); } else { - const result_val = try val.bitwiseNot(operand_type, sema.arena, sema.mod); + const result_val = try val.bitwiseNot(operand_type, sema.arena, mod); return sema.addConstant(operand_type, result_val); } } @@ -12452,18 +13134,19 @@ fn analyzeTupleCat( lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); const src = LazySrcLoc.nodeOffset(src_node); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; - const lhs_len = lhs_ty.structFieldCount(); - const rhs_len = rhs_ty.structFieldCount(); + const lhs_len = lhs_ty.structFieldCount(mod); + const rhs_len = rhs_ty.structFieldCount(mod); const dest_fields = lhs_len + rhs_len; if (dest_fields == 0) { - return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value)); + return sema.addConstant(Type.empty_struct_literal, Value.empty_struct); } if (lhs_len == 0) { return rhs; @@ -12473,42 +13156,48 @@ fn analyzeTupleCat( } const final_len = try sema.usizeCast(block, rhs_src, dest_fields); - const types = try sema.arena.alloc(Type, final_len); - const values = try sema.arena.alloc(Value, final_len); + const types = try sema.arena.alloc(InternPool.Index, final_len); + const values = try sema.arena.alloc(InternPool.Index, final_len); const opt_runtime_src = rs: { var runtime_src: ?LazySrcLoc = null; var i: u32 = 0; while (i < lhs_len) : (i += 1) { - types[i] = lhs_ty.structFieldType(i); - const default_val = lhs_ty.structFieldDefaultValue(i); - values[i] = default_val; + types[i] = lhs_ty.structFieldType(i, mod).toIntern(); + const default_val = lhs_ty.structFieldDefaultValue(i, mod); + values[i] = default_val.toIntern(); const operand_src = lhs_src; // TODO better source location - if (default_val.tag() == .unreachable_value) { + if (default_val.toIntern() == .unreachable_value) { runtime_src = operand_src; + values[i] = .none; } } i = 0; while (i < rhs_len) : (i += 1) { - types[i + lhs_len] = rhs_ty.structFieldType(i); - const default_val = rhs_ty.structFieldDefaultValue(i); - values[i + lhs_len] = default_val; + types[i + lhs_len] = rhs_ty.structFieldType(i, mod).toIntern(); + const default_val = rhs_ty.structFieldDefaultValue(i, mod); + values[i + lhs_len] = default_val.toIntern(); const operand_src = rhs_src; // TODO better source location - if (default_val.tag() == .unreachable_value) { + if (default_val.toIntern() == .unreachable_value) { runtime_src = operand_src; + values[i + lhs_len] = .none; } } break :rs runtime_src; }; - const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ + const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ .types = types, .values = values, - }); + .names = &.{}, + } }); const runtime_src = opt_runtime_src orelse { - const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstant(tuple_ty, tuple_val); + const tuple_val = try mod.intern(.{ .aggregate = .{ + .ty = tuple_ty, + .storage = .{ .elems = values }, + } }); + return sema.addConstant(tuple_ty.toType(), tuple_val.toValue()); }; try sema.requireRuntimeBlock(block, src, runtime_src); @@ -12526,13 +13215,14 @@ fn analyzeTupleCat( try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty); } - return block.addAggregateInit(tuple_ty, element_refs); + return block.addAggregateInit(tuple_ty.toType(), element_refs); } fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const lhs = try sema.resolveInst(extra.lhs); @@ -12541,8 +13231,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs_ty = sema.typeOf(rhs); const src = inst_data.src(); - const lhs_is_tuple = lhs_ty.isTuple(); - const rhs_is_tuple = rhs_ty.isTuple(); + const lhs_is_tuple = lhs_ty.isTuple(mod); + const rhs_is_tuple = rhs_ty.isTuple(mod); if (lhs_is_tuple and rhs_is_tuple) { return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs); } @@ -12552,11 +13242,11 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: { if (lhs_is_tuple) break :lhs_info @as(Type.ArrayInfo, undefined); - return sema.fail(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(sema.mod)}); + return sema.fail(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)}); }; const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse { assert(!rhs_is_tuple); - return sema.fail(block, rhs_src, "expected indexable; found '{}'", .{rhs_ty.fmt(sema.mod)}); + return sema.fail(block, rhs_src, "expected indexable; found '{}'", .{rhs_ty.fmt(mod)}); }; const resolved_elem_ty = t: { @@ -12618,73 +13308,71 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai ), }; - const result_ty = try Type.array(sema.arena, result_len, res_sent_val, resolved_elem_ty, sema.mod); + const result_ty = try Type.array(sema.arena, result_len, res_sent_val, resolved_elem_ty, mod); const ptr_addrspace = p: { - if (lhs_ty.zigTypeTag() == .Pointer) break :p lhs_ty.ptrAddressSpace(); - if (rhs_ty.zigTypeTag() == .Pointer) break :p rhs_ty.ptrAddressSpace(); + if (lhs_ty.zigTypeTag(mod) == .Pointer) break :p lhs_ty.ptrAddressSpace(mod); + if (rhs_ty.zigTypeTag(mod) == .Pointer) break :p rhs_ty.ptrAddressSpace(mod); break :p null; }; - const runtime_src = if (switch (lhs_ty.zigTypeTag()) { + const runtime_src = if (switch (lhs_ty.zigTypeTag(mod)) { .Array, .Struct => try sema.resolveMaybeUndefVal(lhs), .Pointer => try sema.resolveDefinedValue(block, lhs_src, lhs), else => unreachable, }) |lhs_val| rs: { - if (switch (rhs_ty.zigTypeTag()) { + if (switch (rhs_ty.zigTypeTag(mod)) { .Array, .Struct => try sema.resolveMaybeUndefVal(rhs), .Pointer => try sema.resolveDefinedValue(block, rhs_src, rhs), else => unreachable, }) |rhs_val| { - const lhs_sub_val = if (lhs_ty.isSinglePointer()) + const lhs_sub_val = if (lhs_ty.isSinglePointer(mod)) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; - const rhs_sub_val = if (rhs_ty.isSinglePointer()) + const rhs_sub_val = if (rhs_ty.isSinglePointer(mod)) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val; - const final_len_including_sent = result_len + @boolToInt(res_sent_val != null); - const element_vals = try sema.arena.alloc(Value, final_len_including_sent); + const element_vals = try sema.arena.alloc(InternPool.Index, result_len); var elem_i: usize = 0; while (elem_i < lhs_len) : (elem_i += 1) { const lhs_elem_i = elem_i; - const elem_ty = if (lhs_is_tuple) lhs_ty.structFieldType(lhs_elem_i) else lhs_info.elem_type; - const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i) else Value.initTag(.unreachable_value); - const elem_val = if (elem_default_val.tag() == .unreachable_value) try lhs_sub_val.elemValue(sema.mod, sema.arena, lhs_elem_i) else elem_default_val; + const elem_ty = if (lhs_is_tuple) lhs_ty.structFieldType(lhs_elem_i, mod) else lhs_info.elem_type; + const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, mod) else Value.@"unreachable"; + const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(mod, lhs_elem_i) else elem_default_val; const elem_val_inst = try sema.addConstant(elem_ty, elem_val); const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded); const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); - element_vals[elem_i] = coerced_elem_val; + element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod); } while (elem_i < result_len) : (elem_i += 1) { const rhs_elem_i = elem_i - lhs_len; - const elem_ty = if (rhs_is_tuple) rhs_ty.structFieldType(rhs_elem_i) else rhs_info.elem_type; - const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i) else Value.initTag(.unreachable_value); - const elem_val = if (elem_default_val.tag() == .unreachable_value) try rhs_sub_val.elemValue(sema.mod, sema.arena, rhs_elem_i) else elem_default_val; + const elem_ty = if (rhs_is_tuple) rhs_ty.structFieldType(rhs_elem_i, mod) else rhs_info.elem_type; + const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, mod) else Value.@"unreachable"; + const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(mod, rhs_elem_i) else elem_default_val; const elem_val_inst = try sema.addConstant(elem_ty, elem_val); const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded); const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); - element_vals[elem_i] = coerced_elem_val; - } - if (res_sent_val) |sent_val| { - element_vals[result_len] = sent_val; + element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod); } - const val = try Value.Tag.aggregate.create(sema.arena, element_vals); - return sema.addConstantMaybeRef(block, result_ty, val, ptr_addrspace != null); + return sema.addConstantMaybeRef(block, result_ty, (try mod.intern(.{ .aggregate = .{ + .ty = result_ty.toIntern(), + .storage = .{ .elems = element_vals }, + } })).toValue(), ptr_addrspace != null); } else break :rs rhs_src; } else lhs_src; try sema.requireRuntimeBlock(block, src, runtime_src); if (ptr_addrspace) |ptr_as| { - const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ + const alloc_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = result_ty, .@"addrspace" = ptr_as, }); const alloc = try block.addTy(.alloc, alloc_ty); - const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ + const elem_ptr_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = resolved_elem_ty, .@"addrspace" = ptr_as, }); @@ -12706,7 +13394,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (res_sent_val) |sent_val| { const elem_index = try sema.addIntUnsigned(Type.usize, result_len); const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); - const init = try sema.addConstant(lhs_info.elem_type, sent_val); + const init = try sema.addConstant(lhs_info.elem_type, try mod.getCoerced(sent_val, lhs_info.elem_type)); try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); } @@ -12732,11 +13420,12 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo { + const mod = sema.mod; const operand_ty = sema.typeOf(operand); - switch (operand_ty.zigTypeTag()) { - .Array => return operand_ty.arrayInfo(), + switch (operand_ty.zigTypeTag(mod)) { + .Array => return operand_ty.arrayInfo(mod), .Pointer => { - const ptr_info = operand_ty.ptrInfo().data; + const ptr_info = operand_ty.ptrInfo(mod); switch (ptr_info.size) { // TODO: in the Many case here this should only work if the type // has a sentinel, and this code should compute the length based @@ -12746,24 +13435,24 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins return Type.ArrayInfo{ .elem_type = ptr_info.pointee_type, .sentinel = ptr_info.sentinel, - .len = val.sliceLen(sema.mod), + .len = val.sliceLen(mod), }; }, .One => { - if (ptr_info.pointee_type.zigTypeTag() == .Array) { - return ptr_info.pointee_type.arrayInfo(); + if (ptr_info.pointee_type.zigTypeTag(mod) == .Array) { + return ptr_info.pointee_type.arrayInfo(mod); } }, .C => {}, } }, .Struct => { - if (operand_ty.isTuple() and peer_ty.isIndexable()) { - assert(!peer_ty.isTuple()); + if (operand_ty.isTuple(mod) and peer_ty.isIndexable(mod)) { + assert(!peer_ty.isTuple(mod)); return .{ - .elem_type = peer_ty.elemType2(), + .elem_type = peer_ty.elemType2(mod), .sentinel = null, - .len = operand_ty.arrayLen(), + .len = operand_ty.arrayLen(mod), }; } }, @@ -12777,52 +13466,54 @@ fn analyzeTupleMul( block: *Block, src_node: i32, operand: Air.Inst.Ref, - factor: u64, + factor: usize, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const operand_ty = sema.typeOf(operand); const src = LazySrcLoc.nodeOffset(src_node); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; - const tuple_len = operand_ty.structFieldCount(); - const final_len_u64 = std.math.mul(u64, tuple_len, factor) catch + const tuple_len = operand_ty.structFieldCount(mod); + const final_len = std.math.mul(usize, tuple_len, factor) catch return sema.fail(block, rhs_src, "operation results in overflow", .{}); - if (final_len_u64 == 0) { - return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value)); + if (final_len == 0) { + return sema.addConstant(Type.empty_struct_literal, Value.empty_struct); } - const final_len = try sema.usizeCast(block, rhs_src, final_len_u64); - - const types = try sema.arena.alloc(Type, final_len); - const values = try sema.arena.alloc(Value, final_len); + const types = try sema.arena.alloc(InternPool.Index, final_len); + const values = try sema.arena.alloc(InternPool.Index, final_len); const opt_runtime_src = rs: { var runtime_src: ?LazySrcLoc = null; - var i: u32 = 0; - while (i < tuple_len) : (i += 1) { - types[i] = operand_ty.structFieldType(i); - values[i] = operand_ty.structFieldDefaultValue(i); + for (0..tuple_len) |i| { + types[i] = operand_ty.structFieldType(i, mod).toIntern(); + values[i] = operand_ty.structFieldDefaultValue(i, mod).toIntern(); const operand_src = lhs_src; // TODO better source location - if (values[i].tag() == .unreachable_value) { + if (values[i] == .unreachable_value) { runtime_src = operand_src; + values[i] = .none; // TODO don't treat unreachable_value as special } } - i = 0; - while (i < factor) : (i += 1) { - mem.copyForwards(Type, types[tuple_len * i ..], types[0..tuple_len]); - mem.copyForwards(Value, values[tuple_len * i ..], values[0..tuple_len]); + for (0..factor) |i| { + mem.copyForwards(InternPool.Index, types[tuple_len * i ..], types[0..tuple_len]); + mem.copyForwards(InternPool.Index, values[tuple_len * i ..], values[0..tuple_len]); } break :rs runtime_src; }; - const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ + const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ .types = types, .values = values, - }); + .names = &.{}, + } }); const runtime_src = opt_runtime_src orelse { - const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstant(tuple_ty, tuple_val); + const tuple_val = try mod.intern(.{ .aggregate = .{ + .ty = tuple_ty, + .storage = .{ .elems = values }, + } }); + return sema.addConstant(tuple_ty.toType(), tuple_val.toValue()); }; try sema.requireRuntimeBlock(block, src, runtime_src); @@ -12838,13 +13529,14 @@ fn analyzeTupleMul( @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]); } - return block.addAggregateInit(tuple_ty, element_refs); + return block.addAggregateInit(tuple_ty.toType(), element_refs); } fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const lhs = try sema.resolveInst(extra.lhs); @@ -12854,18 +13546,19 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const operator_src: LazySrcLoc = .{ .node_offset_main_token = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; - if (lhs_ty.isTuple()) { + if (lhs_ty.isTuple(mod)) { // In `**` rhs must be comptime-known, but lhs can be runtime-known const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, "array multiplication factor must be comptime-known"); - return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor); + const factor_casted = try sema.usizeCast(block, rhs_src, factor); + return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor_casted); } // Analyze the lhs first, to catch the case that someone tried to do exponentiation const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse { const msg = msg: { - const msg = try sema.errMsg(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(sema.mod)}); + const msg = try sema.errMsg(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); - switch (lhs_ty.zigTypeTag()) { + switch (lhs_ty.zigTypeTag(mod)) { .Int, .Float, .ComptimeFloat, .ComptimeInt, .Vector => { try sema.errNote(block, operator_src, msg, "this operator multiplies arrays; use std.math.pow for exponentiation", .{}); }, @@ -12883,15 +13576,13 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.fail(block, rhs_src, "operation results in overflow", .{}); const result_len = try sema.usizeCast(block, src, result_len_u64); - const result_ty = try Type.array(sema.arena, result_len, lhs_info.sentinel, lhs_info.elem_type, sema.mod); + const result_ty = try Type.array(sema.arena, result_len, lhs_info.sentinel, lhs_info.elem_type, mod); - const ptr_addrspace = if (lhs_ty.zigTypeTag() == .Pointer) lhs_ty.ptrAddressSpace() else null; + const ptr_addrspace = if (lhs_ty.zigTypeTag(mod) == .Pointer) lhs_ty.ptrAddressSpace(mod) else null; const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { - const final_len_including_sent = result_len + @boolToInt(lhs_info.sentinel != null); - - const lhs_sub_val = if (lhs_ty.isSinglePointer()) + const lhs_sub_val = if (lhs_ty.isSinglePointer(mod)) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; @@ -12899,38 +13590,41 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const val = v: { // Optimization for the common pattern of a single element repeated N times, such // as zero-filling a byte array. - if (lhs_len == 1) { - const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, 0); - break :v try Value.Tag.repeated.create(sema.arena, elem_val); + if (lhs_len == 1 and lhs_info.sentinel == null) { + const elem_val = try lhs_sub_val.elemValue(mod, 0); + break :v try mod.intern(.{ .aggregate = .{ + .ty = result_ty.toIntern(), + .storage = .{ .repeated_elem = elem_val.toIntern() }, + } }); } - const element_vals = try sema.arena.alloc(Value, final_len_including_sent); + const element_vals = try sema.arena.alloc(InternPool.Index, result_len); var elem_i: usize = 0; while (elem_i < result_len) { var lhs_i: usize = 0; while (lhs_i < lhs_len) : (lhs_i += 1) { - const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, lhs_i); - element_vals[elem_i] = elem_val; + const elem_val = try lhs_sub_val.elemValue(mod, lhs_i); + element_vals[elem_i] = elem_val.toIntern(); elem_i += 1; } } - if (lhs_info.sentinel) |sent_val| { - element_vals[result_len] = sent_val; - } - break :v try Value.Tag.aggregate.create(sema.arena, element_vals); + break :v try mod.intern(.{ .aggregate = .{ + .ty = result_ty.toIntern(), + .storage = .{ .elems = element_vals }, + } }); }; - return sema.addConstantMaybeRef(block, result_ty, val, ptr_addrspace != null); + return sema.addConstantMaybeRef(block, result_ty, val.toValue(), ptr_addrspace != null); } try sema.requireRuntimeBlock(block, src, lhs_src); if (ptr_addrspace) |ptr_as| { - const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ + const alloc_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = result_ty, .@"addrspace" = ptr_as, }); const alloc = try block.addTy(.alloc, alloc_ty); - const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ + const elem_ptr_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = lhs_info.elem_type, .@"addrspace" = ptr_as, }); @@ -12973,6 +13667,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const lhs_src = src; @@ -12980,34 +13675,31 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const rhs = try sema.resolveInst(inst_data.operand); const rhs_ty = sema.typeOf(rhs); - const rhs_scalar_ty = rhs_ty.scalarType(); + const rhs_scalar_ty = rhs_ty.scalarType(mod); - if (rhs_scalar_ty.isUnsignedInt() or switch (rhs_scalar_ty.zigTypeTag()) { + if (rhs_scalar_ty.isUnsignedInt(mod) or switch (rhs_scalar_ty.zigTypeTag(mod)) { .Int, .ComptimeInt, .Float, .ComptimeFloat => false, else => true, }) { - return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(sema.mod)}); + return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(mod)}); } if (rhs_scalar_ty.isAnyFloat()) { // We handle float negation here to ensure negative zero is represented in the bits. if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| { - if (rhs_val.isUndef()) return sema.addConstUndef(rhs_ty); - return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, sema.mod)); + if (rhs_val.isUndef(mod)) return sema.addConstUndef(rhs_ty); + return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, mod)); } try sema.requireRuntimeBlock(block, src, null); return block.addUnOp(if (block.float_mode == .Optimized) .neg_optimized else .neg, rhs); } - const lhs = if (rhs_ty.zigTypeTag() == .Vector) - try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) - else - try sema.resolveInst(.zero); - + const lhs = try sema.addConstant(rhs_ty, try sema.splat(rhs_ty, try mod.intValue(rhs_scalar_ty, 0))); return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true); } fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const lhs_src = src; @@ -13015,18 +13707,14 @@ fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const rhs = try sema.resolveInst(inst_data.operand); const rhs_ty = sema.typeOf(rhs); - const rhs_scalar_ty = rhs_ty.scalarType(); + const rhs_scalar_ty = rhs_ty.scalarType(mod); - switch (rhs_scalar_ty.zigTypeTag()) { + switch (rhs_scalar_ty.zigTypeTag(mod)) { .Int, .ComptimeInt, .Float, .ComptimeFloat => {}, - else => return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(sema.mod)}), + else => return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(mod)}), } - const lhs = if (rhs_ty.zigTypeTag() == .Vector) - try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) - else - try sema.resolveInst(.zero); - + const lhs = try sema.addConstant(rhs_ty, try sema.splat(rhs_ty, try mod.intValue(rhs_scalar_ty, 0))); return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true); } @@ -13052,6 +13740,7 @@ fn zirArithmetic( } fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; sema.src = src; @@ -13062,8 +13751,8 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); - const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); + const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); + const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); @@ -13072,25 +13761,22 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, }); - const is_vector = resolved_type.zigTypeTag() == .Vector; - const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - const lhs_scalar_ty = lhs_ty.scalarType(); - const rhs_scalar_ty = rhs_ty.scalarType(); - const scalar_tag = resolved_type.scalarType().zigTypeTag(); + const lhs_scalar_ty = lhs_ty.scalarType(mod); + const rhs_scalar_ty = rhs_ty.scalarType(mod); + const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div); - const mod = sema.mod; const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); - if ((lhs_ty.zigTypeTag() == .ComptimeFloat and rhs_ty.zigTypeTag() == .ComptimeInt) or - (lhs_ty.zigTypeTag() == .ComptimeInt and rhs_ty.zigTypeTag() == .ComptimeFloat)) + if ((lhs_ty.zigTypeTag(mod) == .ComptimeFloat and rhs_ty.zigTypeTag(mod) == .ComptimeInt) or + (lhs_ty.zigTypeTag(mod) == .ComptimeInt and rhs_ty.zigTypeTag(mod) == .ComptimeFloat)) { // If it makes a difference whether we coerce to ints or floats before doing the division, error. // If lhs % rhs is 0, it doesn't matter. @@ -13098,9 +13784,12 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const rhs_val = maybe_rhs_val orelse unreachable; const rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod) catch unreachable; if (!rem.compareAllWithZero(.eq, mod)) { - return sema.fail(block, src, "ambiguous coercion of division operands '{s}' and '{s}'; non-zero remainder '{}'", .{ - @tagName(lhs_ty.tag()), @tagName(rhs_ty.tag()), rem.fmtValue(resolved_type, sema.mod), - }); + return sema.fail( + block, + src, + "ambiguous coercion of division operands '{}' and '{}'; non-zero remainder '{}'", + .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod), rem.fmtValue(resolved_type, mod) }, + ); } } @@ -13134,17 +13823,20 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins switch (scalar_tag) { .Int, .ComptimeInt, .ComptimeFloat => { if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef()) { + if (!lhs_val.isUndef(mod)) { if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - const zero_val = if (is_vector) b: { - break :b try Value.Tag.repeated.create(sema.arena, Value.zero); - } else Value.zero; + const scalar_zero = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), + .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), + else => unreachable, + }; + const zero_val = try sema.splat(resolved_type, scalar_zero); return sema.addConstant(resolved_type, zero_val); } } } if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, rhs_src); } if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { @@ -13158,10 +13850,10 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const runtime_src = rs: { if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { - if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { + if (lhs_val.isUndef(mod)) { + if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) { if (maybe_rhs_val) |rhs_val| { - if (try sema.compareAll(rhs_val, .neq, Value.negative_one, resolved_type)) { + if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) { return sema.addConstUndef(resolved_type); } } @@ -13172,10 +13864,10 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins if (maybe_rhs_val) |rhs_val| { if (is_int) { - const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, mod); - var vector_index: usize = undefined; - if (!(try sema.intFitsInType(res, resolved_type, &vector_index))) { - return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index); + var overflow_idx: ?usize = null; + const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); + if (overflow_idx) |vec_idx| { + return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx); } return sema.addConstant(resolved_type, res); } else { @@ -13200,8 +13892,13 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins } const air_tag = if (is_int) blk: { - if (lhs_ty.isSignedInt() or rhs_ty.isSignedInt()) { - return sema.fail(block, src, "division with '{s}' and '{s}': signed integers must use @divTrunc, @divFloor, or @divExact", .{ @tagName(lhs_ty.tag()), @tagName(rhs_ty.tag()) }); + if (lhs_ty.isSignedInt(mod) or rhs_ty.isSignedInt(mod)) { + return sema.fail( + block, + src, + "division with '{}' and '{}': signed integers must use @divTrunc, @divFloor, or @divExact", + .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod) }, + ); } break :blk Air.Inst.Tag.div_trunc; } else switch (block.float_mode) { @@ -13212,6 +13909,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins } fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; sema.src = src; @@ -13222,8 +13920,8 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); - const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); + const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); + const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); @@ -13232,19 +13930,16 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, }); - const is_vector = resolved_type.zigTypeTag() == .Vector; - const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - const lhs_scalar_ty = lhs_ty.scalarType(); - const scalar_tag = resolved_type.scalarType().zigTypeTag(); + const lhs_scalar_ty = lhs_ty.scalarType(mod); + const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact); - const mod = sema.mod; const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); @@ -13266,19 +13961,22 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // If the lhs is undefined, compile error because there is a possible // value for which the division would result in a remainder. if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, rhs_src); } else { if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - const zero_val = if (is_vector) b: { - break :b try Value.Tag.repeated.create(sema.arena, Value.zero); - } else Value.zero; + const scalar_zero = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), + .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), + else => unreachable, + }; + const zero_val = try sema.splat(resolved_type, scalar_zero); return sema.addConstant(resolved_type, zero_val); } } } if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, rhs_src); } if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { @@ -13293,10 +13991,10 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (!(modulus_val.compareAllWithZero(.eq, mod))) { return sema.fail(block, src, "exact division produced remainder", .{}); } - const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, mod); - var vector_index: usize = undefined; - if (!(try sema.intFitsInType(res, resolved_type, &vector_index))) { - return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index); + var overflow_idx: ?usize = null; + const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); + if (overflow_idx) |vec_idx| { + return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx); } return sema.addConstant(resolved_type, res); } else { @@ -13328,7 +14026,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const ok = if (!is_int) ok: { const floored = try block.addUnOp(.floor, result); - if (resolved_type.zigTypeTag() == .Vector) { + if (resolved_type.zigTypeTag(mod) == .Vector) { const eql = try block.addCmpVector(result, floored, .eq); break :ok try block.addInst(.{ .tag = switch (block.float_mode) { @@ -13350,8 +14048,13 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } else ok: { const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs); - if (resolved_type.zigTypeTag() == .Vector) { - const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); + const scalar_zero = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), + .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), + else => unreachable, + }; + if (resolved_type.zigTypeTag(mod) == .Vector) { + const zero_val = try sema.splat(resolved_type, scalar_zero); const zero = try sema.addConstant(resolved_type, zero_val); const eql = try block.addCmpVector(remainder, zero, .eq); break :ok try block.addInst(.{ @@ -13362,7 +14065,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }, }); } else { - const zero = try sema.addConstant(resolved_type, Value.zero); + const zero = try sema.addConstant(resolved_type, scalar_zero); const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero); break :ok is_in_range; } @@ -13375,6 +14078,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; sema.src = src; @@ -13385,8 +14089,8 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); - const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); + const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); + const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); @@ -13395,20 +14099,17 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, }); - const is_vector = resolved_type.zigTypeTag() == .Vector; - const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - const lhs_scalar_ty = lhs_ty.scalarType(); - const rhs_scalar_ty = rhs_ty.scalarType(); - const scalar_tag = resolved_type.scalarType().zigTypeTag(); + const lhs_scalar_ty = lhs_ty.scalarType(mod); + const rhs_scalar_ty = rhs_ty.scalarType(mod); + const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor); - const mod = sema.mod; const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); @@ -13433,17 +14134,20 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // value (zero) for which the division would be illegal behavior. // If the lhs is undefined, result is undefined. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef()) { + if (!lhs_val.isUndef(mod)) { if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - const zero_val = if (is_vector) b: { - break :b try Value.Tag.repeated.create(sema.arena, Value.zero); - } else Value.zero; + const scalar_zero = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), + .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), + else => unreachable, + }; + const zero_val = try sema.splat(resolved_type, scalar_zero); return sema.addConstant(resolved_type, zero_val); } } } if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, rhs_src); } if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { @@ -13452,10 +14156,10 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // TODO: if the RHS is one, return the LHS directly } if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { - if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { + if (lhs_val.isUndef(mod)) { + if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) { if (maybe_rhs_val) |rhs_val| { - if (try sema.compareAll(rhs_val, .neq, Value.negative_one, resolved_type)) { + if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) { return sema.addConstUndef(resolved_type); } } @@ -13491,6 +14195,7 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; sema.src = src; @@ -13501,8 +14206,8 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); - const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); + const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); + const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); @@ -13511,20 +14216,17 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, }); - const is_vector = resolved_type.zigTypeTag() == .Vector; - const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - const lhs_scalar_ty = lhs_ty.scalarType(); - const rhs_scalar_ty = rhs_ty.scalarType(); - const scalar_tag = resolved_type.scalarType().zigTypeTag(); + const lhs_scalar_ty = lhs_ty.scalarType(mod); + const rhs_scalar_ty = rhs_ty.scalarType(mod); + const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc); - const mod = sema.mod; const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); @@ -13549,17 +14251,20 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // value (zero) for which the division would be illegal behavior. // If the lhs is undefined, result is undefined. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef()) { + if (!lhs_val.isUndef(mod)) { if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - const zero_val = if (is_vector) b: { - break :b try Value.Tag.repeated.create(sema.arena, Value.zero); - } else Value.zero; + const scalar_zero = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), + .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), + else => unreachable, + }; + const zero_val = try sema.splat(resolved_type, scalar_zero); return sema.addConstant(resolved_type, zero_val); } } } if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, rhs_src); } if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { @@ -13567,10 +14272,10 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } } if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { - if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { + if (lhs_val.isUndef(mod)) { + if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) { if (maybe_rhs_val) |rhs_val| { - if (try sema.compareAll(rhs_val, .neq, Value.negative_one, resolved_type)) { + if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) { return sema.addConstUndef(resolved_type); } } @@ -13581,10 +14286,10 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (maybe_rhs_val) |rhs_val| { if (is_int) { - const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, mod); - var vector_index: usize = undefined; - if (!(try sema.intFitsInType(res, resolved_type, &vector_index))) { - return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index); + var overflow_idx: ?usize = null; + const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); + if (overflow_idx) |vec_idx| { + return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx); } return sema.addConstant(resolved_type, res); } else { @@ -13618,39 +14323,34 @@ fn addDivIntOverflowSafety( casted_rhs: Air.Inst.Ref, is_int: bool, ) CompileError!void { + const mod = sema.mod; if (!is_int) return; // If the LHS is unsigned, it cannot cause overflow. - if (!lhs_scalar_ty.isSignedInt()) return; - - const mod = sema.mod; - const target = mod.getTarget(); + if (!lhs_scalar_ty.isSignedInt(mod)) return; // If the LHS is widened to a larger integer type, no overflow is possible. - if (lhs_scalar_ty.intInfo(target).bits < resolved_type.intInfo(target).bits) { + if (lhs_scalar_ty.intInfo(mod).bits < resolved_type.intInfo(mod).bits) { return; } - const min_int = try resolved_type.minInt(sema.arena, target); - const neg_one_scalar = try Value.Tag.int_i64.create(sema.arena, -1); - const neg_one = if (resolved_type.zigTypeTag() == .Vector) - try Value.Tag.repeated.create(sema.arena, neg_one_scalar) - else - neg_one_scalar; + const min_int = try resolved_type.minInt(mod, resolved_type); + const neg_one_scalar = try mod.intValue(lhs_scalar_ty, -1); + const neg_one = try sema.splat(resolved_type, neg_one_scalar); // If the LHS is comptime-known to be not equal to the min int, // no overflow is possible. if (maybe_lhs_val) |lhs_val| { - if (lhs_val.compareAll(.neq, min_int, resolved_type, mod)) return; + if (try lhs_val.compareAll(.neq, min_int, resolved_type, mod)) return; } // If the RHS is comptime-known to not be equal to -1, no overflow is possible. if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compareAll(.neq, neg_one, resolved_type, mod)) return; + if (try rhs_val.compareAll(.neq, neg_one, resolved_type, mod)) return; } var ok: Air.Inst.Ref = .none; - if (resolved_type.zigTypeTag() == .Vector) { + if (resolved_type.zigTypeTag(mod) == .Vector) { if (maybe_lhs_val == null) { const min_int_ref = try sema.addConstant(resolved_type, min_int); ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq); @@ -13706,8 +14406,13 @@ fn addDivByZeroSafety( // emitted above. if (maybe_rhs_val != null) return; - const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { - const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); + const mod = sema.mod; + const scalar_zero = if (is_int) + try mod.intValue(resolved_type.scalarType(mod), 0) + else + try mod.floatValue(resolved_type.scalarType(mod), 0.0); + const ok = if (resolved_type.zigTypeTag(mod) == .Vector) ok: { + const zero_val = try sema.splat(resolved_type, scalar_zero); const zero = try sema.addConstant(resolved_type, zero_val); const ok = try block.addCmpVector(casted_rhs, zero, .neq); break :ok try block.addInst(.{ @@ -13718,7 +14423,7 @@ fn addDivByZeroSafety( } }, }); } else ok: { - const zero = try sema.addConstant(resolved_type, Value.zero); + const zero = try sema.addConstant(resolved_type, scalar_zero); break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero); }; try sema.addSafetyCheck(block, ok, .divide_by_zero); @@ -13733,6 +14438,7 @@ fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst } fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; sema.src = src; @@ -13743,8 +14449,8 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); - const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); + const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); + const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); @@ -13753,20 +14459,19 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, }); - const is_vector = resolved_type.zigTypeTag() == .Vector; + const is_vector = resolved_type.zigTypeTag(mod) == .Vector; const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - const lhs_scalar_ty = lhs_ty.scalarType(); - const rhs_scalar_ty = rhs_ty.scalarType(); - const scalar_tag = resolved_type.scalarType().zigTypeTag(); + const lhs_scalar_ty = lhs_ty.scalarType(mod); + const rhs_scalar_ty = rhs_ty.scalarType(mod); + const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem); - const mod = sema.mod; const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); @@ -13786,20 +14491,26 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. // then emit a compile error saying you have to pick one. if (is_int) { if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, lhs_src); } if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - const zero_val = if (is_vector) b: { - break :b try Value.Tag.repeated.create(sema.arena, Value.zero); - } else Value.zero; + const scalar_zero = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), + .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), + else => unreachable, + }; + const zero_val = if (is_vector) (try mod.intern(.{ .aggregate = .{ + .ty = resolved_type.toIntern(), + .storage = .{ .repeated_elem = scalar_zero.toIntern() }, + } })).toValue() else scalar_zero; return sema.addConstant(resolved_type, zero_val); } - } else if (lhs_scalar_ty.isSignedInt()) { + } else if (lhs_scalar_ty.isSignedInt(mod)) { return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); } if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, rhs_src); } if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { @@ -13820,7 +14531,7 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return sema.addConstant(resolved_type, rem_result); } break :rs lhs_src; - } else if (rhs_scalar_ty.isSignedInt()) { + } else if (rhs_scalar_ty.isSignedInt(mod)) { return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); } else { break :rs rhs_src; @@ -13828,7 +14539,7 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } // float operands if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, rhs_src); } if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { @@ -13838,7 +14549,7 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); } if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef() or !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema))) { + if (lhs_val.isUndef(mod) or !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema))) { return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); } return sema.addConstant( @@ -13869,32 +14580,31 @@ fn intRem( lhs: Value, rhs: Value, ) CompileError!Value { - if (ty.zigTypeTag() == .Vector) { - const result_data = try sema.arena.alloc(Value, ty.vectorLen()); + const mod = sema.mod; + if (ty.zigTypeTag(mod) == .Vector) { + const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); + const scalar_ty = ty.scalarType(mod); for (result_data, 0..) |*scalar, i| { - var lhs_buf: Value.ElemValueBuffer = undefined; - var rhs_buf: Value.ElemValueBuffer = undefined; - const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf); - const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf); - scalar.* = try sema.intRemScalar(lhs_elem, rhs_elem); + const lhs_elem = try lhs.elemValue(mod, i); + const rhs_elem = try rhs.elemValue(mod, i); + scalar.* = try (try sema.intRemScalar(lhs_elem, rhs_elem, scalar_ty)).intern(scalar_ty, mod); } - return Value.Tag.aggregate.create(sema.arena, result_data); + return (try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .elems = result_data }, + } })).toValue(); } - return sema.intRemScalar(lhs, rhs); + return sema.intRemScalar(lhs, rhs, ty); } -fn intRemScalar( - sema: *Sema, - lhs: Value, - rhs: Value, -) CompileError!Value { - const target = sema.mod.getTarget(); +fn intRemScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) CompileError!Value { + const mod = sema.mod; // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema); - const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema); + const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); + const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); const limbs_q = try sema.arena.alloc( math.big.Limb, lhs_bigint.limbs.len, @@ -13912,10 +14622,11 @@ fn intRemScalar( var result_q = math.big.int.Mutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; var result_r = math.big.int.Mutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer); - return Value.fromBigInt(sema.arena, result_r.toConst()); + return mod.intValue_big(scalar_ty, result_r.toConst()); } fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; sema.src = src; @@ -13926,8 +14637,8 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); - const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); + const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); + const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); @@ -13939,13 +14650,12 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - const scalar_tag = resolved_type.scalarType().zigTypeTag(); + const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod); - const mod = sema.mod; const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); @@ -13963,12 +14673,12 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins // If the lhs is undefined, result is undefined. if (is_int) { if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, lhs_src); } } if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, rhs_src); } if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { @@ -13987,7 +14697,7 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins } // float operands if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, rhs_src); } if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { @@ -13995,7 +14705,7 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins } } if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { @@ -14018,6 +14728,7 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins } fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; sema.src = src; @@ -14028,8 +14739,8 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); - const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); + const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); + const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); @@ -14041,13 +14752,12 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - const scalar_tag = resolved_type.scalarType().zigTypeTag(); + const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem); - const mod = sema.mod; const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); @@ -14065,12 +14775,12 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins // If the lhs is undefined, result is undefined. if (is_int) { if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, lhs_src); } } if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, rhs_src); } if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { @@ -14089,7 +14799,7 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins } // float operands if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, rhs_src); } if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { @@ -14097,7 +14807,7 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins } } if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { @@ -14159,7 +14869,7 @@ fn zirOverflowArithmetic( const lhs = try sema.coerce(block, dest_ty, uncasted_lhs, lhs_src); const rhs = try sema.coerce(block, rhs_dest_ty, uncasted_rhs, rhs_src); - if (dest_ty.scalarType().zigTypeTag() != .Int) { + if (dest_ty.scalarType(mod).zigTypeTag(mod) != .Int) { return sema.fail(block, src, "expected vector of integers or integer tag type, found '{}'", .{dest_ty.fmt(mod)}); } @@ -14167,30 +14877,32 @@ fn zirOverflowArithmetic( const maybe_rhs_val = try sema.resolveMaybeUndefVal(rhs); const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty); + const overflow_ty = mod.intern_pool.indexToKey(tuple_ty.toIntern()).anon_struct_type.types[1].toType(); var result: struct { inst: Air.Inst.Ref = .none, - wrapped: Value = Value.initTag(.unreachable_value), + wrapped: Value = Value.@"unreachable", overflow_bit: Value, } = result: { + const zero_bit = try mod.intValue(Type.u1, 0); switch (zir_tag) { .add_with_overflow => { // If either of the arguments is zero, `false` is returned and the other is stored // 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() and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { - break :result .{ .overflow_bit = try maybeRepeated(sema, dest_ty, Value.zero), .inst = rhs }; + if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { + break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs }; } } if (maybe_rhs_val) |rhs_val| { - if (!rhs_val.isUndef() and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) { - break :result .{ .overflow_bit = try maybeRepeated(sema, dest_ty, Value.zero), .inst = lhs }; + if (!rhs_val.isUndef(mod) and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) { + break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; } } if (maybe_lhs_val) |lhs_val| { if (maybe_rhs_val) |rhs_val| { - if (lhs_val.isUndef() or rhs_val.isUndef()) { + if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; } @@ -14203,12 +14915,12 @@ fn zirOverflowArithmetic( // If the rhs is zero, then the result is lhs and no overflow occured. // Otherwise, if either result is undefined, both results are undefined. if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; } else if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - break :result .{ .overflow_bit = try maybeRepeated(sema, dest_ty, Value.zero), .inst = lhs }; + break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; } else if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; } @@ -14221,29 +14933,30 @@ fn zirOverflowArithmetic( // If either of the arguments is zero, the result is zero and no overflow occured. // If either of the arguments is one, the result is the other and no overflow occured. // Otherwise, if either of the arguments is undefined, both results are undefined. + const scalar_one = try mod.intValue(dest_ty.scalarType(mod), 1); if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef()) { + if (!lhs_val.isUndef(mod)) { if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - break :result .{ .overflow_bit = try maybeRepeated(sema, dest_ty, Value.zero), .inst = lhs }; - } else if (try sema.compareAll(lhs_val, .eq, try maybeRepeated(sema, dest_ty, Value.one), dest_ty)) { - break :result .{ .overflow_bit = try maybeRepeated(sema, dest_ty, Value.zero), .inst = rhs }; + break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; + } else if (try sema.compareAll(lhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) { + break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs }; } } } if (maybe_rhs_val) |rhs_val| { - if (!rhs_val.isUndef()) { + if (!rhs_val.isUndef(mod)) { if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - break :result .{ .overflow_bit = try maybeRepeated(sema, dest_ty, Value.zero), .inst = rhs }; - } else if (try sema.compareAll(rhs_val, .eq, try maybeRepeated(sema, dest_ty, Value.one), dest_ty)) { - break :result .{ .overflow_bit = try maybeRepeated(sema, dest_ty, Value.zero), .inst = lhs }; + break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs }; + } else if (try sema.compareAll(rhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) { + break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; } } } if (maybe_lhs_val) |lhs_val| { if (maybe_rhs_val) |rhs_val| { - if (lhs_val.isUndef() or rhs_val.isUndef()) { + if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; } @@ -14257,22 +14970,22 @@ fn zirOverflowArithmetic( // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred. // Oterhwise if either of the arguments is undefined, both results are undefined. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef() and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { - break :result .{ .overflow_bit = try maybeRepeated(sema, dest_ty, Value.zero), .inst = lhs }; + if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { + break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; } } if (maybe_rhs_val) |rhs_val| { - if (!rhs_val.isUndef() and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) { - break :result .{ .overflow_bit = try maybeRepeated(sema, dest_ty, Value.zero), .inst = lhs }; + if (!rhs_val.isUndef(mod) and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) { + break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; } } if (maybe_lhs_val) |lhs_val| { if (maybe_rhs_val) |rhs_val| { - if (lhs_val.isUndef() or rhs_val.isUndef()) { + if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; } - const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, sema.mod); + const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, mod); break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; } } @@ -14311,40 +15024,46 @@ fn zirOverflowArithmetic( } if (result.inst == .none) { - const values = try sema.arena.alloc(Value, 2); - values[0] = result.wrapped; - values[1] = result.overflow_bit; - const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstant(tuple_ty, tuple_val); + return sema.addConstant(tuple_ty, (try mod.intern(.{ .aggregate = .{ + .ty = tuple_ty.toIntern(), + .storage = .{ .elems = &.{ + result.wrapped.toIntern(), + result.overflow_bit.toIntern(), + } }, + } })).toValue()); } const element_refs = try sema.arena.alloc(Air.Inst.Ref, 2); element_refs[0] = result.inst; - element_refs[1] = try sema.addConstant(tuple_ty.structFieldType(1), result.overflow_bit); + element_refs[1] = try sema.addConstant(tuple_ty.structFieldType(1, mod), result.overflow_bit); return block.addAggregateInit(tuple_ty, element_refs); } -fn maybeRepeated(sema: *Sema, ty: Type, val: Value) !Value { - if (ty.zigTypeTag() != .Vector) return val; - return Value.Tag.repeated.create(sema.arena, val); +fn splat(sema: *Sema, ty: Type, val: Value) !Value { + const mod = sema.mod; + if (ty.zigTypeTag(mod) != .Vector) return val; + const repeated = try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .repeated_elem = val.toIntern() }, + } }); + return repeated.toValue(); } fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type { - const ov_ty = if (ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, ty.vectorLen(), Type.u1) else Type.u1; - - const types = try sema.arena.alloc(Type, 2); - const values = try sema.arena.alloc(Value, 2); - const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ - .types = types, - .values = values, - }); - - types[0] = ty; - types[1] = ov_ty; - values[0] = Value.initTag(.unreachable_value); - values[1] = Value.initTag(.unreachable_value); + const mod = sema.mod; + const ov_ty = if (ty.zigTypeTag(mod) == .Vector) try mod.vectorType(.{ + .len = ty.vectorLen(mod), + .child = .u1_type, + }) else Type.u1; - return tuple_ty; + const types = [2]InternPool.Index{ ty.toIntern(), ov_ty.toIntern() }; + const values = [2]InternPool.Index{ .none, .none }; + const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ + .types = &types, + .values = &values, + .names = &.{}, + } }); + return tuple_ty.toType(); } fn analyzeArithmetic( @@ -14359,13 +15078,14 @@ fn analyzeArithmetic( rhs_src: LazySrcLoc, want_safety: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); - const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); + const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); + const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); - if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize()) { + if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize(mod)) { .One, .Slice => {}, .Many, .C => { const air_tag: Air.Inst.Tag = switch (zir_tag) { @@ -14382,18 +15102,16 @@ fn analyzeArithmetic( .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, }); - const is_vector = resolved_type.zigTypeTag() == .Vector; - const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - const scalar_tag = resolved_type.scalarType().zigTypeTag(); + const scalar_type = resolved_type.scalarType(mod); + const scalar_tag = scalar_type.zigTypeTag(mod); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag); - const mod = sema.mod; const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); const rs: struct { src: LazySrcLoc, air_tag: Air.Inst.Tag } = rs: { @@ -14407,12 +15125,12 @@ fn analyzeArithmetic( // overflow (max_int), causing illegal behavior. // For floats: either operand being undef makes the result undef. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef() and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { + if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { return casted_rhs; } } if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { if (is_int) { return sema.failWithUseOfUndef(block, rhs_src); } else { @@ -14425,7 +15143,7 @@ fn analyzeArithmetic( } const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .add_optimized else .add; if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { if (is_int) { return sema.failWithUseOfUndef(block, lhs_src); } else { @@ -14434,16 +15152,16 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { if (is_int) { - const sum = try sema.intAdd(lhs_val, rhs_val, resolved_type); - var vector_index: usize = undefined; - if (!(try sema.intFitsInType(sum, resolved_type, &vector_index))) { - return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vector_index); + var overflow_idx: ?usize = null; + const sum = try sema.intAdd(lhs_val, rhs_val, resolved_type, &overflow_idx); + if (overflow_idx) |vec_idx| { + return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vec_idx); } return sema.addConstant(resolved_type, sum); } else { return sema.addConstant( resolved_type, - try sema.floatAdd(lhs_val, rhs_val, resolved_type), + try Value.floatAdd(lhs_val, rhs_val, resolved_type, sema.arena, mod), ); } } else break :rs .{ .src = rhs_src, .air_tag = air_tag }; @@ -14454,13 +15172,13 @@ fn analyzeArithmetic( // If either of the operands are zero, the other operand is returned. // If either of the operands are undefined, the result is undefined. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef() and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { + if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { return casted_rhs; } } const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .addwrap_optimized else .addwrap; if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { @@ -14479,12 +15197,12 @@ fn analyzeArithmetic( // If either of the operands are zero, then the other operand is returned. // If either of the operands are undefined, the result is undefined. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef() and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { + if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { return casted_rhs; } } if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { @@ -14492,7 +15210,7 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { const val = if (scalar_tag == .ComptimeInt) - try sema.intAdd(lhs_val, rhs_val, resolved_type) + try sema.intAdd(lhs_val, rhs_val, resolved_type, undefined) else try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, mod); @@ -14509,7 +15227,7 @@ fn analyzeArithmetic( // overflow, causing illegal behavior. // For floats: either operand being undef makes the result undef. if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { if (is_int) { return sema.failWithUseOfUndef(block, rhs_src); } else { @@ -14522,7 +15240,7 @@ fn analyzeArithmetic( } const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .sub_optimized else .sub; if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { if (is_int) { return sema.failWithUseOfUndef(block, lhs_src); } else { @@ -14531,16 +15249,16 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { if (is_int) { - const diff = try sema.intSub(lhs_val, rhs_val, resolved_type); - var vector_index: usize = undefined; - if (!(try sema.intFitsInType(diff, resolved_type, &vector_index))) { - return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vector_index); + var overflow_idx: ?usize = null; + const diff = try sema.intSub(lhs_val, rhs_val, resolved_type, &overflow_idx); + if (overflow_idx) |vec_idx| { + return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vec_idx); } return sema.addConstant(resolved_type, diff); } else { return sema.addConstant( resolved_type, - try sema.floatSub(lhs_val, rhs_val, resolved_type), + try Value.floatSub(lhs_val, rhs_val, resolved_type, sema.arena, mod), ); } } else break :rs .{ .src = rhs_src, .air_tag = air_tag }; @@ -14551,7 +15269,7 @@ fn analyzeArithmetic( // If the RHS is zero, then the other operand is returned, even if it is undefined. // If either of the operands are undefined, the result is undefined. if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { @@ -14560,7 +15278,7 @@ fn analyzeArithmetic( } const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .subwrap_optimized else .subwrap; if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { @@ -14576,7 +15294,7 @@ fn analyzeArithmetic( // If the RHS is zero, result is LHS. // If either of the operands are undefined, result is undefined. if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { @@ -14584,12 +15302,12 @@ fn analyzeArithmetic( } } if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { const val = if (scalar_tag == .ComptimeInt) - try sema.intSub(lhs_val, rhs_val, resolved_type) + try sema.intSub(lhs_val, rhs_val, resolved_type, undefined) else try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, mod); @@ -14609,62 +15327,74 @@ fn analyzeArithmetic( // If either of the operands are inf, and the other operand is zero, // the result is nan. // If either of the operands are nan, the result is nan. + const scalar_zero = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0), + .ComptimeInt, .Int => try mod.intValue(scalar_type, 0), + else => unreachable, + }; + const scalar_one = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0), + .ComptimeInt, .Int => try mod.intValue(scalar_type, 1), + else => unreachable, + }; if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef()) { - if (lhs_val.isNan()) { + if (!lhs_val.isUndef(mod)) { + if (lhs_val.isNan(mod)) { return sema.addConstant(resolved_type, lhs_val); } if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) lz: { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isNan()) { + if (rhs_val.isNan(mod)) { return sema.addConstant(resolved_type, rhs_val); } - if (rhs_val.isInf()) { - return sema.addConstant(resolved_type, try Value.Tag.float_32.create(sema.arena, std.math.nan_f32)); + if (rhs_val.isInf(mod)) { + return sema.addConstant( + resolved_type, + try mod.floatValue(resolved_type, std.math.nan_f128), + ); } } else if (resolved_type.isAnyFloat()) { break :lz; } - const zero_val = if (is_vector) b: { - break :b try Value.Tag.repeated.create(sema.arena, Value.zero); - } else Value.zero; + const zero_val = try sema.splat(resolved_type, scalar_zero); return sema.addConstant(resolved_type, zero_val); } - if (try sema.compareAll(lhs_val, .eq, Value.one, resolved_type)) { + if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { return casted_rhs; } } } const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .mul_optimized else .mul; if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { if (is_int) { return sema.failWithUseOfUndef(block, rhs_src); } else { return sema.addConstUndef(resolved_type); } } - if (rhs_val.isNan()) { + if (rhs_val.isNan(mod)) { return sema.addConstant(resolved_type, rhs_val); } if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) rz: { if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isInf()) { - return sema.addConstant(resolved_type, try Value.Tag.float_32.create(sema.arena, std.math.nan_f32)); + if (lhs_val.isInf(mod)) { + return sema.addConstant( + resolved_type, + try mod.floatValue(resolved_type, std.math.nan_f128), + ); } } else if (resolved_type.isAnyFloat()) { break :rz; } - const zero_val = if (is_vector) b: { - break :b try Value.Tag.repeated.create(sema.arena, Value.zero); - } else Value.zero; + const zero_val = try sema.splat(resolved_type, scalar_zero); return sema.addConstant(resolved_type, zero_val); } - if (try sema.compareAll(rhs_val, .eq, Value.one, resolved_type)) { + if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { if (is_int) { return sema.failWithUseOfUndef(block, lhs_src); } else { @@ -14672,16 +15402,16 @@ fn analyzeArithmetic( } } if (is_int) { - const product = try lhs_val.intMul(rhs_val, resolved_type, sema.arena, sema.mod); - var vector_index: usize = undefined; - if (!(try sema.intFitsInType(product, resolved_type, &vector_index))) { - return sema.failWithIntegerOverflow(block, src, resolved_type, product, vector_index); + var overflow_idx: ?usize = null; + const product = try lhs_val.intMul(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); + if (overflow_idx) |vec_idx| { + return sema.failWithIntegerOverflow(block, src, resolved_type, product, vec_idx); } return sema.addConstant(resolved_type, product); } else { return sema.addConstant( resolved_type, - try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, sema.mod), + try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, mod), ); } } else break :rs .{ .src = lhs_src, .air_tag = air_tag }; @@ -14692,40 +15422,46 @@ fn analyzeArithmetic( // If either of the operands are zero, result is zero. // If either of the operands are one, result is the other operand. // If either of the operands are undefined, result is undefined. + const scalar_zero = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0), + .ComptimeInt, .Int => try mod.intValue(scalar_type, 0), + else => unreachable, + }; + const scalar_one = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0), + .ComptimeInt, .Int => try mod.intValue(scalar_type, 1), + else => unreachable, + }; if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef()) { + if (!lhs_val.isUndef(mod)) { if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - const zero_val = if (is_vector) b: { - break :b try Value.Tag.repeated.create(sema.arena, Value.zero); - } else Value.zero; + const zero_val = try sema.splat(resolved_type, scalar_zero); return sema.addConstant(resolved_type, zero_val); } - if (try sema.compareAll(lhs_val, .eq, Value.one, resolved_type)) { + if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { return casted_rhs; } } } const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .mulwrap_optimized else .mulwrap; if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - const zero_val = if (is_vector) b: { - break :b try Value.Tag.repeated.create(sema.arena, Value.zero); - } else Value.zero; + const zero_val = try sema.splat(resolved_type, scalar_zero); return sema.addConstant(resolved_type, zero_val); } - if (try sema.compareAll(rhs_val, .eq, Value.one, resolved_type)) { + if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } return sema.addConstant( resolved_type, - try lhs_val.numberMulWrap(rhs_val, resolved_type, sema.arena, sema.mod), + try lhs_val.numberMulWrap(rhs_val, resolved_type, sema.arena, mod), ); } else break :rs .{ .src = lhs_src, .air_tag = air_tag }; } else break :rs .{ .src = rhs_src, .air_tag = air_tag }; @@ -14735,41 +15471,47 @@ fn analyzeArithmetic( // If either of the operands are zero, result is zero. // If either of the operands are one, result is the other operand. // If either of the operands are undefined, result is undefined. + const scalar_zero = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0), + .ComptimeInt, .Int => try mod.intValue(scalar_type, 0), + else => unreachable, + }; + const scalar_one = switch (scalar_tag) { + .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0), + .ComptimeInt, .Int => try mod.intValue(scalar_type, 1), + else => unreachable, + }; if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef()) { + if (!lhs_val.isUndef(mod)) { if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - const zero_val = if (is_vector) b: { - break :b try Value.Tag.repeated.create(sema.arena, Value.zero); - } else Value.zero; + const zero_val = try sema.splat(resolved_type, scalar_zero); return sema.addConstant(resolved_type, zero_val); } - if (try sema.compareAll(lhs_val, .eq, Value.one, resolved_type)) { + if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { return casted_rhs; } } } if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { + if (rhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { - const zero_val = if (is_vector) b: { - break :b try Value.Tag.repeated.create(sema.arena, Value.zero); - } else Value.zero; + const zero_val = try sema.splat(resolved_type, scalar_zero); return sema.addConstant(resolved_type, zero_val); } - if (try sema.compareAll(rhs_val, .eq, Value.one, resolved_type)) { + if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef()) { + if (lhs_val.isUndef(mod)) { return sema.addConstUndef(resolved_type); } const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intMul(rhs_val, resolved_type, sema.arena, sema.mod) + try lhs_val.intMul(rhs_val, resolved_type, undefined, sema.arena, mod) else - try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, sema.mod); + try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, mod); return sema.addConstant(resolved_type, val); } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat }; @@ -14801,7 +15543,7 @@ fn analyzeArithmetic( } }, }); const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); - const any_ov_bit = if (resolved_type.zigTypeTag() == .Vector) + const any_ov_bit = if (resolved_type.zigTypeTag(mod) == .Vector) try block.addInst(.{ .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, .data = .{ .reduce = .{ @@ -14811,7 +15553,7 @@ fn analyzeArithmetic( }) else ov_bit; - const zero_ov = try sema.addConstant(Type.u1, Value.zero); + const zero_ov = try sema.addConstant(Type.u1, try mod.intValue(Type.u1, 0)); const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov); try sema.addSafetyCheck(block, no_ov, .integer_overflow); @@ -14835,15 +15577,12 @@ fn analyzePtrArithmetic( // TODO if the operand is comptime-known to be negative, or is a negative int, // coerce to isize instead of usize. const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src); - const target = sema.mod.getTarget(); + const mod = sema.mod; const opt_ptr_val = try sema.resolveMaybeUndefVal(ptr); const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset); const ptr_ty = sema.typeOf(ptr); - const ptr_info = ptr_ty.ptrInfo().data; - const elem_ty = if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Array) - ptr_info.pointee_type.childType() - else - ptr_info.pointee_type; + const ptr_info = ptr_ty.ptrInfo(mod); + assert(ptr_info.size == .Many or ptr_info.size == .C); const new_ptr_ty = t: { // Calculate the new pointer alignment. @@ -14854,9 +15593,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 = elem_ty.abiSize(target); + const elem_size = ptr_info.pointee_type.abiSize(mod); const addend = if (opt_off_val) |off_val| a: { - const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(target)); + const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(mod)); break :a elem_size * off_int; } else elem_size; @@ -14865,7 +15604,7 @@ fn analyzePtrArithmetic( // non zero). const new_align = @as(u32, 1) << @intCast(u5, @ctz(addend | ptr_info.@"align")); - break :t try Type.ptr(sema.arena, sema.mod, .{ + break :t try Type.ptr(sema.arena, mod, .{ .pointee_type = ptr_info.pointee_type, .sentinel = ptr_info.sentinel, .@"align" = new_align, @@ -14880,24 +15619,24 @@ fn analyzePtrArithmetic( const runtime_src = rs: { if (opt_ptr_val) |ptr_val| { if (opt_off_val) |offset_val| { - if (ptr_val.isUndef()) return sema.addConstUndef(new_ptr_ty); + if (ptr_val.isUndef(mod)) return sema.addConstUndef(new_ptr_ty); - const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(target)); + const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(mod)); if (offset_int == 0) return ptr; - if (try ptr_val.getUnsignedIntAdvanced(target, sema)) |addr| { - const elem_size = elem_ty.abiSize(target); + if (try ptr_val.getUnsignedIntAdvanced(mod, sema)) |addr| { + const elem_size = ptr_info.pointee_type.abiSize(mod); const new_addr = switch (air_tag) { .ptr_add => addr + elem_size * offset_int, .ptr_sub => addr - elem_size * offset_int, else => unreachable, }; - const new_ptr_val = try Value.Tag.int_u64.create(sema.arena, new_addr); + const new_ptr_val = try mod.ptrIntValue(new_ptr_ty, new_addr); return sema.addConstant(new_ptr_ty, new_ptr_val); } if (air_tag == .ptr_sub) { return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{}); } - const new_ptr_val = try ptr_val.elemPtr(ptr_ty, sema.arena, offset_int, sema.mod); + const new_ptr_val = try ptr_val.elemPtr(new_ptr_ty, offset_int, mod); return sema.addConstant(new_ptr_ty, new_ptr_val); } else break :rs offset_src; } else break :rs ptr_src; @@ -14943,7 +15682,7 @@ fn zirAsm( const inputs_len = @truncate(u5, extended.small >> 5); const clobbers_len = @truncate(u5, extended.small >> 10); const is_volatile = @truncate(u1, extended.small >> 15) != 0; - const is_global_assembly = sema.func == null; + const is_global_assembly = sema.func_index == .none; const asm_source: []const u8 = if (tmpl_is_expr) blk: { const tmpl = @intToEnum(Zir.Inst.Ref, extra.data.asm_source); @@ -15007,6 +15746,7 @@ fn zirAsm( const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len); const inputs = try sema.arena.alloc(ConstraintName, inputs_len); + const mod = sema.mod; for (args, 0..) |*arg, arg_i| { const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i); @@ -15014,9 +15754,9 @@ fn zirAsm( const uncasted_arg = try sema.resolveInst(input.data.operand); const uncasted_arg_ty = sema.typeOf(uncasted_arg); - switch (uncasted_arg_ty.zigTypeTag()) { - .ComptimeInt => arg.* = try sema.coerce(block, Type.initTag(.usize), uncasted_arg, src), - .ComptimeFloat => arg.* = try sema.coerce(block, Type.initTag(.f64), uncasted_arg, src), + switch (uncasted_arg_ty.zigTypeTag(mod)) { + .ComptimeInt => arg.* = try sema.coerce(block, Type.usize, uncasted_arg, src), + .ComptimeFloat => arg.* = try sema.coerce(block, Type.f64, uncasted_arg, src), else => { arg.* = uncasted_arg; try sema.queueFullTypeResolution(uncasted_arg_ty); @@ -15096,6 +15836,7 @@ fn zirCmpEq( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const src: LazySrcLoc = inst_data.src(); @@ -15106,8 +15847,8 @@ fn zirCmpEq( const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - const lhs_ty_tag = lhs_ty.zigTypeTag(); - const rhs_ty_tag = rhs_ty.zigTypeTag(); + const lhs_ty_tag = lhs_ty.zigTypeTag(mod); + const rhs_ty_tag = rhs_ty.zigTypeTag(mod); if (lhs_ty_tag == .Null and rhs_ty_tag == .Null) { // null == null, null != null if (op == .eq) { @@ -15118,16 +15859,16 @@ fn zirCmpEq( } // comparing null with optionals - if (lhs_ty_tag == .Null and (rhs_ty_tag == .Optional or rhs_ty.isCPtr())) { + if (lhs_ty_tag == .Null and (rhs_ty_tag == .Optional or rhs_ty.isCPtr(mod))) { return sema.analyzeIsNull(block, src, rhs, op == .neq); } - if (rhs_ty_tag == .Null and (lhs_ty_tag == .Optional or lhs_ty.isCPtr())) { + if (rhs_ty_tag == .Null and (lhs_ty_tag == .Optional or lhs_ty.isCPtr(mod))) { return sema.analyzeIsNull(block, src, lhs, op == .neq); } if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty; - return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(sema.mod)}); + return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(mod)}); } if (lhs_ty_tag == .Union and (rhs_ty_tag == .EnumLiteral or rhs_ty_tag == .Enum)) { @@ -15141,15 +15882,12 @@ fn zirCmpEq( const runtime_src: LazySrcLoc = src: { if (try sema.resolveMaybeUndefVal(lhs)) |lval| { if (try sema.resolveMaybeUndefVal(rhs)) |rval| { - if (lval.isUndef() or rval.isUndef()) { + if (lval.isUndef(mod) or rval.isUndef(mod)) { return sema.addConstUndef(Type.bool); } - // TODO optimisation opportunity: evaluate if mem.eql is faster with the names, - // or calling to Module.getErrorValue to get the values and then compare them is - // faster. - const lhs_name = lval.castTag(.@"error").?.data.name; - const rhs_name = rval.castTag(.@"error").?.data.name; - if (mem.eql(u8, lhs_name, rhs_name) == (op == .eq)) { + const lkey = mod.intern_pool.indexToKey(lval.toIntern()); + const rkey = mod.intern_pool.indexToKey(rval.toIntern()); + if ((lkey.err.name == rkey.err.name) == (op == .eq)) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; @@ -15167,7 +15905,7 @@ fn zirCmpEq( if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs); const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs); - if (lhs_as_type.eql(rhs_as_type, sema.mod) == (op == .eq)) { + if (lhs_as_type.eql(rhs_as_type, mod) == (op == .eq)) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; @@ -15186,12 +15924,13 @@ fn analyzeCmpUnionTag( tag_src: LazySrcLoc, op: std.math.CompareOperator, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const union_ty = try sema.resolveTypeFields(sema.typeOf(un)); - const union_tag_ty = union_ty.unionTagType() orelse { + const union_tag_ty = union_ty.unionTagType(mod) orelse { const msg = msg: { const msg = try sema.errMsg(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); errdefer msg.destroy(sema.gpa); - try sema.mod.errNoteNonLazy(union_ty.declSrcLoc(sema.mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(sema.mod)}); + try mod.errNoteNonLazy(union_ty.declSrcLoc(mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(mod)}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -15202,9 +15941,9 @@ fn analyzeCmpUnionTag( const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| { - if (enum_val.isUndef()) return sema.addConstUndef(Type.bool); - const field_ty = union_ty.unionFieldType(enum_val, sema.mod); - if (field_ty.zigTypeTag() == .NoReturn) { + if (enum_val.isUndef(mod)) return sema.addConstUndef(Type.bool); + const field_ty = union_ty.unionFieldType(enum_val, mod); + if (field_ty.zigTypeTag(mod) == .NoReturn) { return Air.Inst.Ref.bool_false; } } @@ -15243,34 +15982,35 @@ fn analyzeCmp( rhs_src: LazySrcLoc, is_equality_cmp: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - if (lhs_ty.zigTypeTag() != .Optional and rhs_ty.zigTypeTag() != .Optional) { + if (lhs_ty.zigTypeTag(mod) != .Optional and rhs_ty.zigTypeTag(mod) != .Optional) { try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); } - if (lhs_ty.zigTypeTag() == .Vector and rhs_ty.zigTypeTag() == .Vector) { + if (lhs_ty.zigTypeTag(mod) == .Vector and rhs_ty.zigTypeTag(mod) == .Vector) { return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src); } - if (lhs_ty.isNumeric() and rhs_ty.isNumeric()) { + if (lhs_ty.isNumeric(mod) and rhs_ty.isNumeric(mod)) { // This operation allows any combination of integer and float types, regardless of the // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for // numeric types. return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src); } - if (is_equality_cmp and lhs_ty.zigTypeTag() == .ErrorUnion and rhs_ty.zigTypeTag() == .ErrorSet) { + if (is_equality_cmp and lhs_ty.zigTypeTag(mod) == .ErrorUnion and rhs_ty.zigTypeTag(mod) == .ErrorSet) { const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs); return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src); } - if (is_equality_cmp and lhs_ty.zigTypeTag() == .ErrorSet and rhs_ty.zigTypeTag() == .ErrorUnion) { + if (is_equality_cmp and lhs_ty.zigTypeTag(mod) == .ErrorSet and rhs_ty.zigTypeTag(mod) == .ErrorUnion) { const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs); return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src); } const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); - if (!resolved_type.isSelfComparable(is_equality_cmp)) { + if (!resolved_type.isSelfComparable(mod, is_equality_cmp)) { return sema.fail(block, src, "operator {s} not allowed for type '{}'", .{ - compareOperatorName(op), resolved_type.fmt(sema.mod), + compareOperatorName(op), resolved_type.fmt(mod), }); } const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); @@ -15299,15 +16039,19 @@ fn cmpSelf( lhs_src: LazySrcLoc, rhs_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const resolved_type = sema.typeOf(casted_lhs); const runtime_src: LazySrcLoc = src: { if (try sema.resolveMaybeUndefVal(casted_lhs)) |lhs_val| { - if (lhs_val.isUndef()) return sema.addConstUndef(Type.bool); + if (lhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool); if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| { - if (rhs_val.isUndef()) return sema.addConstUndef(Type.bool); + if (rhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool); - if (resolved_type.zigTypeTag() == .Vector) { - const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.bool); + if (resolved_type.zigTypeTag(mod) == .Vector) { + const result_ty = try mod.vectorType(.{ + .len = resolved_type.vectorLen(mod), + .child = .bool_type, + }); const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type); return sema.addConstant(result_ty, cmp_val); } @@ -15318,7 +16062,7 @@ fn cmpSelf( return Air.Inst.Ref.bool_false; } } else { - if (resolved_type.zigTypeTag() == .Bool) { + if (resolved_type.zigTypeTag(mod) == .Bool) { // We can lower bool eq/neq more efficiently. return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src); } @@ -15327,9 +16071,9 @@ fn cmpSelf( } else { // For bools, we still check the other operand, because we can lower // bool eq/neq more efficiently. - if (resolved_type.zigTypeTag() == .Bool) { + if (resolved_type.zigTypeTag(mod) == .Bool) { if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| { - if (rhs_val.isUndef()) return sema.addConstUndef(Type.bool); + if (rhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool); return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src); } } @@ -15337,7 +16081,7 @@ fn cmpSelf( } }; try sema.requireRuntimeBlock(block, src, runtime_src); - if (resolved_type.zigTypeTag() == .Vector) { + if (resolved_type.zigTypeTag(mod) == .Vector) { return block.addCmpVector(casted_lhs, casted_rhs, op); } const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized); @@ -15366,16 +16110,17 @@ fn runtimeBoolCmp( } fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ty = try sema.resolveType(block, operand_src, inst_data.operand); - switch (ty.zigTypeTag()) { + switch (ty.zigTypeTag(mod)) { .Fn, .NoReturn, .Undefined, .Null, .Opaque, - => return sema.fail(block, operand_src, "no size available for type '{}'", .{ty.fmt(sema.mod)}), + => return sema.fail(block, operand_src, "no size available for type '{}'", .{ty.fmt(mod)}), .Type, .EnumLiteral, @@ -15400,25 +16145,25 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. .AnyFrame, => {}, } - const target = sema.mod.getTarget(); - const val = try ty.lazyAbiSize(target, sema.arena); - if (val.tag() == .lazy_size) { + const val = try ty.lazyAbiSize(mod); + if (val.isLazySize(mod)) { try sema.queueFullTypeResolution(ty); } return sema.addConstant(Type.comptime_int, val); } fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand); - switch (operand_ty.zigTypeTag()) { + switch (operand_ty.zigTypeTag(mod)) { .Fn, .NoReturn, .Undefined, .Null, .Opaque, - => return sema.fail(block, operand_src, "no size available for type '{}'", .{operand_ty.fmt(sema.mod)}), + => return sema.fail(block, operand_src, "no size available for type '{}'", .{operand_ty.fmt(mod)}), .Type, .EnumLiteral, @@ -15443,8 +16188,7 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A .AnyFrame, => {}, } - const target = sema.mod.getTarget(); - const bit_size = try operand_ty.bitSizeAdvanced(target, sema); + const bit_size = try operand_ty.bitSizeAdvanced(mod, sema); return sema.addIntUnsigned(Type.comptime_int, bit_size); } @@ -15453,17 +16197,13 @@ fn zirThis( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { - const this_decl_index = block.namespace.getDeclIndex(); + const mod = sema.mod; + const this_decl_index = mod.namespaceDeclIndex(block.namespace); const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); return sema.analyzeDeclVal(block, src, this_decl_index); } -fn zirClosureCapture( - sema: *Sema, - block: *Block, - inst: Zir.Inst.Index, -) CompileError!void { - // TODO: Compile error when closed over values are modified +fn zirClosureCapture(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const inst_data = sema.code.instructions.items(.data)[inst].un_tok; // Closures are not necessarily constant values. For example, the // code might do something like this: @@ -15471,26 +16211,24 @@ fn zirClosureCapture( // ...in which case the closure_capture instruction has access to a runtime // value only. In such case we preserve the type and use a dummy runtime value. const operand = try sema.resolveInst(inst_data.operand); - const val = (try sema.resolveMaybeUndefValAllowVariables(operand)) orelse - Value.initTag(.unreachable_value); - - try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, .{ - .ty = try sema.typeOf(operand).copy(sema.perm_arena), - .val = try val.copy(sema.perm_arena), - }); + const ty = sema.typeOf(operand); + const capture: CaptureScope.Capture = blk: { + if (try sema.resolveMaybeUndefValAllowVariables(operand)) |val| { + const ip_index = try val.intern(ty, sema.mod); + break :blk .{ .comptime_val = ip_index }; + } + break :blk .{ .runtime_val = ty.toIntern() }; + }; + try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, capture); } -fn zirClosureGet( - sema: *Sema, - block: *Block, - inst: Zir.Inst.Index, -) CompileError!Air.Inst.Ref { - // TODO CLOSURE: Test this with inline functions +fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].inst_node; - var scope: *CaptureScope = sema.mod.declPtr(block.src_decl).src_scope.?; + var scope: *CaptureScope = mod.declPtr(block.src_decl).src_scope.?; // Note: The target closure must be in this scope list. // If it's not here, the zir is invalid, or the list is broken. - const tv = while (true) { + const capture = while (true) { // Note: We don't need to add a dependency here, because // decls always depend on their lexical parents. @@ -15503,17 +16241,17 @@ fn zirClosureGet( } return error.AnalysisFail; } - if (scope.captures.getPtr(inst_data.inst)) |tv| { - break tv; + if (scope.captures.get(inst_data.inst)) |capture| { + break capture; } scope = scope.parent.?; }; - if (tv.val.tag() == .unreachable_value and !block.is_typeof and sema.func == null) { + if (capture == .runtime_val and !block.is_typeof and sema.func_index == .none) { const msg = msg: { const name = name: { - const file = sema.owner_decl.getFileScope(); - const tree = file.getTree(sema.mod.gpa) catch |err| { + const file = sema.owner_decl.getFileScope(mod); + const tree = file.getTree(sema.gpa) catch |err| { // In this case we emit a warning + a less precise source location. log.warn("unable to load {s}: {s}", .{ file.sub_file_path, @errorName(err), @@ -15537,11 +16275,11 @@ fn zirClosureGet( return sema.failWithOwnedErrorMsg(msg); } - if (tv.val.tag() == .unreachable_value and !block.is_typeof and !block.is_comptime and sema.func != null) { + if (capture == .runtime_val and !block.is_typeof and !block.is_comptime and sema.func_index != .none) { const msg = msg: { const name = name: { - const file = sema.owner_decl.getFileScope(); - const tree = file.getTree(sema.mod.gpa) catch |err| { + const file = sema.owner_decl.getFileScope(mod); + const tree = file.getTree(sema.gpa) catch |err| { // In this case we emit a warning + a less precise source location. log.warn("unable to load {s}: {s}", .{ file.sub_file_path, @errorName(err), @@ -15567,13 +16305,17 @@ fn zirClosureGet( return sema.failWithOwnedErrorMsg(msg); } - if (tv.val.tag() == .unreachable_value) { - assert(block.is_typeof); - // We need a dummy runtime instruction with the correct type. - return block.addTy(.alloc, tv.ty); + switch (capture) { + .runtime_val => |ty_ip_index| { + assert(block.is_typeof); + // We need a dummy runtime instruction with the correct type. + return block.addTy(.alloc, ty_ip_index.toType()); + }, + .comptime_val => |val_ip_index| { + const ty = mod.intern_pool.typeOf(val_ip_index).toType(); + return sema.addConstant(ty, val_ip_index.toValue()); + }, } - - return sema.addConstant(tv.ty, tv.val); } fn zirRetAddr( @@ -15608,345 +16350,422 @@ fn zirBuiltinSrc( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); const func = sema.func orelse return sema.fail(block, src, "@src outside function", .{}); - const fn_owner_decl = sema.mod.declPtr(func.owner_decl); + const fn_owner_decl = mod.declPtr(func.owner_decl); const func_name_val = blk: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); - const name = std.mem.span(fn_owner_decl.name); - const bytes = try anon_decl.arena().dupe(u8, name[0 .. name.len + 1]); + // TODO: write something like getCoercedInts to avoid needing to dupe + const name = try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(fn_owner_decl.name)); + const new_decl_ty = try mod.arrayType(.{ + .len = name.len, + .child = .u8_type, + .sentinel = .zero_u8, + }); const new_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len - 1), - try Value.Tag.bytes.create(anon_decl.arena(), bytes), + new_decl_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = new_decl_ty.toIntern(), + .storage = .{ .bytes = name }, + } })).toValue(), 0, // default alignment ); - break :blk try Value.Tag.decl_ref.create(sema.arena, new_decl); + break :blk try mod.intern(.{ .ptr = .{ + .ty = .slice_const_u8_sentinel_0_type, + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + } }); }; const file_name_val = blk: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); // The compiler must not call realpath anywhere. - const name = try fn_owner_decl.getFileScope().fullPathZ(anon_decl.arena()); + const name = try fn_owner_decl.getFileScope(mod).fullPathZ(sema.arena); + const new_decl_ty = try mod.arrayType(.{ + .len = name.len, + .child = .u8_type, + .sentinel = .zero_u8, + }); const new_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), name.len), - try Value.Tag.bytes.create(anon_decl.arena(), name[0 .. name.len + 1]), + new_decl_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = new_decl_ty.toIntern(), + .storage = .{ .bytes = name }, + } })).toValue(), 0, // default alignment ); - break :blk try Value.Tag.decl_ref.create(sema.arena, new_decl); + break :blk try mod.intern(.{ .ptr = .{ + .ty = .slice_const_u8_sentinel_0_type, + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + } }); }; - const field_values = try sema.arena.alloc(Value, 4); - // file: [:0]const u8, - field_values[0] = file_name_val; - // fn_name: [:0]const u8, - field_values[1] = func_name_val; - // line: u32 - field_values[2] = try Value.Tag.runtime_value.create(sema.arena, try Value.Tag.int_u64.create(sema.arena, extra.line + 1)); - // column: u32, - field_values[3] = try Value.Tag.int_u64.create(sema.arena, extra.column + 1); - - return sema.addConstant( - try sema.getBuiltinType("SourceLocation"), - try Value.Tag.aggregate.create(sema.arena, field_values), - ); + const src_loc_ty = try sema.getBuiltinType("SourceLocation"); + const fields = .{ + // file: [:0]const u8, + file_name_val, + // fn_name: [:0]const u8, + func_name_val, + // line: u32, + try mod.intern(.{ .runtime_value = .{ + .ty = .u32_type, + .val = (try mod.intValue(Type.u32, extra.line + 1)).toIntern(), + } }), + // column: u32, + (try mod.intValue(Type.u32, extra.column + 1)).toIntern(), + }; + return sema.addConstant(src_loc_ty, (try mod.intern(.{ .aggregate = .{ + .ty = src_loc_ty.toIntern(), + .storage = .{ .elems = &fields }, + } })).toValue()); } fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const ty = try sema.resolveType(block, src, inst_data.operand); const type_info_ty = try sema.getBuiltinType("Type"); - const target = sema.mod.getTarget(); + const type_info_tag_ty = type_info_ty.unionTagType(mod).?; - switch (ty.zigTypeTag()) { - .Type => return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Type)), - .val = Value.void, - }), - ), - .Void => return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Void)), - .val = Value.void, - }), - ), - .Bool => return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Bool)), - .val = Value.void, - }), - ), - .NoReturn => return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.NoReturn)), - .val = Value.void, - }), - ), - .ComptimeFloat => return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeFloat)), - .val = Value.void, - }), - ), - .ComptimeInt => return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeInt)), - .val = Value.void, - }), - ), - .Undefined => return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Undefined)), - .val = Value.void, - }), - ), - .Null => return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Null)), - .val = Value.void, - }), - ), - .EnumLiteral => return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.EnumLiteral)), - .val = Value.void, - }), - ), + switch (ty.zigTypeTag(mod)) { + .Type, + .Void, + .Bool, + .NoReturn, + .ComptimeFloat, + .ComptimeInt, + .Undefined, + .Null, + .EnumLiteral, + => |type_info_tag| return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(type_info_tag))).toIntern(), + .val = .void_value, + } })).toValue()), .Fn => { // TODO: look into memoizing this result. - const info = ty.fnInfo(); - var params_anon_decl = try block.startAnonDecl(); defer params_anon_decl.deinit(); - const param_vals = try params_anon_decl.arena().alloc(Value, info.param_types.len); + const fn_info_decl_index = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Fn"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, fn_info_decl_index); + try sema.ensureDeclAnalyzed(fn_info_decl_index); + const fn_info_decl = mod.declPtr(fn_info_decl_index); + const fn_info_ty = fn_info_decl.val.toType(); + + const param_info_decl_index = (try sema.namespaceLookup( + block, + src, + fn_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Param"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, param_info_decl_index); + try sema.ensureDeclAnalyzed(param_info_decl_index); + const param_info_decl = mod.declPtr(param_info_decl_index); + const param_info_ty = param_info_decl.val.toType(); + + const param_vals = try sema.arena.alloc(InternPool.Index, mod.typeToFunc(ty).?.param_types.len); for (param_vals, 0..) |*param_val, i| { + const info = mod.typeToFunc(ty).?; const param_ty = info.param_types[i]; - const is_generic = param_ty.tag() == .generic_poison; - const param_ty_val = if (is_generic) - Value.null - else - try Value.Tag.opt_payload.create( - params_anon_decl.arena(), - try Value.Tag.ty.create(params_anon_decl.arena(), try param_ty.copy(params_anon_decl.arena())), - ); + const is_generic = param_ty == .generic_poison_type; + const param_ty_val = try ip.get(gpa, .{ .opt = .{ + .ty = try ip.get(gpa, .{ .opt_type = .type_type }), + .val = if (is_generic) .none else param_ty, + } }); const is_noalias = blk: { const index = std.math.cast(u5, i) orelse break :blk false; break :blk @truncate(u1, info.noalias_bits >> index) != 0; }; - const param_fields = try params_anon_decl.arena().create([3]Value); - param_fields.* = .{ + const param_fields = .{ // is_generic: bool, - Value.makeBool(is_generic), + Value.makeBool(is_generic).toIntern(), // is_noalias: bool, - Value.makeBool(is_noalias), + Value.makeBool(is_noalias).toIntern(), // type: ?type, param_ty_val, }; - param_val.* = try Value.Tag.aggregate.create(params_anon_decl.arena(), param_fields); + param_val.* = try mod.intern(.{ .aggregate = .{ + .ty = param_info_ty.toIntern(), + .storage = .{ .elems = ¶m_fields }, + } }); } const args_val = v: { - const fn_info_decl_index = (try sema.namespaceLookup( - block, - src, - type_info_ty.getNamespace().?, - "Fn", - )).?; - try sema.mod.declareDeclDependency(sema.owner_decl_index, fn_info_decl_index); - try sema.ensureDeclAnalyzed(fn_info_decl_index); - const fn_info_decl = sema.mod.declPtr(fn_info_decl_index); - var fn_ty_buffer: Value.ToTypeBuffer = undefined; - const fn_ty = fn_info_decl.val.toType(&fn_ty_buffer); - const param_info_decl_index = (try sema.namespaceLookup( - block, - src, - fn_ty.getNamespace().?, - "Param", - )).?; - try sema.mod.declareDeclDependency(sema.owner_decl_index, param_info_decl_index); - try sema.ensureDeclAnalyzed(param_info_decl_index); - const param_info_decl = sema.mod.declPtr(param_info_decl_index); - var param_buffer: Value.ToTypeBuffer = undefined; - const param_ty = param_info_decl.val.toType(¶m_buffer); + const new_decl_ty = try mod.arrayType(.{ + .len = param_vals.len, + .child = param_info_ty.toIntern(), + }); const new_decl = try params_anon_decl.finish( - try Type.Tag.array.create(params_anon_decl.arena(), .{ - .len = param_vals.len, - .elem_type = try param_ty.copy(params_anon_decl.arena()), - }), - try Value.Tag.aggregate.create( - params_anon_decl.arena(), - param_vals, - ), + new_decl_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = new_decl_ty.toIntern(), + .storage = .{ .elems = param_vals }, + } })).toValue(), 0, // default alignment ); - break :v try Value.Tag.slice.create(sema.arena, .{ - .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), - .len = try Value.Tag.int_u64.create(sema.arena, param_vals.len), - }); + break :v try mod.intern(.{ .ptr = .{ + .ty = (try mod.ptrType(.{ + .child = param_info_ty.toIntern(), + .flags = .{ + .size = .Slice, + .is_const = true, + }, + })).toIntern(), + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, param_vals.len)).toIntern(), + } }); }; - const ret_ty_opt = if (info.return_type.tag() != .generic_poison) - try Value.Tag.opt_payload.create( - sema.arena, - try Value.Tag.ty.create(sema.arena, info.return_type), - ) - else - Value.null; + const info = mod.typeToFunc(ty).?; + const ret_ty_opt = try mod.intern(.{ .opt = .{ + .ty = try ip.get(gpa, .{ .opt_type = .type_type }), + .val = if (info.return_type == .generic_poison_type) .none else info.return_type, + } }); + + const callconv_ty = try sema.getBuiltinType("CallingConvention"); - const field_values = try sema.arena.create([6]Value); - field_values.* = .{ + const field_values = .{ // calling_convention: CallingConvention, - try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.cc)), + (try mod.enumValueFieldIndex(callconv_ty, @enumToInt(info.cc))).toIntern(), // alignment: comptime_int, - try Value.Tag.int_u64.create(sema.arena, ty.abiAlignment(target)), + (try mod.intValue(Type.comptime_int, ty.abiAlignment(mod))).toIntern(), // is_generic: bool, - Value.makeBool(info.is_generic), + Value.makeBool(info.is_generic).toIntern(), // is_var_args: bool, - Value.makeBool(info.is_var_args), + Value.makeBool(info.is_var_args).toIntern(), // return_type: ?type, ret_ty_opt, // args: []const Fn.Param, args_val, }; - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Fn)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.Fn))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = fn_info_ty.toIntern(), + .storage = .{ .elems = &field_values }, + } }), + } })).toValue()); }, .Int => { - const info = ty.intInfo(target); - const field_values = try sema.arena.alloc(Value, 2); - // signedness: Signedness, - field_values[0] = try Value.Tag.enum_field_index.create( - sema.arena, - @enumToInt(info.signedness), - ); - // bits: comptime_int, - field_values[1] = try Value.Tag.int_u64.create(sema.arena, info.bits); - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Int)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + const int_info_decl_index = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Int"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, int_info_decl_index); + try sema.ensureDeclAnalyzed(int_info_decl_index); + const int_info_decl = mod.declPtr(int_info_decl_index); + const int_info_ty = int_info_decl.val.toType(); + + const signedness_ty = try sema.getBuiltinType("Signedness"); + const info = ty.intInfo(mod); + const field_values = .{ + // signedness: Signedness, + try (try mod.enumValueFieldIndex(signedness_ty, @enumToInt(info.signedness))).intern(signedness_ty, mod), + // bits: u16, + (try mod.intValue(Type.u16, info.bits)).toIntern(), + }; + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.Int))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = int_info_ty.toIntern(), + .storage = .{ .elems = &field_values }, + } }), + } })).toValue()); }, .Float => { - const field_values = try sema.arena.alloc(Value, 1); - // bits: comptime_int, - field_values[0] = try Value.Tag.int_u64.create(sema.arena, ty.bitSize(target)); - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Float)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + const float_info_decl_index = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Float"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, float_info_decl_index); + try sema.ensureDeclAnalyzed(float_info_decl_index); + const float_info_decl = mod.declPtr(float_info_decl_index); + const float_info_ty = float_info_decl.val.toType(); + + const field_vals = .{ + // bits: u16, + (try mod.intValue(Type.u16, ty.bitSize(mod))).toIntern(), + }; + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.Float))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = float_info_ty.toIntern(), + .storage = .{ .elems = &field_vals }, + } }), + } })).toValue()); }, .Pointer => { - const info = ty.ptrInfo().data; + const info = ty.ptrInfo(mod); const alignment = if (info.@"align" != 0) - try Value.Tag.int_u64.create(sema.arena, info.@"align") + try mod.intValue(Type.comptime_int, info.@"align") else - try info.pointee_type.lazyAbiAlignment(target, sema.arena); + try info.pointee_type.lazyAbiAlignment(mod); - const field_values = try sema.arena.create([8]Value); - field_values.* = .{ + const addrspace_ty = try sema.getBuiltinType("AddressSpace"); + const pointer_ty = t: { + const decl_index = (try sema.namespaceLookup( + block, + src, + (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Pointer"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, decl_index); + try sema.ensureDeclAnalyzed(decl_index); + const decl = mod.declPtr(decl_index); + break :t decl.val.toType(); + }; + const ptr_size_ty = t: { + const decl_index = (try sema.namespaceLookup( + block, + src, + pointer_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Size"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, decl_index); + try sema.ensureDeclAnalyzed(decl_index); + const decl = mod.declPtr(decl_index); + break :t decl.val.toType(); + }; + + const field_values = .{ // size: Size, - try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.size)), + try (try mod.enumValueFieldIndex(ptr_size_ty, @enumToInt(info.size))).intern(ptr_size_ty, mod), // is_const: bool, - Value.makeBool(!info.mutable), + Value.makeBool(!info.mutable).toIntern(), // is_volatile: bool, - Value.makeBool(info.@"volatile"), + Value.makeBool(info.@"volatile").toIntern(), // alignment: comptime_int, - alignment, + alignment.toIntern(), // address_space: AddressSpace - try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.@"addrspace")), + try (try mod.enumValueFieldIndex(addrspace_ty, @enumToInt(info.@"addrspace"))).intern(addrspace_ty, mod), // child: type, - try Value.Tag.ty.create(sema.arena, info.pointee_type), + info.pointee_type.toIntern(), // is_allowzero: bool, - Value.makeBool(info.@"allowzero"), + Value.makeBool(info.@"allowzero").toIntern(), // sentinel: ?*const anyopaque, - try sema.optRefValue(block, info.pointee_type, info.sentinel), + (try sema.optRefValue(block, info.pointee_type, info.sentinel)).toIntern(), }; - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Pointer)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.Pointer))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = pointer_ty.toIntern(), + .storage = .{ .elems = &field_values }, + } }), + } })).toValue()); }, .Array => { - const info = ty.arrayInfo(); - const field_values = try sema.arena.alloc(Value, 3); - // len: comptime_int, - field_values[0] = try Value.Tag.int_u64.create(sema.arena, info.len); - // child: type, - field_values[1] = try Value.Tag.ty.create(sema.arena, info.elem_type); - // sentinel: ?*const anyopaque, - field_values[2] = try sema.optRefValue(block, info.elem_type, info.sentinel); - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Array)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + const array_field_ty = t: { + const array_field_ty_decl_index = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Array"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, array_field_ty_decl_index); + try sema.ensureDeclAnalyzed(array_field_ty_decl_index); + const array_field_ty_decl = mod.declPtr(array_field_ty_decl_index); + break :t array_field_ty_decl.val.toType(); + }; + + const info = ty.arrayInfo(mod); + const field_values = .{ + // len: comptime_int, + (try mod.intValue(Type.comptime_int, info.len)).toIntern(), + // child: type, + info.elem_type.toIntern(), + // sentinel: ?*const anyopaque, + (try sema.optRefValue(block, info.elem_type, info.sentinel)).toIntern(), + }; + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.Array))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = array_field_ty.toIntern(), + .storage = .{ .elems = &field_values }, + } }), + } })).toValue()); }, .Vector => { - const info = ty.arrayInfo(); - const field_values = try sema.arena.alloc(Value, 2); - // len: comptime_int, - field_values[0] = try Value.Tag.int_u64.create(sema.arena, info.len); - // child: type, - field_values[1] = try Value.Tag.ty.create(sema.arena, info.elem_type); - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Vector)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + const vector_field_ty = t: { + const vector_field_ty_decl_index = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Vector"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, vector_field_ty_decl_index); + try sema.ensureDeclAnalyzed(vector_field_ty_decl_index); + const vector_field_ty_decl = mod.declPtr(vector_field_ty_decl_index); + break :t vector_field_ty_decl.val.toType(); + }; + + const info = ty.arrayInfo(mod); + const field_values = .{ + // len: comptime_int, + (try mod.intValue(Type.comptime_int, info.len)).toIntern(), + // child: type, + info.elem_type.toIntern(), + }; + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.Vector))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = vector_field_ty.toIntern(), + .storage = .{ .elems = &field_values }, + } }), + } })).toValue()); }, .Optional => { - const field_values = try sema.arena.alloc(Value, 1); - // child: type, - field_values[0] = try Value.Tag.ty.create(sema.arena, try ty.optionalChildAlloc(sema.arena)); - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Optional)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + const optional_field_ty = t: { + const optional_field_ty_decl_index = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Optional"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, optional_field_ty_decl_index); + try sema.ensureDeclAnalyzed(optional_field_ty_decl_index); + const optional_field_ty_decl = mod.declPtr(optional_field_ty_decl_index); + break :t optional_field_ty_decl.val.toType(); + }; + + const field_values = .{ + // child: type, + ty.optionalChild(mod).toIntern(), + }; + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.Optional))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = optional_field_ty.toIntern(), + .storage = .{ .elems = &field_values }, + } }), + } })).toValue()); }, .ErrorSet => { var fields_anon_decl = try block.startAnonDecl(); @@ -15957,17 +16776,16 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const set_field_ty_decl_index = (try sema.namespaceLookup( block, src, - type_info_ty.getNamespace().?, - "Error", + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Error"), )).?; - try sema.mod.declareDeclDependency(sema.owner_decl_index, set_field_ty_decl_index); + try mod.declareDeclDependency(sema.owner_decl_index, set_field_ty_decl_index); try sema.ensureDeclAnalyzed(set_field_ty_decl_index); - const set_field_ty_decl = sema.mod.declPtr(set_field_ty_decl_index); - var buffer: Value.ToTypeBuffer = undefined; - break :t try set_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); + const set_field_ty_decl = mod.declPtr(set_field_ty_decl_index); + break :t set_field_ty_decl.val.toType(); }; - try sema.queueFullTypeResolution(try error_field_ty.copy(sema.arena)); + try sema.queueFullTypeResolution(error_field_ty); // If the error set is inferred it must be resolved at this point try sema.resolveInferredErrorSetTy(block, src, ty); @@ -15975,90 +16793,119 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // Build our list of Error values // Optional value is only null if anyerror // Value can be zero-length slice otherwise - const error_field_vals: ?[]Value = if (ty.isAnyError()) null else blk: { - const names = ty.errorSetNames(); - const vals = try fields_anon_decl.arena().alloc(Value, names.len); + const error_field_vals = if (ty.isAnyError(mod)) null else blk: { + const vals = try sema.arena.alloc(InternPool.Index, ty.errorSetNames(mod).len); for (vals, 0..) |*field_val, i| { - const name = names[i]; + // TODO: write something like getCoercedInts to avoid needing to dupe + const name = try sema.arena.dupe(u8, ip.stringToSlice(ty.errorSetNames(mod)[i])); const name_val = v: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); - const bytes = try anon_decl.arena().dupeZ(u8, name); + const new_decl_ty = try mod.arrayType(.{ + .len = name.len, + .child = .u8_type, + }); const new_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), - try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + new_decl_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = new_decl_ty.toIntern(), + .storage = .{ .bytes = name }, + } })).toValue(), 0, // default alignment ); - break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl); + break :v try mod.intern(.{ .ptr = .{ + .ty = .slice_const_u8_type, + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + } }); }; - const error_field_fields = try fields_anon_decl.arena().create([1]Value); - error_field_fields.* = .{ + const error_field_fields = .{ // name: []const u8, name_val, }; - - field_val.* = try Value.Tag.aggregate.create( - fields_anon_decl.arena(), - error_field_fields, - ); + field_val.* = try mod.intern(.{ .aggregate = .{ + .ty = error_field_ty.toIntern(), + .storage = .{ .elems = &error_field_fields }, + } }); } break :blk vals; }; // Build our ?[]const Error value - const errors_val = if (error_field_vals) |vals| v: { + const slice_errors_ty = try mod.ptrType(.{ + .child = error_field_ty.toIntern(), + .flags = .{ + .size = .Slice, + .is_const = true, + }, + }); + const opt_slice_errors_ty = try mod.optionalType(slice_errors_ty.toIntern()); + const errors_payload_val: InternPool.Index = if (error_field_vals) |vals| v: { + const array_errors_ty = try mod.arrayType(.{ + .len = vals.len, + .child = error_field_ty.toIntern(), + .sentinel = .none, + }); const new_decl = try fields_anon_decl.finish( - try Type.Tag.array.create(fields_anon_decl.arena(), .{ - .len = vals.len, - .elem_type = error_field_ty, - }), - try Value.Tag.aggregate.create( - fields_anon_decl.arena(), - vals, - ), + array_errors_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = array_errors_ty.toIntern(), + .storage = .{ .elems = vals }, + } })).toValue(), 0, // default alignment ); - - const new_decl_val = try Value.Tag.decl_ref.create(sema.arena, new_decl); - const slice_val = try Value.Tag.slice.create(sema.arena, .{ - .ptr = new_decl_val, - .len = try Value.Tag.int_u64.create(sema.arena, vals.len), - }); - break :v try Value.Tag.opt_payload.create(sema.arena, slice_val); - } else Value.null; + break :v try mod.intern(.{ .ptr = .{ + .ty = slice_errors_ty.toIntern(), + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, vals.len)).toIntern(), + } }); + } else .none; + const errors_val = try mod.intern(.{ .opt = .{ + .ty = opt_slice_errors_ty.toIntern(), + .val = errors_payload_val, + } }); // Construct Type{ .ErrorSet = errors_val } - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ErrorSet)), - .val = errors_val, - }), - ); + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.ErrorSet))).toIntern(), + .val = errors_val, + } })).toValue()); }, .ErrorUnion => { - const field_values = try sema.arena.alloc(Value, 2); - // error_set: type, - field_values[0] = try Value.Tag.ty.create(sema.arena, ty.errorUnionSet()); - // payload: type, - field_values[1] = try Value.Tag.ty.create(sema.arena, ty.errorUnionPayload()); - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ErrorUnion)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + const error_union_field_ty = t: { + const error_union_field_ty_decl_index = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "ErrorUnion"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, error_union_field_ty_decl_index); + try sema.ensureDeclAnalyzed(error_union_field_ty_decl_index); + const error_union_field_ty_decl = mod.declPtr(error_union_field_ty_decl_index); + break :t error_union_field_ty_decl.val.toType(); + }; + + const field_values = .{ + // error_set: type, + ty.errorUnionSet(mod).toIntern(), + // payload: type, + ty.errorUnionPayload(mod).toIntern(), + }; + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.ErrorUnion))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = error_union_field_ty.toIntern(), + .storage = .{ .elems = &field_values }, + } }), + } })).toValue()); }, .Enum => { // TODO: look into memoizing this result. - var int_tag_type_buffer: Type.Payload.Bits = undefined; - const int_tag_ty = try ty.intTagType(&int_tag_type_buffer).copy(sema.arena); - - const is_exhaustive = Value.makeBool(!ty.isNonexhaustiveEnum()); + const is_exhaustive = Value.makeBool(ip.indexToKey(ty.toIntern()).enum_type.tag_mode != .nonexhaustive); var fields_anon_decl = try block.startAnonDecl(); defer fields_anon_decl.deinit(); @@ -16067,88 +16914,121 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const enum_field_ty_decl_index = (try sema.namespaceLookup( block, src, - type_info_ty.getNamespace().?, - "EnumField", + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "EnumField"), )).?; - try sema.mod.declareDeclDependency(sema.owner_decl_index, enum_field_ty_decl_index); + try mod.declareDeclDependency(sema.owner_decl_index, enum_field_ty_decl_index); try sema.ensureDeclAnalyzed(enum_field_ty_decl_index); - const enum_field_ty_decl = sema.mod.declPtr(enum_field_ty_decl_index); - var buffer: Value.ToTypeBuffer = undefined; - break :t try enum_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); + const enum_field_ty_decl = mod.declPtr(enum_field_ty_decl_index); + break :t enum_field_ty_decl.val.toType(); }; - const enum_fields = ty.enumFields(); - const enum_field_vals = try fields_anon_decl.arena().alloc(Value, enum_fields.count()); - + const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.indexToKey(ty.toIntern()).enum_type.names.len); for (enum_field_vals, 0..) |*field_val, i| { - var tag_val_payload: Value.Payload.U32 = .{ - .base = .{ .tag = .enum_field_index }, - .data = @intCast(u32, i), - }; - const tag_val = Value.initPayload(&tag_val_payload.base); - - var buffer: Value.Payload.U64 = undefined; - const int_val = try tag_val.enumToInt(ty, &buffer).copy(fields_anon_decl.arena()); - - const name = enum_fields.keys()[i]; + const enum_type = ip.indexToKey(ty.toIntern()).enum_type; + const value_val = if (enum_type.values.len > 0) + try mod.intern_pool.getCoerced(gpa, enum_type.values[i], .comptime_int_type) + else + try mod.intern(.{ .int = .{ + .ty = .comptime_int_type, + .storage = .{ .u64 = @intCast(u64, i) }, + } }); + // TODO: write something like getCoercedInts to avoid needing to dupe + const name = try sema.arena.dupe(u8, ip.stringToSlice(enum_type.names[i])); const name_val = v: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); - const bytes = try anon_decl.arena().dupeZ(u8, name); + const new_decl_ty = try mod.arrayType(.{ + .len = name.len, + .child = .u8_type, + }); const new_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), - try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + new_decl_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = new_decl_ty.toIntern(), + .storage = .{ .bytes = name }, + } })).toValue(), 0, // default alignment ); - break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl); + break :v try mod.intern(.{ .ptr = .{ + .ty = .slice_const_u8_type, + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + } }); }; - const enum_field_fields = try fields_anon_decl.arena().create([2]Value); - enum_field_fields.* = .{ + const enum_field_fields = .{ // name: []const u8, name_val, // value: comptime_int, - int_val, + value_val, }; - field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), enum_field_fields); + field_val.* = try mod.intern(.{ .aggregate = .{ + .ty = enum_field_ty.toIntern(), + .storage = .{ .elems = &enum_field_fields }, + } }); } const fields_val = v: { + const fields_array_ty = try mod.arrayType(.{ + .len = enum_field_vals.len, + .child = enum_field_ty.toIntern(), + .sentinel = .none, + }); const new_decl = try fields_anon_decl.finish( - try Type.Tag.array.create(fields_anon_decl.arena(), .{ - .len = enum_field_vals.len, - .elem_type = enum_field_ty, - }), - try Value.Tag.aggregate.create( - fields_anon_decl.arena(), - enum_field_vals, - ), + fields_array_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = fields_array_ty.toIntern(), + .storage = .{ .elems = enum_field_vals }, + } })).toValue(), 0, // default alignment ); - break :v try Value.Tag.decl_ref.create(sema.arena, new_decl); + break :v try mod.intern(.{ .ptr = .{ + .ty = (try mod.ptrType(.{ + .child = enum_field_ty.toIntern(), + .flags = .{ + .size = .Slice, + .is_const = true, + }, + })).toIntern(), + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, enum_field_vals.len)).toIntern(), + } }); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespace()); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ip.indexToKey(ty.toIntern()).enum_type.namespace); + + const type_enum_ty = t: { + const type_enum_ty_decl_index = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Enum"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, type_enum_ty_decl_index); + try sema.ensureDeclAnalyzed(type_enum_ty_decl_index); + const type_enum_ty_decl = mod.declPtr(type_enum_ty_decl_index); + break :t type_enum_ty_decl.val.toType(); + }; - const field_values = try sema.arena.create([4]Value); - field_values.* = .{ + const field_values = .{ // tag_type: type, - try Value.Tag.ty.create(sema.arena, int_tag_ty), + ip.indexToKey(ty.toIntern()).enum_type.tag_ty, // fields: []const EnumField, fields_val, // decls: []const Declaration, decls_val, // is_exhaustive: bool, - is_exhaustive, + is_exhaustive.toIntern(), }; - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Enum)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.Enum))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = type_enum_ty.toIntern(), + .storage = .{ .elems = &field_values }, + } }), + } })).toValue()); }, .Union => { // TODO: look into memoizing this result. @@ -16156,91 +17036,135 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai var fields_anon_decl = try block.startAnonDecl(); defer fields_anon_decl.deinit(); + const type_union_ty = t: { + const type_union_ty_decl_index = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Union"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, type_union_ty_decl_index); + try sema.ensureDeclAnalyzed(type_union_ty_decl_index); + const type_union_ty_decl = mod.declPtr(type_union_ty_decl_index); + break :t type_union_ty_decl.val.toType(); + }; + const union_field_ty = t: { const union_field_ty_decl_index = (try sema.namespaceLookup( block, src, - type_info_ty.getNamespace().?, - "UnionField", + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "UnionField"), )).?; - try sema.mod.declareDeclDependency(sema.owner_decl_index, union_field_ty_decl_index); + try mod.declareDeclDependency(sema.owner_decl_index, union_field_ty_decl_index); try sema.ensureDeclAnalyzed(union_field_ty_decl_index); - const union_field_ty_decl = sema.mod.declPtr(union_field_ty_decl_index); - var buffer: Value.ToTypeBuffer = undefined; - break :t try union_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); + const union_field_ty_decl = mod.declPtr(union_field_ty_decl_index); + break :t union_field_ty_decl.val.toType(); }; const union_ty = try sema.resolveTypeFields(ty); try sema.resolveTypeLayout(ty); // Getting alignment requires type layout - const layout = union_ty.containerLayout(); + const layout = union_ty.containerLayout(mod); - const union_fields = union_ty.unionFields(); - const union_field_vals = try fields_anon_decl.arena().alloc(Value, union_fields.count()); + const union_fields = union_ty.unionFields(mod); + const union_field_vals = try gpa.alloc(InternPool.Index, union_fields.count()); + defer gpa.free(union_field_vals); for (union_field_vals, 0..) |*field_val, i| { const field = union_fields.values()[i]; - const name = union_fields.keys()[i]; + // TODO: write something like getCoercedInts to avoid needing to dupe + const name = try sema.arena.dupe(u8, ip.stringToSlice(union_fields.keys()[i])); const name_val = v: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); - const bytes = try anon_decl.arena().dupeZ(u8, name); + const new_decl_ty = try mod.arrayType(.{ + .len = name.len, + .child = .u8_type, + }); const new_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), - try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + new_decl_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = new_decl_ty.toIntern(), + .storage = .{ .bytes = name }, + } })).toValue(), 0, // default alignment ); - break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl); + break :v try mod.intern(.{ .ptr = .{ + .ty = .slice_const_u8_type, + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + } }); }; - const union_field_fields = try fields_anon_decl.arena().create([3]Value); const alignment = switch (layout) { .Auto, .Extern => try sema.unionFieldAlignment(field), .Packed => 0, }; - union_field_fields.* = .{ + const union_field_fields = .{ // name: []const u8, name_val, // type: type, - try Value.Tag.ty.create(fields_anon_decl.arena(), field.ty), + field.ty.toIntern(), // alignment: comptime_int, - try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment), + (try mod.intValue(Type.comptime_int, alignment)).toIntern(), }; - field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), union_field_fields); + field_val.* = try mod.intern(.{ .aggregate = .{ + .ty = union_field_ty.toIntern(), + .storage = .{ .elems = &union_field_fields }, + } }); } const fields_val = v: { + const array_fields_ty = try mod.arrayType(.{ + .len = union_field_vals.len, + .child = union_field_ty.toIntern(), + .sentinel = .none, + }); const new_decl = try fields_anon_decl.finish( - try Type.Tag.array.create(fields_anon_decl.arena(), .{ - .len = union_field_vals.len, - .elem_type = union_field_ty, - }), - try Value.Tag.aggregate.create( - fields_anon_decl.arena(), - try fields_anon_decl.arena().dupe(Value, union_field_vals), - ), + array_fields_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = array_fields_ty.toIntern(), + .storage = .{ .elems = union_field_vals }, + } })).toValue(), 0, // default alignment ); - break :v try Value.Tag.slice.create(sema.arena, .{ - .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), - .len = try Value.Tag.int_u64.create(sema.arena, union_field_vals.len), - }); + break :v try mod.intern(.{ .ptr = .{ + .ty = (try mod.ptrType(.{ + .child = union_field_ty.toIntern(), + .flags = .{ + .size = .Slice, + .is_const = true, + }, + })).toIntern(), + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, union_field_vals.len)).toIntern(), + } }); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespace()); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespaceIndex(mod)); - const enum_tag_ty_val = if (union_ty.unionTagType()) |tag_ty| v: { - const ty_val = try Value.Tag.ty.create(sema.arena, tag_ty); - break :v try Value.Tag.opt_payload.create(sema.arena, ty_val); - } else Value.null; + const enum_tag_ty_val = try mod.intern(.{ .opt = .{ + .ty = (try mod.optionalType(.type_type)).toIntern(), + .val = if (union_ty.unionTagType(mod)) |tag_ty| tag_ty.toIntern() else .none, + } }); + + const container_layout_ty = t: { + const decl_index = (try sema.namespaceLookup( + block, + src, + (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "ContainerLayout"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, decl_index); + try sema.ensureDeclAnalyzed(decl_index); + const decl = mod.declPtr(decl_index); + break :t decl.val.toType(); + }; - const field_values = try sema.arena.create([4]Value); - field_values.* = .{ + const field_values = .{ // layout: ContainerLayout, - try Value.Tag.enum_field_index.create( - sema.arena, - @enumToInt(layout), - ), + (try mod.enumValueFieldIndex(container_layout_ty, @enumToInt(layout))).toIntern(), // tag_type: ?type, enum_tag_ty_val, @@ -16249,14 +17173,14 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // decls: []const Declaration, decls_val, }; - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Union)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.Union))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = type_union_ty.toIntern(), + .storage = .{ .elems = &field_values }, + } }), + } })).toValue()); }, .Struct => { // TODO: look into memoizing this result. @@ -16264,154 +17188,212 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai var fields_anon_decl = try block.startAnonDecl(); defer fields_anon_decl.deinit(); + const type_struct_ty = t: { + const type_struct_ty_decl_index = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Struct"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, type_struct_ty_decl_index); + try sema.ensureDeclAnalyzed(type_struct_ty_decl_index); + const type_struct_ty_decl = mod.declPtr(type_struct_ty_decl_index); + break :t type_struct_ty_decl.val.toType(); + }; + const struct_field_ty = t: { const struct_field_ty_decl_index = (try sema.namespaceLookup( block, src, - type_info_ty.getNamespace().?, - "StructField", + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "StructField"), )).?; - try sema.mod.declareDeclDependency(sema.owner_decl_index, struct_field_ty_decl_index); + try mod.declareDeclDependency(sema.owner_decl_index, struct_field_ty_decl_index); try sema.ensureDeclAnalyzed(struct_field_ty_decl_index); - const struct_field_ty_decl = sema.mod.declPtr(struct_field_ty_decl_index); - var buffer: Value.ToTypeBuffer = undefined; - break :t try struct_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); + const struct_field_ty_decl = mod.declPtr(struct_field_ty_decl_index); + break :t struct_field_ty_decl.val.toType(); }; + const struct_ty = try sema.resolveTypeFields(ty); try sema.resolveTypeLayout(ty); // Getting alignment requires type layout - const layout = struct_ty.containerLayout(); - - const struct_field_vals = fv: { - if (struct_ty.isSimpleTupleOrAnonStruct()) { - const tuple = struct_ty.tupleFields(); - const field_types = tuple.types; - const struct_field_vals = try fields_anon_decl.arena().alloc(Value, field_types.len); - for (struct_field_vals, 0..) |*struct_field_val, i| { - const field_ty = field_types[i]; - const name_val = v: { - var anon_decl = try block.startAnonDecl(); - defer anon_decl.deinit(); - const bytes = if (struct_ty.castTag(.anon_struct)) |payload| - try anon_decl.arena().dupeZ(u8, payload.data.names[i]) - else - try std.fmt.allocPrintZ(anon_decl.arena(), "{d}", .{i}); - const new_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), - try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), - 0, // default alignment - ); - break :v try Value.Tag.slice.create(fields_anon_decl.arena(), .{ - .ptr = try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl), - .len = try Value.Tag.int_u64.create(fields_anon_decl.arena(), bytes.len), - }); - }; - - const struct_field_fields = try fields_anon_decl.arena().create([5]Value); - const field_val = tuple.values[i]; - const is_comptime = field_val.tag() != .unreachable_value; - const opt_default_val = if (is_comptime) field_val else null; - const default_val_ptr = try sema.optRefValue(block, field_ty, opt_default_val); - struct_field_fields.* = .{ - // name: []const u8, - name_val, - // type: type, - try Value.Tag.ty.create(fields_anon_decl.arena(), field_ty), - // default_value: ?*const anyopaque, - try default_val_ptr.copy(fields_anon_decl.arena()), - // is_comptime: bool, - Value.makeBool(is_comptime), - // alignment: comptime_int, - try field_ty.lazyAbiAlignment(target, fields_anon_decl.arena()), - }; - struct_field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), struct_field_fields); - } - break :fv struct_field_vals; - } - const struct_fields = struct_ty.structFields(); - const struct_field_vals = try fields_anon_decl.arena().alloc(Value, struct_fields.count()); + const layout = struct_ty.containerLayout(mod); + + var struct_field_vals: []InternPool.Index = &.{}; + defer gpa.free(struct_field_vals); + fv: { + const struct_type = switch (ip.indexToKey(struct_ty.toIntern())) { + .anon_struct_type => |tuple| { + struct_field_vals = try gpa.alloc(InternPool.Index, tuple.types.len); + for (struct_field_vals, 0..) |*struct_field_val, i| { + const anon_struct_type = ip.indexToKey(struct_ty.toIntern()).anon_struct_type; + const field_ty = anon_struct_type.types[i]; + const field_val = anon_struct_type.values[i]; + const name_val = v: { + var anon_decl = try block.startAnonDecl(); + defer anon_decl.deinit(); + // TODO: write something like getCoercedInts to avoid needing to dupe + const bytes = if (tuple.names.len != 0) + // https://github.com/ziglang/zig/issues/15709 + try sema.arena.dupe(u8, ip.stringToSlice(ip.indexToKey(struct_ty.toIntern()).anon_struct_type.names[i])) + else + try std.fmt.allocPrint(sema.arena, "{d}", .{i}); + const new_decl_ty = try mod.arrayType(.{ + .len = bytes.len, + .child = .u8_type, + }); + const new_decl = try anon_decl.finish( + new_decl_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = new_decl_ty.toIntern(), + .storage = .{ .bytes = bytes }, + } })).toValue(), + 0, // default alignment + ); + break :v try mod.intern(.{ .ptr = .{ + .ty = .slice_const_u8_type, + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(), + } }); + }; - for (struct_field_vals, 0..) |*field_val, i| { - const field = struct_fields.values()[i]; - const name = struct_fields.keys()[i]; + const is_comptime = field_val != .none; + const opt_default_val = if (is_comptime) field_val.toValue() else null; + const default_val_ptr = try sema.optRefValue(block, field_ty.toType(), opt_default_val); + const struct_field_fields = .{ + // name: []const u8, + name_val, + // type: type, + field_ty, + // default_value: ?*const anyopaque, + default_val_ptr.toIntern(), + // is_comptime: bool, + Value.makeBool(is_comptime).toIntern(), + // alignment: comptime_int, + (try mod.intValue(Type.comptime_int, field_ty.toType().abiAlignment(mod))).toIntern(), + }; + struct_field_val.* = try mod.intern(.{ .aggregate = .{ + .ty = struct_field_ty.toIntern(), + .storage = .{ .elems = &struct_field_fields }, + } }); + } + break :fv; + }, + .struct_type => |s| s, + else => unreachable, + }; + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse break :fv; + struct_field_vals = try gpa.alloc(InternPool.Index, struct_obj.fields.count()); + + for ( + struct_field_vals, + struct_obj.fields.keys(), + struct_obj.fields.values(), + ) |*field_val, name_nts, field| { + // TODO: write something like getCoercedInts to avoid needing to dupe + const name = try sema.arena.dupe(u8, ip.stringToSlice(name_nts)); const name_val = v: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); - const bytes = try anon_decl.arena().dupeZ(u8, name); + const new_decl_ty = try mod.arrayType(.{ + .len = name.len, + .child = .u8_type, + }); const new_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), - try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + new_decl_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = new_decl_ty.toIntern(), + .storage = .{ .bytes = name }, + } })).toValue(), 0, // default alignment ); - break :v try Value.Tag.slice.create(fields_anon_decl.arena(), .{ - .ptr = try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl), - .len = try Value.Tag.int_u64.create(fields_anon_decl.arena(), bytes.len), - }); + break :v try mod.intern(.{ .ptr = .{ + .ty = .slice_const_u8_type, + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + } }); }; - const struct_field_fields = try fields_anon_decl.arena().create([5]Value); - const opt_default_val = if (field.default_val.tag() == .unreachable_value) + const opt_default_val = if (field.default_val == .none) null else - field.default_val; + field.default_val.toValue(); const default_val_ptr = try sema.optRefValue(block, field.ty, opt_default_val); - const alignment = field.alignment(target, layout); + const alignment = field.alignment(mod, layout); - struct_field_fields.* = .{ + const struct_field_fields = .{ // name: []const u8, name_val, // type: type, - try Value.Tag.ty.create(fields_anon_decl.arena(), field.ty), + field.ty.toIntern(), // default_value: ?*const anyopaque, - try default_val_ptr.copy(fields_anon_decl.arena()), + default_val_ptr.toIntern(), // is_comptime: bool, - Value.makeBool(field.is_comptime), + Value.makeBool(field.is_comptime).toIntern(), // alignment: comptime_int, - try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment), + (try mod.intValue(Type.comptime_int, alignment)).toIntern(), }; - field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), struct_field_fields); + field_val.* = try mod.intern(.{ .aggregate = .{ + .ty = struct_field_ty.toIntern(), + .storage = .{ .elems = &struct_field_fields }, + } }); } - break :fv struct_field_vals; - }; + } const fields_val = v: { + const array_fields_ty = try mod.arrayType(.{ + .len = struct_field_vals.len, + .child = struct_field_ty.toIntern(), + .sentinel = .none, + }); const new_decl = try fields_anon_decl.finish( - try Type.Tag.array.create(fields_anon_decl.arena(), .{ - .len = struct_field_vals.len, - .elem_type = struct_field_ty, - }), - try Value.Tag.aggregate.create( - fields_anon_decl.arena(), - try fields_anon_decl.arena().dupe(Value, struct_field_vals), - ), + array_fields_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = array_fields_ty.toIntern(), + .storage = .{ .elems = struct_field_vals }, + } })).toValue(), 0, // default alignment ); - break :v try Value.Tag.slice.create(sema.arena, .{ - .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), - .len = try Value.Tag.int_u64.create(sema.arena, struct_field_vals.len), - }); + break :v try mod.intern(.{ .ptr = .{ + .ty = (try mod.ptrType(.{ + .child = struct_field_ty.toIntern(), + .flags = .{ + .size = .Slice, + .is_const = true, + }, + })).toIntern(), + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, struct_field_vals.len)).toIntern(), + } }); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespace()); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespaceIndex(mod)); - const backing_integer_val = blk: { - if (layout == .Packed) { - const struct_obj = struct_ty.castTag(.@"struct").?.data; + const backing_integer_val = try mod.intern(.{ .opt = .{ + .ty = (try mod.optionalType(.type_type)).toIntern(), + .val = if (layout == .Packed) val: { + const struct_obj = mod.typeToStruct(struct_ty).?; assert(struct_obj.haveLayout()); - assert(struct_obj.backing_int_ty.isInt()); - const backing_int_ty_val = try Value.Tag.ty.create(sema.arena, struct_obj.backing_int_ty); - break :blk try Value.Tag.opt_payload.create(sema.arena, backing_int_ty_val); - } else { - break :blk Value.initTag(.null_value); - } + assert(struct_obj.backing_int_ty.isInt(mod)); + break :val struct_obj.backing_int_ty.toIntern(); + } else .none, + } }); + + const container_layout_ty = t: { + const decl_index = (try sema.namespaceLookup( + block, + src, + (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "ContainerLayout"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, decl_index); + try sema.ensureDeclAnalyzed(decl_index); + const decl = mod.declPtr(decl_index); + break :t decl.val.toType(); }; - const field_values = try sema.arena.create([5]Value); - field_values.* = .{ + const field_values = [_]InternPool.Index{ // layout: ContainerLayout, - try Value.Tag.enum_field_index.create( - sema.arena, - @enumToInt(layout), - ), + (try mod.enumValueFieldIndex(container_layout_ty, @enumToInt(layout))).toIntern(), // backing_integer: ?type, backing_integer_val, // fields: []const StructField, @@ -16419,36 +17401,48 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // decls: []const Declaration, decls_val, // is_tuple: bool, - Value.makeBool(struct_ty.isTuple()), + Value.makeBool(struct_ty.isTuple(mod)).toIntern(), }; - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Struct)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.Struct))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = type_struct_ty.toIntern(), + .storage = .{ .elems = &field_values }, + } }), + } })).toValue()); }, .Opaque => { // TODO: look into memoizing this result. + const type_opaque_ty = t: { + const type_opaque_ty_decl_index = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, "Opaque"), + )).?; + try mod.declareDeclDependency(sema.owner_decl_index, type_opaque_ty_decl_index); + try sema.ensureDeclAnalyzed(type_opaque_ty_decl_index); + const type_opaque_ty_decl = mod.declPtr(type_opaque_ty_decl_index); + break :t type_opaque_ty_decl.val.toType(); + }; + const opaque_ty = try sema.resolveTypeFields(ty); - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespace()); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespaceIndex(mod)); - const field_values = try sema.arena.create([1]Value); - field_values.* = .{ + const field_values = .{ // decls: []const Declaration, decls_val, }; - - return sema.addConstant( - type_info_ty, - try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Opaque)), - .val = try Value.Tag.aggregate.create(sema.arena, field_values), - }), - ); + return sema.addConstant(type_info_ty, (try mod.intern(.{ .un = .{ + .ty = type_info_ty.toIntern(), + .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @enumToInt(std.builtin.TypeId.Opaque))).toIntern(), + .val = try mod.intern(.{ .aggregate = .{ + .ty = type_opaque_ty.toIntern(), + .storage = .{ .elems = &field_values }, + } }), + } })).toValue()); }, .Frame => return sema.failWithUseOfAsync(block, src), .AnyFrame => return sema.failWithUseOfAsync(block, src), @@ -16460,8 +17454,11 @@ fn typeInfoDecls( block: *Block, src: LazySrcLoc, type_info_ty: Type, - opt_namespace: ?*Module.Namespace, -) CompileError!Value { + opt_namespace: Module.Namespace.OptionalIndex, +) CompileError!InternPool.Index { + const mod = sema.mod; + const gpa = sema.gpa; + var decls_anon_decl = try block.startAnonDecl(); defer decls_anon_decl.deinit(); @@ -16469,89 +17466,110 @@ fn typeInfoDecls( const declaration_ty_decl_index = (try sema.namespaceLookup( block, src, - type_info_ty.getNamespace().?, - "Declaration", + type_info_ty.getNamespaceIndex(mod).unwrap().?, + try mod.intern_pool.getOrPutString(gpa, "Declaration"), )).?; - try sema.mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index); + try mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index); try sema.ensureDeclAnalyzed(declaration_ty_decl_index); - const declaration_ty_decl = sema.mod.declPtr(declaration_ty_decl_index); - var buffer: Value.ToTypeBuffer = undefined; - break :t try declaration_ty_decl.val.toType(&buffer).copy(decls_anon_decl.arena()); + const declaration_ty_decl = mod.declPtr(declaration_ty_decl_index); + break :t declaration_ty_decl.val.toType(); }; - try sema.queueFullTypeResolution(try declaration_ty.copy(sema.arena)); + try sema.queueFullTypeResolution(declaration_ty); - var decl_vals = std.ArrayList(Value).init(sema.gpa); + var decl_vals = std.ArrayList(InternPool.Index).init(gpa); defer decl_vals.deinit(); - var seen_namespaces = std.AutoHashMap(*Namespace, void).init(sema.gpa); + var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa); defer seen_namespaces.deinit(); - if (opt_namespace) |some| { - try sema.typeInfoNamespaceDecls(block, decls_anon_decl.arena(), some, &decl_vals, &seen_namespaces); + if (opt_namespace.unwrap()) |namespace_index| { + const namespace = mod.namespacePtr(namespace_index); + try sema.typeInfoNamespaceDecls(block, namespace, declaration_ty, &decl_vals, &seen_namespaces); } + const array_decl_ty = try mod.arrayType(.{ + .len = decl_vals.items.len, + .child = declaration_ty.toIntern(), + .sentinel = .none, + }); const new_decl = try decls_anon_decl.finish( - try Type.Tag.array.create(decls_anon_decl.arena(), .{ - .len = decl_vals.items.len, - .elem_type = declaration_ty, - }), - try Value.Tag.aggregate.create( - decls_anon_decl.arena(), - try decls_anon_decl.arena().dupe(Value, decl_vals.items), - ), + array_decl_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = array_decl_ty.toIntern(), + .storage = .{ .elems = decl_vals.items }, + } })).toValue(), 0, // default alignment ); - return try Value.Tag.slice.create(sema.arena, .{ - .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), - .len = try Value.Tag.int_u64.create(sema.arena, decl_vals.items.len), - }); + return try mod.intern(.{ .ptr = .{ + .ty = (try mod.ptrType(.{ + .child = declaration_ty.toIntern(), + .flags = .{ + .size = .Slice, + .is_const = true, + }, + })).toIntern(), + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, decl_vals.items.len)).toIntern(), + } }); } fn typeInfoNamespaceDecls( sema: *Sema, block: *Block, - decls_anon_decl: Allocator, namespace: *Namespace, - decl_vals: *std.ArrayList(Value), + declaration_ty: Type, + decl_vals: *std.ArrayList(InternPool.Index), seen_namespaces: *std.AutoHashMap(*Namespace, void), ) !void { + const mod = sema.mod; + const ip = &mod.intern_pool; const gop = try seen_namespaces.getOrPut(namespace); if (gop.found_existing) return; const decls = namespace.decls.keys(); for (decls) |decl_index| { - const decl = sema.mod.declPtr(decl_index); + const decl = mod.declPtr(decl_index); if (decl.kind == .@"usingnamespace") { if (decl.analysis == .in_progress) continue; - try sema.mod.ensureDeclAnalyzed(decl_index); - var buf: Value.ToTypeBuffer = undefined; - const new_ns = decl.val.toType(&buf).getNamespace().?; - try sema.typeInfoNamespaceDecls(block, decls_anon_decl, new_ns, decl_vals, seen_namespaces); + try mod.ensureDeclAnalyzed(decl_index); + const new_ns = decl.val.toType().getNamespace(mod).?; + try sema.typeInfoNamespaceDecls(block, new_ns, declaration_ty, decl_vals, seen_namespaces); continue; } if (decl.kind != .named) continue; const name_val = v: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); - const bytes = try anon_decl.arena().dupeZ(u8, mem.sliceTo(decl.name, 0)); + // TODO: write something like getCoercedInts to avoid needing to dupe + const name = try sema.arena.dupe(u8, ip.stringToSlice(decl.name)); + const new_decl_ty = try mod.arrayType(.{ + .len = name.len, + .child = .u8_type, + }); const new_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), - try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + new_decl_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = new_decl_ty.toIntern(), + .storage = .{ .bytes = name }, + } })).toValue(), 0, // default alignment ); - break :v try Value.Tag.slice.create(decls_anon_decl, .{ - .ptr = try Value.Tag.decl_ref.create(decls_anon_decl, new_decl), - .len = try Value.Tag.int_u64.create(decls_anon_decl, bytes.len), - }); + break :v try mod.intern(.{ .ptr = .{ + .ty = .slice_const_u8_type, + .addr = .{ .decl = new_decl }, + .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + } }); }; - const fields = try decls_anon_decl.create([2]Value); - fields.* = .{ + const fields = .{ //name: []const u8, name_val, //is_pub: bool, - Value.makeBool(decl.is_pub), + Value.makeBool(decl.is_pub).toIntern(), }; - try decl_vals.append(try Value.Tag.aggregate.create(decls_anon_decl, fields)); + try decl_vals.append(try mod.intern(.{ .aggregate = .{ + .ty = declaration_ty.toIntern(), + .storage = .{ .elems = &fields }, + } })); } } @@ -16586,7 +17604,7 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const operand = try sema.resolveBody(&child_block, body, inst); const operand_ty = sema.typeOf(operand); - if (operand_ty.tag() == .generic_poison) return error.GenericPoison; + if (operand_ty.isGenericPoison()) return error.GenericPoison; return sema.addType(operand_ty); } @@ -16600,10 +17618,11 @@ fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil } fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type { - switch (operand.zigTypeTag()) { + const mod = sema.mod; + switch (operand.zigTypeTag(mod)) { .ComptimeInt => return Type.comptime_int, .Int => { - const bits = operand.bitSize(sema.mod.getTarget()); + const bits = operand.bitSize(mod); const count = if (bits == 0) 0 else blk: { @@ -16614,14 +17633,14 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi } break :blk count; }; - return Module.makeIntType(sema.arena, .unsigned, count); + return mod.intType(.unsigned, count); }, .Vector => { - const elem_ty = operand.elemType2(); + const elem_ty = operand.elemType2(mod); const log2_elem_ty = try sema.log2IntType(block, elem_ty, src); - return Type.Tag.vector.create(sema.arena, .{ - .len = operand.vectorLen(), - .elem_type = log2_elem_ty, + return mod.vectorType(.{ + .len = operand.vectorLen(mod), + .child = log2_elem_ty.toIntern(), }); }, else => {}, @@ -16630,7 +17649,7 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi block, src, "bit shifting operation expected integer type, found '{}'", - .{operand.fmt(sema.mod)}, + .{operand.fmt(mod)}, ); } @@ -16681,6 +17700,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; @@ -16688,7 +17708,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const operand = try sema.coerce(block, Type.bool, uncasted_operand, operand_src); if (try sema.resolveMaybeUndefVal(operand)) |val| { - return if (val.isUndef()) + return if (val.isUndef(mod)) sema.addConstUndef(Type.bool) else if (val.toBool()) Air.Inst.Ref.bool_false @@ -16708,6 +17728,7 @@ fn zirBoolBr( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const datas = sema.code.instructions.items(.data); const inst_data = datas[inst].bool_br; const lhs = try sema.resolveInst(inst_data.lhs); @@ -16756,12 +17777,12 @@ fn zirBoolBr( _ = try lhs_block.addBr(block_inst, lhs_result); const rhs_result = try sema.resolveBody(rhs_block, body, inst); - if (!sema.typeOf(rhs_result).isNoReturn()) { + if (!sema.typeOf(rhs_result).isNoReturn(mod)) { _ = try rhs_block.addBr(block_inst, rhs_result); } const result = sema.finishCondBr(parent_block, &child_block, &then_block, &else_block, lhs, block_inst); - if (!sema.typeOf(rhs_result).isNoReturn()) { + if (!sema.typeOf(rhs_result).isNoReturn(mod)) { if (try sema.resolveDefinedValue(rhs_block, sema.src, rhs_result)) |rhs_val| { if (is_bool_or and rhs_val.toBool()) { return Air.Inst.Ref.bool_true; @@ -16811,9 +17832,10 @@ fn finishCondBr( } fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .Optional, .Null, .Undefined => return, - .Pointer => if (ty.isPtrLikeOptional()) return, + .Pointer => if (ty.isPtrLikeOptional(mod)) return, else => {}, } return sema.failWithExpectedOptionalType(block, src, ty); @@ -16842,10 +17864,11 @@ fn zirIsNonNullPtr( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const ptr = try sema.resolveInst(inst_data.operand); - try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2()); + try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(mod)); if ((try sema.resolveMaybeUndefVal(ptr)) == null) { return block.addUnOp(.is_non_null_ptr, ptr); } @@ -16854,10 +17877,11 @@ fn zirIsNonNullPtr( } fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .ErrorSet, .ErrorUnion, .Undefined => return, else => return sema.fail(block, src, "expected error union type, found '{}'", .{ - ty.fmt(sema.mod), + ty.fmt(mod), }), } } @@ -16877,10 +17901,11 @@ fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const ptr = try sema.resolveInst(inst_data.operand); - try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2()); + try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(mod)); const loaded = try sema.analyzeLoad(block, src, ptr, src); return sema.analyzeIsNonErr(block, src, loaded); } @@ -16903,6 +17928,7 @@ fn zirCondbr( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); @@ -16943,8 +17969,8 @@ fn zirCondbr( const err_inst_data = sema.code.instructions.items(.data)[index].un_node; const err_operand = try sema.resolveInst(err_inst_data.operand); const operand_ty = sema.typeOf(err_operand); - assert(operand_ty.zigTypeTag() == .ErrorUnion); - const result_ty = operand_ty.errorUnionSet(); + assert(operand_ty.zigTypeTag(mod) == .ErrorUnion); + const result_ty = operand_ty.errorUnionSet(mod); break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand); }; @@ -16970,7 +17996,7 @@ fn zirCondbr( return always_noreturn; } -fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref { +fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; @@ -16978,9 +18004,10 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! const body = sema.code.extra[extra.end..][0..extra.data.body_len]; const err_union = try sema.resolveInst(extra.data.operand); const err_union_ty = sema.typeOf(err_union); - if (err_union_ty.zigTypeTag() != .ErrorUnion) { + const mod = sema.mod; + if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ - err_union_ty.fmt(sema.mod), + err_union_ty.fmt(mod), }); } const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); @@ -17015,7 +18042,7 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! return try_inst; } -fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref { +fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; @@ -17024,9 +18051,10 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr const operand = try sema.resolveInst(extra.data.operand); const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src); const err_union_ty = sema.typeOf(err_union); - if (err_union_ty.zigTypeTag() != .ErrorUnion) { + const mod = sema.mod; + if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ - err_union_ty.fmt(sema.mod), + err_union_ty.fmt(mod), }); } const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); @@ -17047,9 +18075,9 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr _ = try sema.analyzeBodyInner(&sub_block, body); const operand_ty = sema.typeOf(operand); - const ptr_info = operand_ty.ptrInfo().data; - const res_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = err_union_ty.errorUnionPayload(), + const ptr_info = operand_ty.ptrInfo(mod); + const res_ty = try Type.ptr(sema.arena, mod, .{ + .pointee_type = err_union_ty.errorUnionPayload(mod), .@"addrspace" = ptr_info.@"addrspace", .mutable = ptr_info.mutable, .@"allowzero" = ptr_info.@"allowzero", @@ -17145,16 +18173,17 @@ fn zirRetErrValue( block: *Block, inst: Zir.Inst.Index, ) CompileError!Zir.Inst.Index { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].str_tok; - const err_name = inst_data.get(sema.code); + const err_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); + _ = try mod.getErrorValue(err_name); const src = inst_data.src(); - // Return the error code from the function. - const kv = try sema.mod.getErrorValue(err_name); - const result_inst = try sema.addConstant( - try Type.Tag.error_set_single.create(sema.arena, kv.key), - try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }), - ); + const error_set_type = try mod.singleErrorSetType(err_name); + const result_inst = try sema.addConstant(error_set_type, (try mod.intern(.{ .err = .{ + .ty = error_set_type.toIntern(), + .name = err_name, + } })).toValue()); return sema.analyzeRet(block, result_inst, src); } @@ -17166,16 +18195,17 @@ fn zirRetImplicit( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_tok; const operand = try sema.resolveInst(inst_data.operand); const r_brace_src = inst_data.src(); const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; - const base_tag = sema.fn_ret_ty.baseZigTypeTag(); + const base_tag = sema.fn_ret_ty.baseZigTypeTag(mod); if (base_tag == .NoReturn) { const msg = msg: { const msg = try sema.errMsg(block, ret_ty_src, "function declared '{}' implicitly returns", .{ - sema.fn_ret_ty.fmt(sema.mod), + sema.fn_ret_ty.fmt(mod), }); errdefer msg.destroy(sema.gpa); try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); @@ -17185,7 +18215,7 @@ fn zirRetImplicit( } else if (base_tag != .Void) { const msg = msg: { const msg = try sema.errMsg(block, ret_ty_src, "function with non-void return type '{}' implicitly returns", .{ - sema.fn_ret_ty.fmt(sema.mod), + sema.fn_ret_ty.fmt(mod), }); errdefer msg.destroy(sema.gpa); try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); @@ -17223,7 +18253,7 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr); - return sema.retWithErrTracing(block, src, is_non_err, .ret_load, ret_ptr); + return sema.retWithErrTracing(block, is_non_err, .ret_load, ret_ptr); } _ = try block.addUnOp(.ret_load, ret_ptr); @@ -17233,11 +18263,11 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir fn retWithErrTracing( sema: *Sema, block: *Block, - src: LazySrcLoc, is_non_err: Air.Inst.Ref, ret_tag: Air.Inst.Tag, operand: Air.Inst.Ref, ) CompileError!Zir.Inst.Index { + const mod = sema.mod; const need_check = switch (is_non_err) { .bool_true => { _ = try block.addUnOp(ret_tag, operand); @@ -17249,13 +18279,13 @@ fn retWithErrTracing( const gpa = sema.gpa; const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); - const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty); + const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); const return_err_fn = try sema.getBuiltin("returnError"); const args: [1]Air.Inst.Ref = .{err_return_trace}; if (!need_check) { - _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); + try sema.callBuiltin(block, return_err_fn, .never_inline, &args); _ = try block.addUnOp(ret_tag, operand); return always_noreturn; } @@ -17266,7 +18296,7 @@ fn retWithErrTracing( var else_block = block.makeSubBlock(); defer else_block.instructions.deinit(gpa); - _ = try sema.analyzeCall(&else_block, return_err_fn, src, src, .never_inline, false, &args, null); + try sema.callBuiltin(&else_block, return_err_fn, .never_inline, &args); _ = try else_block.addUnOp(ret_tag, operand); try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + @@ -17289,17 +18319,19 @@ fn retWithErrTracing( } fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool { - if (!sema.mod.backendSupportsFeature(.error_return_trace)) return false; + const mod = sema.mod; + if (!mod.backendSupportsFeature(.error_return_trace)) return false; - return fn_ret_ty.isError() and - sema.mod.comp.bin_file.options.error_return_tracing; + return fn_ret_ty.isError(mod) and + mod.comp.bin_file.options.error_return_tracing; } fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].save_err_ret_index; - if (!sema.mod.backendSupportsFeature(.error_return_trace)) return; - if (!sema.mod.comp.bin_file.options.error_return_tracing) return; + if (!mod.backendSupportsFeature(.error_return_trace)) return; + if (!mod.comp.bin_file.options.error_return_tracing) return; // This is only relevant at runtime. if (block.is_comptime or block.is_typeof) return; @@ -17307,7 +18339,7 @@ fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const save_index = inst_data.operand == .none or b: { const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - break :b operand_ty.isError(); + break :b operand_ty.isError(mod); }; if (save_index) @@ -17328,7 +18360,7 @@ fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) const tracy = trace(@src()); defer tracy.end(); - const saved_index = if (Zir.refToIndex(inst_data.block)) |zir_block| b: { + const saved_index = if (Zir.refToIndexAllowNone(inst_data.block)) |zir_block| b: { var block = start_block; while (true) { if (block.label) |label| { @@ -17354,22 +18386,21 @@ fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere - const operand = try sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInstAllowNone(inst_data.operand); return sema.popErrorReturnTrace(start_block, src, operand, saved_index); } fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { - assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion); + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; + assert(sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion); - if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| { + if (mod.typeToInferredErrorSet(sema.fn_ret_ty.errorUnionSet(mod))) |ies| { const op_ty = sema.typeOf(uncasted_operand); - switch (op_ty.zigTypeTag()) { - .ErrorSet => { - try payload.data.addErrorSet(sema.gpa, op_ty); - }, - .ErrorUnion => { - try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet()); - }, + switch (op_ty.zigTypeTag(mod)) { + .ErrorSet => try ies.addErrorSet(op_ty, ip, gpa), + .ErrorUnion => try ies.addErrorSet(op_ty.errorUnionSet(mod), ip, gpa), else => {}, } } @@ -17384,7 +18415,8 @@ fn analyzeRet( // Special case for returning an error to an inferred error set; we need to // add the error tag to the inferred error set of the in-scope function, so // that the coercion below works correctly. - if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) { + const mod = sema.mod; + if (sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion) { try sema.addToInferredErrorSet(uncasted_operand); } const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, .{ .is_ret = true }) catch |err| switch (err) { @@ -17412,7 +18444,7 @@ fn analyzeRet( // Avoid adding a frame to the error return trace in case the value is comptime-known // to be not an error. const is_non_err = try sema.analyzeIsNonErr(block, src, operand); - return sema.retWithErrTracing(block, src, is_non_err, .ret, operand); + return sema.retWithErrTracing(block, is_non_err, .ret, operand); } _ = try block.addUnOp(.ret, operand); @@ -17432,6 +18464,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); const elem_ty_src: LazySrcLoc = .{ .node_offset_ptr_elem = extra.data.src_node }; @@ -17444,46 +18477,54 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const elem_ty = blk: { const air_inst = try sema.resolveInst(extra.data.elem_type); const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| { - if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer()) { + if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(mod)) { try sema.errNote(block, elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{}); } return err; }; - if (ty.tag() == .generic_poison) return error.GenericPoison; + if (ty.isGenericPoison()) return error.GenericPoison; break :blk ty; }; - const target = sema.mod.getTarget(); + + if (elem_ty.zigTypeTag(mod) == .NoReturn) + return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); + + const target = mod.getTarget(); var extra_i = extra.end; const sentinel = if (inst_data.flags.has_sentinel) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - break :blk (try sema.resolveInstConst(block, sentinel_src, ref, "pointer sentinel value must be comptime-known")).val; - } else null; + const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src); + const val = try sema.resolveConstValue(block, sentinel_src, coerced, "pointer sentinel value must be comptime-known"); + break :blk val.toIntern(); + } else .none; - const abi_align: u32 = if (inst_data.flags.has_align) blk: { + const abi_align: InternPool.Alignment = if (inst_data.flags.has_align) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), align_src); const val = try sema.resolveConstValue(block, align_src, coerced, "pointer alignment must be comptime-known"); // Check if this happens to be the lazy alignment of our element type, in // which case we can make this 0 without resolving it. - if (val.castTag(.lazy_align)) |payload| { - if (payload.data.eql(elem_ty, sema.mod)) { - break :blk 0; - } + switch (mod.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 abi_align = @intCast(u32, (try val.getUnsignedIntAdvanced(target, sema)).?); + const abi_align = @intCast(u32, (try val.getUnsignedIntAdvanced(mod, sema)).?); try sema.validateAlign(block, align_src, abi_align); - break :blk abi_align; - } else 0; + break :blk InternPool.Alignment.fromByteUnits(abi_align); + } else .none; const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; break :blk try sema.analyzeAddressSpace(block, addrspace_src, ref, .pointer); - } else if (elem_ty.zigTypeTag() == .Fn and target.cpu.arch == .avr) .flash else .generic; + } else if (elem_ty.zigTypeTag(mod) == .Fn and target.cpu.arch == .avr) .flash else .generic; const bit_offset = if (inst_data.flags.has_bit_range) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); @@ -17503,50 +18544,52 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return sema.fail(block, bitoffset_src, "bit offset starts after end of host integer", .{}); } - if (elem_ty.zigTypeTag() == .NoReturn) { - return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); - } else if (elem_ty.zigTypeTag() == .Fn) { + if (elem_ty.zigTypeTag(mod) == .Fn) { if (inst_data.size != .One) { return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); } - const fn_align = elem_ty.fnInfo().alignment; - if (inst_data.flags.has_align and abi_align != 0 and fn_align != 0 and + const fn_align = mod.typeToFunc(elem_ty).?.alignment; + if (inst_data.flags.has_align and abi_align != .none and fn_align != .none and abi_align != fn_align) { return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{}); } - } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) { + } else if (inst_data.size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); } else if (inst_data.size == .C) { if (!try sema.validateExternType(elem_ty, .other)) { const msg = msg: { - const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)}); + const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl, mod), elem_ty, .other); try sema.addDeclaredHereNote(msg, elem_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } - if (elem_ty.zigTypeTag() == .Opaque) { + if (elem_ty.zigTypeTag(mod) == .Opaque) { return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{}); } } - const ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = elem_ty, + const ty = try mod.ptrType(.{ + .child = elem_ty.toIntern(), .sentinel = sentinel, - .@"align" = abi_align, - .@"addrspace" = address_space, - .bit_offset = bit_offset, - .host_size = host_size, - .mutable = inst_data.flags.is_mutable, - .@"allowzero" = inst_data.flags.is_allowzero, - .@"volatile" = inst_data.flags.is_volatile, - .size = inst_data.size, + .flags = .{ + .alignment = abi_align, + .address_space = address_space, + .is_const = !inst_data.flags.is_mutable, + .is_allowzero = inst_data.flags.is_allowzero, + .is_volatile = inst_data.flags.is_volatile, + .size = inst_data.size, + }, + .packed_offset = .{ + .bit_offset = bit_offset, + .host_size = host_size, + }, }); return sema.addType(ty); } @@ -17558,8 +18601,9 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const obj_ty = try sema.resolveType(block, src, inst_data.operand); + const mod = sema.mod; - switch (obj_ty.zigTypeTag()) { + switch (obj_ty.zigTypeTag(mod)) { .Struct => return sema.structInitEmpty(block, obj_ty, src, src), .Array, .Vector => return sema.arrayInitEmpty(block, src, obj_ty), .Void => return sema.addConstant(obj_ty, Value.void), @@ -17575,12 +18619,13 @@ fn structInitEmpty( dest_src: LazySrcLoc, init_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const gpa = sema.gpa; // This logic must be synchronized with that in `zirStructInit`. const struct_ty = try sema.resolveTypeFields(obj_ty); // The init values to use for the struct instance. - const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount()); + const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(mod)); defer gpa.free(field_inits); @memset(field_inits, .none); @@ -17588,20 +18633,19 @@ fn structInitEmpty( } fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref { - const arr_len = obj_ty.arrayLen(); + const mod = sema.mod; + const arr_len = obj_ty.arrayLen(mod); if (arr_len != 0) { - if (obj_ty.zigTypeTag() == .Array) { + if (obj_ty.zigTypeTag(mod) == .Array) { return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len}); } else { return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len}); } } - if (obj_ty.sentinel()) |sentinel| { - const val = try Value.Tag.empty_array_sentinel.create(sema.arena, sentinel); - return sema.addConstant(obj_ty, val); - } else { - return sema.addConstant(obj_ty, Value.initTag(.empty_array)); - } + return sema.addConstant(obj_ty, (try mod.intern(.{ .aggregate = .{ + .ty = obj_ty.toIntern(), + .storage = .{ .elems = &.{} }, + } })).toValue()); } fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -17611,7 +18655,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; const union_ty = try sema.resolveType(block, ty_src, extra.union_type); - const field_name = try sema.resolveConstString(block, field_src, extra.field_name, "name of field being initialized must be comptime-known"); + const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, "name of field being initialized must be comptime-known"); const init = try sema.resolveInst(extra.init); return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src); } @@ -17623,21 +18667,23 @@ fn unionInit( init_src: LazySrcLoc, union_ty: Type, union_ty_src: LazySrcLoc, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); - const field = union_ty.unionFields().values()[field_index]; + const field = union_ty.unionFields(mod).values()[field_index]; const init = try sema.coerce(block, field.ty, uncasted_init, init_src); if (try sema.resolveMaybeUndefVal(init)) |init_val| { - const tag_ty = union_ty.unionTagTypeHypothetical(); - const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?); - const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); - return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{ - .tag = tag_val, - .val = init_val, - })); + const tag_ty = union_ty.unionTagTypeHypothetical(mod); + const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name, mod).?); + const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); + return sema.addConstant(union_ty, (try mod.intern(.{ .un = .{ + .ty = union_ty.toIntern(), + .tag = try tag_val.intern(tag_ty, mod), + .val = try init_val.intern(field.ty, mod), + } })).toValue()); } try sema.requireRuntimeBlock(block, init_src, null); @@ -17658,29 +18704,30 @@ fn zirStructInit( const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index); const src = inst_data.src(); + const mod = sema.mod; const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data; const first_field_type_data = zir_datas[first_item.field_type].pl_node; const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; const resolved_ty = try sema.resolveType(block, src, first_field_type_extra.container_type); try sema.resolveTypeLayout(resolved_ty); - if (resolved_ty.zigTypeTag() == .Struct) { + if (resolved_ty.zigTypeTag(mod) == .Struct) { // This logic must be synchronized with that in `zirStructInitEmpty`. // Maps field index to field_type index of where it was already initialized. // For making sure all fields are accounted for and no fields are duplicated. - const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount()); + const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(mod)); defer gpa.free(found_fields); // The init values to use for the struct instance. - const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount()); + const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(mod)); defer gpa.free(field_inits); @memset(field_inits, .none); var field_i: u32 = 0; var extra_index = extra.end; - const is_packed = resolved_ty.containerLayout() == .Packed; + const is_packed = resolved_ty.containerLayout(mod) == .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; @@ -17688,8 +18735,8 @@ fn zirStructInit( const field_type_data = zir_datas[item.data.field_type].pl_node; const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; - const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); - const field_index = if (resolved_ty.isTuple()) + const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); + const field_index = if (resolved_ty.isTuple(mod)) try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src) else try sema.structFieldIndex(block, resolved_ty, field_name, field_src); @@ -17707,19 +18754,19 @@ fn zirStructInit( } found_fields[field_index] = item.data.field_type; field_inits[field_index] = try sema.resolveInst(item.data.init); - if (!is_packed) if (resolved_ty.structFieldValueComptime(field_index)) |default_value| { + if (!is_packed) if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| { const init_val = (try sema.resolveMaybeUndefVal(field_inits[field_index])) orelse { return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known"); }; - if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index), sema.mod)) { + if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) { return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index); } }; } return sema.finishStructInit(block, src, src, field_inits, resolved_ty, is_ref); - } else if (resolved_ty.zigTypeTag() == .Union) { + } else if (resolved_ty.zigTypeTag(mod) == .Union) { if (extra.data.fields_len != 1) { return sema.fail(block, src, "union initialization expects exactly one field", .{}); } @@ -17729,40 +18776,40 @@ fn zirStructInit( const field_type_data = zir_datas[item.data.field_type].pl_node; const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; - const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); + const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); - const tag_ty = resolved_ty.unionTagTypeHypothetical(); - const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?); - const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); + const tag_ty = resolved_ty.unionTagTypeHypothetical(mod); + const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name, mod).?); + const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); const init_inst = try sema.resolveInst(item.data.init); if (try sema.resolveMaybeUndefVal(init_inst)) |val| { - return sema.addConstantMaybeRef( - block, - resolved_ty, - try Value.Tag.@"union".create(sema.arena, .{ .tag = tag_val, .val = val }), - is_ref, - ); + const field = resolved_ty.unionFields(mod).values()[field_index]; + return sema.addConstantMaybeRef(block, resolved_ty, (try mod.intern(.{ .un = .{ + .ty = resolved_ty.toIntern(), + .tag = try tag_val.intern(tag_ty, mod), + .val = try val.intern(field.ty, mod), + } })).toValue(), is_ref); } if (is_ref) { - const target = sema.mod.getTarget(); - const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ + const target = mod.getTarget(); + const alloc_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = resolved_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); const alloc = try block.addTy(.alloc, alloc_ty); const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty, true); try sema.storePtr(block, src, field_ptr, init_inst); - const new_tag = try sema.addConstant(resolved_ty.unionTagTypeHypothetical(), tag_val); + const new_tag = try sema.addConstant(resolved_ty.unionTagTypeHypothetical(mod), tag_val); _ = try block.addBinOp(.set_union_tag, alloc, new_tag); - return alloc; + return sema.makePtrConst(block, alloc); } try sema.requireRuntimeBlock(block, src, null); try sema.queueFullTypeResolution(resolved_ty); return block.addUnionInit(resolved_ty, field_index, init_inst); - } else if (resolved_ty.isAnonStruct()) { + } else if (resolved_ty.isAnonStruct(mod)) { return sema.fail(block, src, "TODO anon struct init validation", .{}); } unreachable; @@ -17777,101 +18824,100 @@ fn finishStructInit( struct_ty: Type, is_ref: bool, ) CompileError!Air.Inst.Ref { - const gpa = sema.gpa; + const mod = sema.mod; + const ip = &mod.intern_pool; var root_msg: ?*Module.ErrorMsg = null; errdefer if (root_msg) |msg| msg.destroy(sema.gpa); - if (struct_ty.isAnonStruct()) { - const struct_obj = struct_ty.castTag(.anon_struct).?.data; - for (struct_obj.values, 0..) |default_val, i| { - if (field_inits[i] != .none) continue; - - if (default_val.tag() == .unreachable_value) { - const field_name = struct_obj.names[i]; - const template = "missing struct field: {s}"; - const args = .{field_name}; - if (root_msg) |msg| { - try sema.errNote(block, init_src, msg, template, args); - } else { - root_msg = try sema.errMsg(block, init_src, template, args); - } - } else { - field_inits[i] = try sema.addConstant(struct_obj.types[i], default_val); - } - } - } else if (struct_ty.isTuple()) { - var i: u32 = 0; - const len = struct_ty.structFieldCount(); - while (i < len) : (i += 1) { - if (field_inits[i] != .none) continue; + switch (ip.indexToKey(struct_ty.toIntern())) { + .anon_struct_type => |anon_struct| { + for (anon_struct.types, anon_struct.values, 0..) |field_ty, default_val, i| { + if (field_inits[i] != .none) continue; - const default_val = struct_ty.structFieldDefaultValue(i); - if (default_val.tag() == .unreachable_value) { - const template = "missing tuple field with index {d}"; - if (root_msg) |msg| { - try sema.errNote(block, init_src, msg, template, .{i}); + if (default_val == .none) { + if (anon_struct.names.len == 0) { + const template = "missing tuple field with index {d}"; + if (root_msg) |msg| { + try sema.errNote(block, init_src, msg, template, .{i}); + } else { + root_msg = try sema.errMsg(block, init_src, template, .{i}); + } + } else { + const field_name = anon_struct.names[i]; + const template = "missing struct field: {}"; + const args = .{field_name.fmt(ip)}; + if (root_msg) |msg| { + try sema.errNote(block, init_src, msg, template, args); + } else { + root_msg = try sema.errMsg(block, init_src, template, args); + } + } } else { - root_msg = try sema.errMsg(block, init_src, template, .{i}); + field_inits[i] = try sema.addConstant(field_ty.toType(), default_val.toValue()); } - } else { - field_inits[i] = try sema.addConstant(struct_ty.structFieldType(i), default_val); } - } - } else { - const struct_obj = struct_ty.castTag(.@"struct").?.data; - for (struct_obj.fields.values(), 0..) |field, i| { - if (field_inits[i] != .none) continue; + }, + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + for (struct_obj.fields.values(), 0..) |field, i| { + if (field_inits[i] != .none) continue; - if (field.default_val.tag() == .unreachable_value) { - const field_name = struct_obj.fields.keys()[i]; - const template = "missing struct field: {s}"; - const args = .{field_name}; - if (root_msg) |msg| { - try sema.errNote(block, init_src, msg, template, args); + if (field.default_val == .none) { + const field_name = struct_obj.fields.keys()[i]; + const template = "missing struct field: {}"; + const args = .{field_name.fmt(ip)}; + if (root_msg) |msg| { + try sema.errNote(block, init_src, msg, template, args); + } else { + root_msg = try sema.errMsg(block, init_src, template, args); + } } else { - root_msg = try sema.errMsg(block, init_src, template, args); + field_inits[i] = try sema.addConstant(field.ty, field.default_val.toValue()); } - } else { - field_inits[i] = try sema.addConstant(field.ty, field.default_val); } - } + }, + else => unreachable, } if (root_msg) |msg| { - if (struct_ty.castTag(.@"struct")) |struct_obj| { - const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod); - defer gpa.free(fqn); - try sema.mod.errNoteNonLazy( - struct_obj.data.srcLoc(sema.mod), + if (mod.typeToStruct(struct_ty)) |struct_obj| { + const fqn = try struct_obj.getFullyQualifiedName(mod); + try mod.errNoteNonLazy( + struct_obj.srcLoc(mod), msg, - "struct '{s}' declared here", - .{fqn}, + "struct '{}' declared here", + .{fqn.fmt(ip)}, ); } root_msg = null; return sema.failWithOwnedErrorMsg(msg); } - const is_comptime = for (field_inits) |field_init| { + // Find which field forces the expression to be runtime, if any. + const opt_runtime_index = for (field_inits, 0..) |field_init, i| { if (!(try sema.isComptimeKnown(field_init))) { - break false; + break i; } - } else true; + } else null; - if (is_comptime) { - const values = try sema.arena.alloc(Value, field_inits.len); - for (field_inits, 0..) |field_init, i| { - values[i] = (sema.resolveMaybeUndefVal(field_init) catch unreachable).?; - } - const struct_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstantMaybeRef(block, struct_ty, struct_val, is_ref); - } + const runtime_index = opt_runtime_index orelse { + const elems = try sema.arena.alloc(InternPool.Index, field_inits.len); + for (elems, field_inits, 0..) |*elem, field_init, field_i| { + elem.* = try (sema.resolveMaybeUndefVal(field_init) catch unreachable).? + .intern(struct_ty.structFieldType(field_i, mod), mod); + } + const struct_val = try mod.intern(.{ .aggregate = .{ + .ty = struct_ty.toIntern(), + .storage = .{ .elems = elems }, + } }); + return sema.addConstantMaybeRef(block, struct_ty, struct_val.toValue(), is_ref); + }; if (is_ref) { try sema.resolveStructLayout(struct_ty); const target = sema.mod.getTarget(); - const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ + const alloc_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = struct_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); @@ -17883,10 +18929,18 @@ fn finishStructInit( try sema.storePtr(block, dest_src, field_ptr, field_init); } - return alloc; + return sema.makePtrConst(block, alloc); } - try sema.requireRuntimeBlock(block, dest_src, null); + sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = mod.declPtr(block.src_decl); + const field_src = mod.initSrc(dest_src.node_offset.x, decl, runtime_index); + try sema.requireRuntimeBlock(block, dest_src, field_src); + unreachable; + }, + else => |e| return e, + }; try sema.queueFullTypeResolution(struct_ty); return block.addAggregateInit(struct_ty, field_inits); } @@ -17897,78 +18951,85 @@ fn zirStructInitAnon( inst: Zir.Inst.Index, is_ref: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const gpa = sema.gpa; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); - const types = try sema.arena.alloc(Type, extra.data.fields_len); - const values = try sema.arena.alloc(Value, types.len); - var fields = std.StringArrayHashMapUnmanaged(u32){}; - defer fields.deinit(sema.gpa); - try fields.ensureUnusedCapacity(sema.gpa, types.len); + const types = try sema.arena.alloc(InternPool.Index, extra.data.fields_len); + const values = try sema.arena.alloc(InternPool.Index, types.len); + var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena); + try fields.ensureUnusedCapacity(types.len); + // Find which field forces the expression to be runtime, if any. const opt_runtime_index = rs: { var runtime_index: ?usize = null; var extra_index = extra.end; - for (types, 0..) |*field_ty, i| { + for (types, 0..) |*field_ty, i_usize| { + const i = @intCast(u32, i_usize); const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); extra_index = item.end; const name = sema.code.nullTerminatedString(item.data.field_name); - const gop = fields.getOrPutAssumeCapacity(name); + const name_ip = try mod.intern_pool.getOrPutString(gpa, name); + const gop = fields.getOrPutAssumeCapacity(name_ip); if (gop.found_existing) { const msg = msg: { - const decl = sema.mod.declPtr(block.src_decl); - const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, i); + const decl = mod.declPtr(block.src_decl); + const field_src = mod.initSrc(src.node_offset.x, decl, i); const msg = try sema.errMsg(block, field_src, "duplicate field", .{}); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); - const prev_source = Module.initSrc(src.node_offset.x, sema.gpa, decl, gop.value_ptr.*); + const prev_source = mod.initSrc(src.node_offset.x, decl, gop.value_ptr.*); try sema.errNote(block, prev_source, msg, "other field here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } - gop.value_ptr.* = @intCast(u32, i); + gop.value_ptr.* = i; const init = try sema.resolveInst(item.data.init); - field_ty.* = sema.typeOf(init); - if (types[i].zigTypeTag() == .Opaque) { + field_ty.* = sema.typeOf(init).toIntern(); + if (field_ty.toType().zigTypeTag(mod) == .Opaque) { const msg = msg: { - const decl = sema.mod.declPtr(block.src_decl); - const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, i); + const decl = mod.declPtr(block.src_decl); + const field_src = mod.initSrc(src.node_offset.x, decl, i); const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, types[i]); + try sema.addDeclaredHereNote(msg, field_ty.toType()); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } if (try sema.resolveMaybeUndefVal(init)) |init_val| { - values[i] = init_val; + values[i] = try init_val.intern(field_ty.toType(), mod); } else { - values[i] = Value.initTag(.unreachable_value); + values[i] = .none; runtime_index = i; } } break :rs runtime_index; }; - const tuple_ty = try Type.Tag.anon_struct.create(sema.arena, .{ - .names = try sema.arena.dupe([]const u8, fields.keys()), + const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ + .names = fields.keys(), .types = types, .values = values, - }); + } }); const runtime_index = opt_runtime_index orelse { - const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstantMaybeRef(block, tuple_ty, tuple_val, is_ref); + const tuple_val = try mod.intern(.{ .aggregate = .{ + .ty = tuple_ty, + .storage = .{ .elems = values }, + } }); + return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref); }; - sema.requireRuntimeBlock(block, src, .unneeded) catch |err| switch (err) { + sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { error.NeededSourceLocation => { - const decl = sema.mod.declPtr(block.src_decl); - const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, runtime_index); + const decl = mod.declPtr(block.src_decl); + const field_src = mod.initSrc(src.node_offset.x, decl, runtime_index); try sema.requireRuntimeBlock(block, src, field_src); unreachable; }, @@ -17976,9 +19037,9 @@ fn zirStructInitAnon( }; if (is_ref) { - const target = sema.mod.getTarget(); - const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = tuple_ty, + const target = mod.getTarget(); + const alloc_ty = try Type.ptr(sema.arena, mod, .{ + .pointee_type = tuple_ty.toType(), .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); const alloc = try block.addTy(.alloc, alloc_ty); @@ -17988,19 +19049,19 @@ fn zirStructInitAnon( const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); extra_index = item.end; - const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ + const field_ptr_ty = try Type.ptr(sema.arena, mod, .{ .mutable = true, .@"addrspace" = target_util.defaultAddressSpace(target, .local), - .pointee_type = field_ty, + .pointee_type = field_ty.toType(), }); - if (values[i].tag() == .unreachable_value) { + if (values[i] == .none) { const init = try sema.resolveInst(item.data.init); const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); _ = try block.addBinOp(.store, field_ptr, init); } } - return alloc; + return sema.makePtrConst(block, alloc); } const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len); @@ -18011,7 +19072,7 @@ fn zirStructInitAnon( element_refs[i] = try sema.resolveInst(item.data.init); } - return block.addAggregateInit(tuple_ty, element_refs); + return block.addAggregateInit(tuple_ty.toType(), element_refs); } fn zirArrayInit( @@ -18020,6 +19081,7 @@ fn zirArrayInit( inst: Zir.Inst.Index, is_ref: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const gpa = sema.gpa; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); @@ -18029,20 +19091,20 @@ fn zirArrayInit( assert(args.len >= 2); // array_ty + at least one element const array_ty = try sema.resolveType(block, src, args[0]); - const sentinel_val = array_ty.sentinel(); + const sentinel_val = array_ty.sentinel(mod); const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @boolToInt(sentinel_val != null)); defer gpa.free(resolved_args); for (args[1..], 0..) |arg, i| { const resolved_arg = try sema.resolveInst(arg); - const elem_ty = if (array_ty.zigTypeTag() == .Struct) - array_ty.structFieldType(i) + const elem_ty = if (array_ty.zigTypeTag(mod) == .Struct) + array_ty.structFieldType(i, mod) else - array_ty.elemType2(); + array_ty.elemType2(mod); resolved_args[i] = sema.coerce(block, elem_ty, resolved_arg, .unneeded) catch |err| switch (err) { error.NeededSourceLocation => { - const decl = sema.mod.declPtr(block.src_decl); - const elem_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, i); + const decl = mod.declPtr(block.src_decl); + const elem_src = mod.initSrc(src.node_offset.x, decl, i); _ = try sema.coerce(block, elem_ty, resolved_arg, elem_src); unreachable; }, @@ -18051,7 +19113,7 @@ fn zirArrayInit( } if (sentinel_val) |some| { - resolved_args[resolved_args.len - 1] = try sema.addConstant(array_ty.elemType2(), some); + resolved_args[resolved_args.len - 1] = try sema.addConstant(array_ty.elemType2(mod), some); } const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| { @@ -18060,21 +19122,25 @@ fn zirArrayInit( } else null; const runtime_index = opt_runtime_index orelse { - const elem_vals = try sema.arena.alloc(Value, resolved_args.len); - - for (resolved_args, 0..) |arg, i| { + const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len); + for (elem_vals, resolved_args, 0..) |*val, arg, i| { + const elem_ty = if (array_ty.zigTypeTag(mod) == .Struct) + array_ty.structFieldType(i, mod) + else + array_ty.elemType2(mod); // We checked that all args are comptime above. - elem_vals[i] = (sema.resolveMaybeUndefVal(arg) catch unreachable).?; + val.* = try ((sema.resolveMaybeUndefVal(arg) catch unreachable).?).intern(elem_ty, mod); } - - const array_val = try Value.Tag.aggregate.create(sema.arena, elem_vals); - return sema.addConstantMaybeRef(block, array_ty, array_val, is_ref); + return sema.addConstantMaybeRef(block, array_ty, (try mod.intern(.{ .aggregate = .{ + .ty = array_ty.toIntern(), + .storage = .{ .elems = elem_vals }, + } })).toValue(), is_ref); }; - sema.requireRuntimeBlock(block, src, .unneeded) catch |err| switch (err) { + sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { error.NeededSourceLocation => { - const decl = sema.mod.declPtr(block.src_decl); - const elem_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, runtime_index); + const decl = mod.declPtr(block.src_decl); + const elem_src = mod.initSrc(src.node_offset.x, decl, runtime_index); try sema.requireRuntimeBlock(block, src, elem_src); unreachable; }, @@ -18083,19 +19149,19 @@ fn zirArrayInit( try sema.queueFullTypeResolution(array_ty); if (is_ref) { - const target = sema.mod.getTarget(); - const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ + const target = mod.getTarget(); + const alloc_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = array_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); const alloc = try block.addTy(.alloc, alloc_ty); - if (array_ty.isTuple()) { + if (array_ty.isTuple(mod)) { for (resolved_args, 0..) |arg, i| { - const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ + const elem_ptr_ty = try Type.ptr(sema.arena, mod, .{ .mutable = true, .@"addrspace" = target_util.defaultAddressSpace(target, .local), - .pointee_type = array_ty.structFieldType(i), + .pointee_type = array_ty.structFieldType(i, mod), }); const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); @@ -18103,13 +19169,13 @@ fn zirArrayInit( const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); _ = try block.addBinOp(.store, elem_ptr, arg); } - return alloc; + return sema.makePtrConst(block, alloc); } - const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ + const elem_ptr_ty = try Type.ptr(sema.arena, mod, .{ .mutable = true, .@"addrspace" = target_util.defaultAddressSpace(target, .local), - .pointee_type = array_ty.elemType2(), + .pointee_type = array_ty.elemType2(mod), }); const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); @@ -18118,7 +19184,7 @@ fn zirArrayInit( const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); _ = try block.addBinOp(.store, elem_ptr, arg); } - return alloc; + return sema.makePtrConst(block, alloc); } return block.addAggregateInit(array_ty, resolved_args); @@ -18134,44 +19200,49 @@ fn zirArrayInitAnon( const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); const operands = sema.code.refSlice(extra.end, extra.data.operands_len); + const mod = sema.mod; - const types = try sema.arena.alloc(Type, operands.len); - const values = try sema.arena.alloc(Value, operands.len); + const types = try sema.arena.alloc(InternPool.Index, operands.len); + const values = try sema.arena.alloc(InternPool.Index, operands.len); const opt_runtime_src = rs: { var runtime_src: ?LazySrcLoc = null; for (operands, 0..) |operand, i| { const operand_src = src; // TODO better source location const elem = try sema.resolveInst(operand); - types[i] = sema.typeOf(elem); - if (types[i].zigTypeTag() == .Opaque) { + types[i] = sema.typeOf(elem).toIntern(); + if (types[i].toType().zigTypeTag(mod) == .Opaque) { const msg = msg: { const msg = try sema.errMsg(block, operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, types[i]); + try sema.addDeclaredHereNote(msg, types[i].toType()); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } if (try sema.resolveMaybeUndefVal(elem)) |val| { - values[i] = val; + values[i] = val.toIntern(); } else { - values[i] = Value.initTag(.unreachable_value); + values[i] = .none; runtime_src = operand_src; } } break :rs runtime_src; }; - const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ + const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ .types = types, .values = values, - }); + .names = &.{}, + } }); const runtime_src = opt_runtime_src orelse { - const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstantMaybeRef(block, tuple_ty, tuple_val, is_ref); + const tuple_val = try mod.intern(.{ .aggregate = .{ + .ty = tuple_ty, + .storage = .{ .elems = values }, + } }); + return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref); }; try sema.requireRuntimeBlock(block, src, runtime_src); @@ -18179,7 +19250,7 @@ fn zirArrayInitAnon( if (is_ref) { const target = sema.mod.getTarget(); const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = tuple_ty, + .pointee_type = tuple_ty.toType(), .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); const alloc = try block.addTy(.alloc, alloc_ty); @@ -18188,15 +19259,15 @@ fn zirArrayInitAnon( const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .mutable = true, .@"addrspace" = target_util.defaultAddressSpace(target, .local), - .pointee_type = types[i], + .pointee_type = types[i].toType(), }); - if (values[i].tag() == .unreachable_value) { + if (values[i] == .none) { const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand)); } } - return alloc; + return sema.makePtrConst(block, alloc); } const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); @@ -18204,7 +19275,7 @@ fn zirArrayInitAnon( element_refs[i] = try sema.resolveInst(operand); } - return block.addAggregateInit(tuple_ty, element_refs); + return block.addAggregateInit(tuple_ty.toType(), element_refs); } fn addConstantMaybeRef( @@ -18219,8 +19290,8 @@ fn addConstantMaybeRef( var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); const decl = try anon_decl.finish( - try ty.copy(anon_decl.arena()), - try val.copy(anon_decl.arena()), + ty, + val, 0, // default alignment ); return sema.analyzeDeclRef(decl); @@ -18232,18 +19303,27 @@ fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const ty_src = inst_data.src(); const field_src = inst_data.src(); const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); - const field_name = try sema.resolveConstString(block, field_src, extra.field_name, "field name must be comptime-known"); + const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, "field name must be comptime-known"); return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); } fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const ip = &mod.intern_pool; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; const ty_src = inst_data.src(); const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; - const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); - if (aggregate_ty.tag() == .var_args_param) return sema.addType(aggregate_ty); - const field_name = sema.code.nullTerminatedString(extra.name_start); + const aggregate_ty = sema.resolveType(block, ty_src, extra.container_type) catch |err| switch (err) { + // Since this is a ZIR instruction that returns a type, encountering + // generic poison should not result in a failed compilation, but the + // generic poison type. This prevents unnecessary failures when + // constructing types at compile-time. + error.GenericPoison => return Air.Inst.Ref.generic_poison_type, + else => |e| return e, + }; + const zir_field_name = sema.code.nullTerminatedString(extra.name_start); + const field_name = try ip.getOrPutString(sema.gpa, zir_field_name); return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); } @@ -18251,41 +19331,43 @@ fn fieldType( sema: *Sema, block: *Block, aggregate_ty: Type, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_src: LazySrcLoc, ty_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; var cur_ty = aggregate_ty; while (true) { const resolved_ty = try sema.resolveTypeFields(cur_ty); cur_ty = resolved_ty; - switch (cur_ty.zigTypeTag()) { - .Struct => { - if (cur_ty.isAnonStruct()) { + switch (cur_ty.zigTypeTag(mod)) { + .Struct => switch (mod.intern_pool.indexToKey(cur_ty.toIntern())) { + .anon_struct_type => |anon_struct| { const field_index = try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src); - return sema.addType(cur_ty.tupleFields().types[field_index]); - } - const struct_obj = cur_ty.castTag(.@"struct").?.data; - const field = struct_obj.fields.get(field_name) orelse - return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); - return sema.addType(field.ty); + return sema.addType(anon_struct.types[field_index].toType()); + }, + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + const field = struct_obj.fields.get(field_name) orelse + return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); + return sema.addType(field.ty); + }, + else => unreachable, }, .Union => { - const union_obj = cur_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(cur_ty).?; const field = union_obj.fields.get(field_name) orelse return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); return sema.addType(field.ty); }, .Optional => { - if (cur_ty.castTag(.optional)) |some| { - // Struct/array init through optional requires the child type to not be a pointer. - // If the child of .optional is a pointer it'll error on the next loop. - cur_ty = some.data; - continue; - } + // Struct/array init through optional requires the child type to not be a pointer. + // If the child of .optional is a pointer it'll error on the next loop. + cur_ty = mod.intern_pool.indexToKey(cur_ty.toIntern()).opt_type.toType(); + continue; }, .ErrorUnion => { - cur_ty = cur_ty.errorUnionPayload(); + cur_ty = cur_ty.errorUnionPayload(mod); continue; }, else => {}, @@ -18301,18 +19383,23 @@ fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { } fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { + const mod = sema.mod; const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); - const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty); + const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); + const opt_ptr_stack_trace_ty = try Type.optional(sema.arena, ptr_stack_trace_ty, mod); if (sema.owner_func != null and sema.owner_func.?.calls_or_awaits_errorable_fn and - sema.mod.comp.bin_file.options.error_return_tracing and - sema.mod.backendSupportsFeature(.error_return_trace)) + mod.comp.bin_file.options.error_return_tracing and + mod.backendSupportsFeature(.error_return_trace)) { return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); } - return sema.addConstant(opt_ptr_stack_trace_ty, Value.null); + return sema.addConstant(opt_ptr_stack_trace_ty, (try mod.intern(.{ .opt = .{ + .ty = opt_ptr_stack_trace_ty.toIntern(), + .val = .none, + } })).toValue()); } fn zirFrame( @@ -18325,27 +19412,28 @@ fn zirFrame( } fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ty = try sema.resolveType(block, operand_src, inst_data.operand); - if (ty.isNoReturn()) { + if (ty.isNoReturn(mod)) { return sema.fail(block, operand_src, "no align available for type '{}'", .{ty.fmt(sema.mod)}); } - const target = sema.mod.getTarget(); - const val = try ty.lazyAbiAlignment(target, sema.arena); - if (val.tag() == .lazy_align) { + const val = try ty.lazyAbiAlignment(mod); + if (val.isLazyAlign(mod)) { try sema.queueFullTypeResolution(ty); } return sema.addConstant(Type.comptime_int, val); } fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand = try sema.resolveInst(inst_data.operand); if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef()) return sema.addConstUndef(Type.initTag(.u1)); - const bool_ints = [2]Air.Inst.Ref{ .zero, .one }; - return bool_ints[@boolToInt(val.toBool())]; + if (val.isUndef(mod)) return sema.addConstUndef(Type.u1); + if (val.toBool()) return sema.addConstant(Type.u1, try mod.intValue(Type.u1, 1)); + return sema.addConstant(Type.u1, try mod.intValue(Type.u1, 0)); } return block.addUnOp(.bool_to_int, operand); } @@ -18356,8 +19444,8 @@ fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { - const bytes = val.castTag(.@"error").?.data.name; - return sema.addStrLit(block, bytes); + const err_name = sema.mod.intern_pool.indexToKey(val.toIntern()).err.name; + return sema.addStrLit(block, sema.mod.intern_pool.stringToSlice(err_name)); } // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass @@ -18375,16 +19463,17 @@ fn zirUnaryMath( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand = try sema.resolveInst(inst_data.operand); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_ty = sema.typeOf(operand); - switch (operand_ty.zigTypeTag()) { + switch (operand_ty.zigTypeTag(mod)) { .ComptimeFloat, .Float => {}, .Vector => { - const scalar_ty = operand_ty.scalarType(); - switch (scalar_ty.zigTypeTag()) { + const scalar_ty = operand_ty.scalarType(mod); + switch (scalar_ty.zigTypeTag(mod)) { .ComptimeFloat, .Float => {}, else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(sema.mod)}), } @@ -18392,25 +19481,27 @@ fn zirUnaryMath( else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(sema.mod)}), } - switch (operand_ty.zigTypeTag()) { + switch (operand_ty.zigTypeTag(mod)) { .Vector => { - const scalar_ty = operand_ty.scalarType(); - const vec_len = operand_ty.vectorLen(); - const result_ty = try Type.vector(sema.arena, vec_len, scalar_ty); + const scalar_ty = operand_ty.scalarType(mod); + const vec_len = operand_ty.vectorLen(mod); + const result_ty = try mod.vectorType(.{ + .len = vec_len, + .child = scalar_ty.toIntern(), + }); if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef()) + if (val.isUndef(mod)) return sema.addConstUndef(result_ty); - var elem_buf: Value.ElemValueBuffer = undefined; - const elems = try sema.arena.alloc(Value, vec_len); + const elems = try sema.arena.alloc(InternPool.Index, vec_len); for (elems, 0..) |*elem, i| { - const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf); - elem.* = try eval(elem_val, scalar_ty, sema.arena, sema.mod); + const elem_val = try val.elemValue(sema.mod, i); + elem.* = try (try eval(elem_val, scalar_ty, sema.arena, sema.mod)).intern(scalar_ty, mod); } - return sema.addConstant( - result_ty, - try Value.Tag.aggregate.create(sema.arena, elems), - ); + return sema.addConstant(result_ty, (try mod.intern(.{ .aggregate = .{ + .ty = result_ty.toIntern(), + .storage = .{ .elems = elems }, + } })).toValue()); } try sema.requireRuntimeBlock(block, operand_src, null); @@ -18418,7 +19509,7 @@ fn zirUnaryMath( }, .ComptimeFloat, .Float => { if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { - if (operand_val.isUndef()) + if (operand_val.isUndef(mod)) return sema.addConstUndef(operand_ty); const result_val = try eval(operand_val, operand_ty, sema.arena, sema.mod); return sema.addConstant(operand_ty, result_val); @@ -18438,16 +19529,17 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const mod = sema.mod; + const ip = &mod.intern_pool; try sema.resolveTypeLayout(operand_ty); - const enum_ty = switch (operand_ty.zigTypeTag()) { + const enum_ty = switch (operand_ty.zigTypeTag(mod)) { .EnumLiteral => { const val = try sema.resolveConstValue(block, .unneeded, operand, ""); - const bytes = val.castTag(.enum_literal).?.data; - return sema.addStrLit(block, bytes); + const tag_name = ip.indexToKey(val.toIntern()).enum_literal; + return sema.addStrLit(block, ip.stringToSlice(tag_name)); }, .Enum => operand_ty, - .Union => operand_ty.unionTagType() orelse { + .Union => operand_ty.unionTagType(mod) orelse { const msg = msg: { const msg = try sema.errMsg(block, src, "union '{}' is untagged", .{ operand_ty.fmt(sema.mod), @@ -18462,30 +19554,31 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air operand_ty.fmt(mod), }), }; - if (enum_ty.enumFieldCount() == 0) { + if (enum_ty.enumFieldCount(mod) == 0) { // TODO I don't think this is the correct way to handle this but // it prevents a crash. return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{}'", .{ enum_ty.fmt(mod), }); } - const enum_decl_index = enum_ty.getOwnerDecl(); + const enum_decl_index = enum_ty.getOwnerDecl(mod); const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse { const enum_decl = mod.declPtr(enum_decl_index); const msg = msg: { - const msg = try sema.errMsg(block, src, "no field with value '{}' in enum '{s}'", .{ - val.fmtValue(enum_ty, sema.mod), enum_decl.name, + const msg = try sema.errMsg(block, src, "no field with value '{}' in enum '{}'", .{ + val.fmtValue(enum_ty, sema.mod), enum_decl.name.fmt(ip), }); errdefer msg.destroy(sema.gpa); - try mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{}); + try mod.errNoteNonLazy(enum_decl.srcLoc(mod), msg, "declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); }; - const field_name = enum_ty.enumFieldName(field_index); - return sema.addStrLit(block, field_name); + // TODO: write something like getCoercedInts to avoid needing to dupe + const field_name = enum_ty.enumFieldName(field_index, mod); + return sema.addStrLit(block, ip.stringToSlice(field_name)); } try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety() and sema.mod.backendSupportsFeature(.is_named_enum_value)) { @@ -18498,8 +19591,15 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return block.addUnOp(.tag_name, casted_operand); } -fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirReify( + sema: *Sema, + block: *Block, + extended: Zir.Inst.Extended.InstData, + inst: Zir.Inst.Index, +) CompileError!Air.Inst.Ref { const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; const name_strategy = @intToEnum(Zir.Inst.NameStrategy, extended.small); const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); @@ -18508,10 +19608,10 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); const val = try sema.resolveConstValue(block, operand_src, type_info, "operand to @Type must be comptime-known"); - const union_val = val.cast(Value.Payload.Union).?.data; + const union_val = ip.indexToKey(val.toIntern()).un; const target = mod.getTarget(); - const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag, mod).?; - if (union_val.val.anyUndef(mod)) return sema.failWithUseOfUndef(block, src); + if (try union_val.val.toValue().anyUndef(mod)) return sema.failWithUseOfUndef(block, src); + const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag.toValue(), mod).?; switch (@intToEnum(std.builtin.TypeId, tag_index)) { .Type => return Air.Inst.Ref.type_type, .Void => return Air.Inst.Ref.void_type, @@ -18524,41 +19624,48 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in .AnyFrame => return sema.failWithUseOfAsync(block, src), .EnumLiteral => return Air.Inst.Ref.enum_literal_type, .Int => { - const struct_val = union_val.val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - const signedness_val = struct_val[0]; - const bits_val = struct_val[1]; - - const signedness = signedness_val.toEnum(std.builtin.Signedness); - const bits = @intCast(u16, bits_val.toUnsignedInt(target)); - const ty = switch (signedness) { - .signed => try Type.Tag.int_signed.create(sema.arena, bits), - .unsigned => try Type.Tag.int_unsigned.create(sema.arena, bits), - }; + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const signedness_val = try union_val.val.toValue().fieldValue( + mod, + fields.getIndex(try ip.getOrPutString(gpa, "signedness")).?, + ); + const bits_val = try union_val.val.toValue().fieldValue( + mod, + fields.getIndex(try ip.getOrPutString(gpa, "bits")).?, + ); + + const signedness = mod.toEnum(std.builtin.Signedness, signedness_val); + const bits = @intCast(u16, bits_val.toUnsignedInt(mod)); + const ty = try mod.intType(signedness, bits); return sema.addType(ty); }, .Vector => { - const struct_val = union_val.val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - const len_val = struct_val[0]; - const child_val = struct_val[1]; + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const len_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "len"), + ).?); + const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "child"), + ).?); - const len = len_val.toUnsignedInt(target); - var buffer: Value.ToTypeBuffer = undefined; - const child_ty = child_val.toType(&buffer); + const len = @intCast(u32, len_val.toUnsignedInt(mod)); + const child_ty = child_val.toType(); try sema.checkVectorElemType(block, src, child_ty); - const ty = try Type.vector(sema.arena, len, try child_ty.copy(sema.arena)); + const ty = try mod.vectorType(.{ + .len = len, + .child = child_ty.toIntern(), + }); return sema.addType(ty); }, .Float => { - const struct_val = union_val.val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // bits: comptime_int, - const bits_val = struct_val[0]; + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const bits_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "bits"), + ).?); - const bits = @intCast(u16, bits_val.toUnsignedInt(target)); + const bits = @intCast(u16, bits_val.toUnsignedInt(mod)); const ty = switch (bits) { 16 => Type.f16, 32 => Type.f32, @@ -18570,25 +19677,42 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in return sema.addType(ty); }, .Pointer => { - const struct_val = union_val.val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - const size_val = struct_val[0]; - const is_const_val = struct_val[1]; - const is_volatile_val = struct_val[2]; - const alignment_val = struct_val[3]; - const address_space_val = struct_val[4]; - const child_val = struct_val[5]; - const is_allowzero_val = struct_val[6]; - const sentinel_val = struct_val[7]; + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const size_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "size"), + ).?); + const is_const_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "is_const"), + ).?); + const is_volatile_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "is_volatile"), + ).?); + const alignment_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "alignment"), + ).?); + const address_space_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "address_space"), + ).?); + const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "child"), + ).?); + const is_allowzero_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "is_allowzero"), + ).?); + const sentinel_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "sentinel"), + ).?); if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { return sema.fail(block, src, "alignment must fit in 'u32'", .{}); } - const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema)).?); - var buffer: Value.ToTypeBuffer = undefined; - const unresolved_elem_ty = child_val.toType(&buffer); - const elem_ty = if (abi_align == 0) + const abi_align = InternPool.Alignment.fromByteUnits( + (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?, + ); + + const unresolved_elem_ty = child_val.toType(); + const elem_ty = if (abi_align == .none) unresolved_elem_ty else t: { const elem_ty = try sema.resolveTypeFields(unresolved_elem_ty); @@ -18596,301 +19720,282 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in break :t elem_ty; }; - const ptr_size = size_val.toEnum(std.builtin.Type.Pointer.Size); + const ptr_size = mod.toEnum(std.builtin.Type.Pointer.Size, size_val); - var actual_sentinel: ?Value = null; - if (!sentinel_val.isNull()) { - if (ptr_size == .One or ptr_size == .C) { - return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{}); + const actual_sentinel: InternPool.Index = s: { + if (!sentinel_val.isNull(mod)) { + if (ptr_size == .One or ptr_size == .C) { + return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{}); + } + const sentinel_ptr_val = sentinel_val.optionalValue(mod).?; + const ptr_ty = try Type.ptr(sema.arena, mod, .{ + .@"addrspace" = .generic, + .pointee_type = elem_ty, + }); + const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?; + break :s sent_val.toIntern(); } - const sentinel_ptr_val = sentinel_val.castTag(.opt_payload).?.data; - const ptr_ty = try Type.ptr(sema.arena, mod, .{ - .@"addrspace" = .generic, - .pointee_type = try elem_ty.copy(sema.arena), - }); - actual_sentinel = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?; - } + break :s .none; + }; - if (elem_ty.zigTypeTag() == .NoReturn) { + if (elem_ty.zigTypeTag(mod) == .NoReturn) { return sema.fail(block, src, "pointer to noreturn not allowed", .{}); - } else if (elem_ty.zigTypeTag() == .Fn) { + } else if (elem_ty.zigTypeTag(mod) == .Fn) { if (ptr_size != .One) { return sema.fail(block, src, "function pointers must be single pointers", .{}); } - const fn_align = elem_ty.fnInfo().alignment; - if (abi_align != 0 and fn_align != 0 and + const fn_align = mod.typeToFunc(elem_ty).?.alignment; + if (abi_align != .none and fn_align != .none and abi_align != fn_align) { return sema.fail(block, src, "function pointer alignment disagrees with function alignment", .{}); } - } else if (ptr_size == .Many and elem_ty.zigTypeTag() == .Opaque) { + } else if (ptr_size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{}); } else if (ptr_size == .C) { if (!try sema.validateExternType(elem_ty, .other)) { const msg = msg: { - const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); + const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)}); + errdefer msg.destroy(gpa); - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), elem_ty, .other); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), elem_ty, .other); try sema.addDeclaredHereNote(msg, elem_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } - if (elem_ty.zigTypeTag() == .Opaque) { + if (elem_ty.zigTypeTag(mod) == .Opaque) { return sema.fail(block, src, "C pointers cannot point to opaque types", .{}); } } - const ty = try Type.ptr(sema.arena, mod, .{ - .size = ptr_size, - .mutable = !is_const_val.toBool(), - .@"volatile" = is_volatile_val.toBool(), - .@"align" = abi_align, - .@"addrspace" = address_space_val.toEnum(std.builtin.AddressSpace), - .pointee_type = try elem_ty.copy(sema.arena), - .@"allowzero" = is_allowzero_val.toBool(), + const ty = try mod.ptrType(.{ + .child = elem_ty.toIntern(), .sentinel = actual_sentinel, + .flags = .{ + .size = ptr_size, + .is_const = is_const_val.toBool(), + .is_volatile = is_volatile_val.toBool(), + .alignment = abi_align, + .address_space = mod.toEnum(std.builtin.AddressSpace, address_space_val), + .is_allowzero = is_allowzero_val.toBool(), + }, }); return sema.addType(ty); }, .Array => { - const struct_val = union_val.val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // len: comptime_int, - const len_val = struct_val[0]; - // child: type, - const child_val = struct_val[1]; - // sentinel: ?*const anyopaque, - const sentinel_val = struct_val[2]; - - const len = len_val.toUnsignedInt(target); - var buffer: Value.ToTypeBuffer = undefined; - const child_ty = try child_val.toType(&buffer).copy(sema.arena); - const sentinel = if (sentinel_val.castTag(.opt_payload)) |p| blk: { + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const len_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "len"), + ).?); + const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "child"), + ).?); + const sentinel_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "sentinel"), + ).?); + + const len = len_val.toUnsignedInt(mod); + const child_ty = child_val.toType(); + const sentinel = if (sentinel_val.optionalValue(mod)) |p| blk: { const ptr_ty = try Type.ptr(sema.arena, mod, .{ .@"addrspace" = .generic, .pointee_type = child_ty, }); - break :blk (try sema.pointerDeref(block, src, p.data, ptr_ty)).?; + break :blk (try sema.pointerDeref(block, src, p, ptr_ty)).?; } else null; - const ty = try Type.array(sema.arena, len, sentinel, child_ty, sema.mod); + const ty = try Type.array(sema.arena, len, sentinel, child_ty, mod); return sema.addType(ty); }, .Optional => { - const struct_val = union_val.val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // child: type, - const child_val = struct_val[0]; + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "child"), + ).?); - var buffer: Value.ToTypeBuffer = undefined; - const child_ty = try child_val.toType(&buffer).copy(sema.arena); + const child_ty = child_val.toType(); - const ty = try Type.optional(sema.arena, child_ty); + const ty = try Type.optional(sema.arena, child_ty, mod); return sema.addType(ty); }, .ErrorUnion => { - const struct_val = union_val.val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // error_set: type, - const error_set_val = struct_val[0]; - // payload: type, - const payload_val = struct_val[1]; - - var buffer: Value.ToTypeBuffer = undefined; - const error_set_ty = try error_set_val.toType(&buffer).copy(sema.arena); - const payload_ty = try payload_val.toType(&buffer).copy(sema.arena); - - if (error_set_ty.zigTypeTag() != .ErrorSet) { + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const error_set_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "error_set"), + ).?); + const payload_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "payload"), + ).?); + + const error_set_ty = error_set_val.toType(); + const payload_ty = payload_val.toType(); + + if (error_set_ty.zigTypeTag(mod) != .ErrorSet) { return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{}); } - const ty = try Type.Tag.error_union.create(sema.arena, .{ - .error_set = error_set_ty, - .payload = payload_ty, - }); + const ty = try mod.errorUnionType(error_set_ty, payload_ty); return sema.addType(ty); }, .ErrorSet => { - const payload_val = union_val.val.optionalValue() orelse - return sema.addType(Type.initTag(.anyerror)); - const slice_val = payload_val.castTag(.slice).?.data; + const payload_val = union_val.val.toValue().optionalValue(mod) orelse + return sema.addType(Type.anyerror); - const len = try sema.usizeCast(block, src, slice_val.len.toUnsignedInt(mod.getTarget())); - var names: Module.ErrorSet.NameMap = .{}; + const len = try sema.usizeCast(block, src, payload_val.sliceLen(mod)); + var names: Module.Fn.InferredErrorSet.NameMap = .{}; try names.ensureUnusedCapacity(sema.arena, len); - var i: usize = 0; - while (i < len) : (i += 1) { - var buf: Value.ElemValueBuffer = undefined; - const elem_val = slice_val.ptr.elemValueBuffer(mod, i, &buf); - const struct_val = elem_val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // error_set: type, - const name_val = struct_val[0]; - const name_str = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, sema.mod); - - const kv = try mod.getErrorValue(name_str); - const gop = names.getOrPutAssumeCapacity(kv.key); + for (0..len) |i| { + const elem_val = try payload_val.elemValue(mod, i); + const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); + const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "name"), + ).?); + + const name = try name_val.toIpString(Type.slice_const_u8, mod); + _ = try mod.getErrorValue(name); + const gop = names.getOrPutAssumeCapacity(name); if (gop.found_existing) { - return sema.fail(block, src, "duplicate error '{s}'", .{name_str}); + return sema.fail(block, src, "duplicate error '{}'", .{ + name.fmt(ip), + }); } } - // names must be sorted - Module.ErrorSet.sortNames(&names); - const ty = try Type.Tag.error_set_merged.create(sema.arena, names); + const ty = try mod.errorSetFromUnsortedNames(names.keys()); return sema.addType(ty); }, .Struct => { - // TODO use reflection instead of magic numbers here - const struct_val = union_val.val.castTag(.aggregate).?.data; - // layout: containerlayout, - const layout_val = struct_val[0]; - // backing_int: ?type, - const backing_int_val = struct_val[1]; - // fields: []const enumfield, - const fields_val = struct_val[2]; - // decls: []const declaration, - const decls_val = struct_val[3]; - // is_tuple: bool, - const is_tuple_val = struct_val[4]; - assert(struct_val.len == 5); - - const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout); + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const layout_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "layout"), + ).?); + const backing_integer_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "backing_integer"), + ).?); + const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "fields"), + ).?); + const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "decls"), + ).?); + const is_tuple_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "is_tuple"), + ).?); + + const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); // Decls if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified structs must have no decls", .{}); } - if (layout != .Packed and !backing_int_val.isNull()) { + if (layout != .Packed and !backing_integer_val.isNull(mod)) { return sema.fail(block, src, "non-packed struct does not support backing integer type", .{}); } - return try sema.reifyStruct(block, inst, src, layout, backing_int_val, fields_val, name_strategy, is_tuple_val.toBool()); + return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_val, name_strategy, is_tuple_val.toBool()); }, .Enum => { - const struct_val: []const Value = union_val.val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // tag_type: type, - const tag_type_val = struct_val[0]; - // fields: []const EnumField, - const fields_val = struct_val[1]; - // decls: []const Declaration, - const decls_val = struct_val[2]; - // is_exhaustive: bool, - const is_exhaustive_val = struct_val[3]; + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const tag_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "tag_type"), + ).?); + const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "fields"), + ).?); + const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "decls"), + ).?); + const is_exhaustive_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "is_exhaustive"), + ).?); // Decls if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified enums must have no decls", .{}); } - const gpa = sema.gpa; - var new_decl_arena = std.heap.ArenaAllocator.init(gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); + const int_tag_ty = tag_type_val.toType(); + if (int_tag_ty.zigTypeTag(mod) != .Int) { + return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); + } + + // Because these things each reference each other, `undefined` + // placeholders are used before being set after the enum type gains + // an InternPool index. - // Define our empty enum decl - const enum_obj = try new_decl_arena_allocator.create(Module.EnumFull); - const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumFull); - enum_ty_payload.* = .{ - .base = .{ - .tag = if (!is_exhaustive_val.toBool()) - .enum_nonexhaustive - else - .enum_full, - }, - .data = enum_obj, - }; - const enum_ty = Type.initPayload(&enum_ty_payload.base); - const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.type, - .val = enum_val, + .ty = Type.noreturn, + .val = Value.@"unreachable", }, name_strategy, "enum", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl_index); - - enum_obj.* = .{ - .owner_decl = new_decl_index, - .tag_ty = Type.null, - .tag_ty_inferred = false, - .fields = .{}, - .values = .{}, - .namespace = .{ - .parent = block.namespace, - .ty = enum_ty, - .file_scope = block.getFileScope(), - }, - }; - - // Enum tag type - var buffer: Value.ToTypeBuffer = undefined; - const int_tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator); - - if (int_tag_ty.zigTypeTag() != .Int) { - return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); + errdefer { + new_decl.has_tv = false; // namespace and val were destroyed by later errdefers + mod.abortAnonDecl(new_decl_index); } - enum_obj.tag_ty = int_tag_ty; - // Fields - const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); - try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); - try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ - .ty = enum_obj.tag_ty, - .mod = mod, + // Define our empty enum decl + const fields_len = @intCast(u32, try sema.usizeCast(block, src, fields_val.sliceLen(mod))); + const incomplete_enum = try ip.getIncompleteEnum(gpa, .{ + .decl = new_decl_index, + .namespace = .none, + .fields_len = fields_len, + .has_values = true, + .tag_mode = if (!is_exhaustive_val.toBool()) + .nonexhaustive + else + .explicit, + .tag_ty = int_tag_ty.toIntern(), }); + // TODO: figure out InternPool removals for incremental compilation + //errdefer ip.remove(incomplete_enum.index); - var field_i: usize = 0; - while (field_i < fields_len) : (field_i += 1) { - const elem_val = try fields_val.elemValue(sema.mod, sema.arena, field_i); - const field_struct_val: []const Value = elem_val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // name: []const u8 - const name_val = field_struct_val[0]; - // value: comptime_int - const value_val = field_struct_val[1]; - - const field_name = try name_val.toAllocatedBytes( - Type.initTag(.const_slice_u8), - new_decl_arena_allocator, - sema.mod, - ); + new_decl.ty = Type.type; + new_decl.val = incomplete_enum.index.toValue(); + + for (0..fields_len) |field_i| { + const elem_val = try fields_val.elemValue(mod, field_i); + const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); + const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "name"), + ).?); + const value_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "value"), + ).?); - if (!try sema.intFitsInType(value_val, enum_obj.tag_ty, null)) { + const field_name = try name_val.toIpString(Type.slice_const_u8, mod); + + if (!try sema.intFitsInType(value_val, int_tag_ty, null)) { // TODO: better source location - return sema.fail(block, src, "field '{s}' with enumeration value '{}' is too large for backing int type '{}'", .{ - field_name, + return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{ + field_name.fmt(ip), value_val.fmtValue(Type.comptime_int, mod), - enum_obj.tag_ty.fmt(mod), + int_tag_ty.fmt(mod), }); } - const gop_field = enum_obj.fields.getOrPutAssumeCapacity(field_name); - if (gop_field.found_existing) { + if (try incomplete_enum.addFieldName(ip, gpa, field_name)) |other_index| { const msg = msg: { - const msg = try sema.errMsg(block, src, "duplicate enum field '{s}'", .{field_name}); + const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{ + field_name.fmt(ip), + }); errdefer msg.destroy(gpa); + _ = other_index; // TODO: this note is incorrect try sema.errNote(block, src, msg, "other field here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } - const copied_tag_val = try value_val.copy(new_decl_arena_allocator); - const gop_val = enum_obj.values.getOrPutAssumeCapacityContext(copied_tag_val, .{ - .ty = enum_obj.tag_ty, - .mod = mod, - }); - if (gop_val.found_existing) { + if (try incomplete_enum.addFieldValue(ip, gpa, (try mod.getCoerced(value_val, int_tag_ty)).toIntern())) |other| { const msg = msg: { const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{value_val.fmtValue(Type.comptime_int, mod)}); errdefer msg.destroy(gpa); + _ = other; // TODO: this note is incorrect try sema.errNote(block, src, msg, "other enum tag value here", .{}); break :msg msg; }; @@ -18898,182 +20003,209 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in } } - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; }, .Opaque => { - const struct_val = union_val.val.castTag(.aggregate).?.data; - // decls: []const Declaration, - const decls_val = struct_val[0]; + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "decls"), + ).?); // Decls if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified opaque must have no decls", .{}); } - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); + // Because these three things each reference each other, + // `undefined` placeholders are used in two places before being set + // after the opaque type gains an InternPool index. - const opaque_obj = try new_decl_arena_allocator.create(Module.Opaque); - const opaque_ty_payload = try new_decl_arena_allocator.create(Type.Payload.Opaque); - opaque_ty_payload.* = .{ - .base = .{ .tag = .@"opaque" }, - .data = opaque_obj, - }; - const opaque_ty = Type.initPayload(&opaque_ty_payload.base); - const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.type, - .val = opaque_val, + .ty = Type.noreturn, + .val = Value.@"unreachable", }, name_strategy, "opaque", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl_index); + errdefer { + new_decl.has_tv = false; // namespace and val were destroyed by later errdefers + mod.abortAnonDecl(new_decl_index); + } - opaque_obj.* = .{ - .owner_decl = new_decl_index, - .namespace = .{ - .parent = block.namespace, - .ty = opaque_ty, - .file_scope = block.getFileScope(), - }, - }; + const new_namespace_index = try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .ty = undefined, + .file_scope = block.getFileScope(mod), + }); + const new_namespace = mod.namespacePtr(new_namespace_index); + errdefer mod.destroyNamespace(new_namespace_index); + + const opaque_ty = try mod.intern(.{ .opaque_type = .{ + .decl = new_decl_index, + .namespace = new_namespace_index, + } }); + // TODO: figure out InternPool removals for incremental compilation + //errdefer ip.remove(opaque_ty); + + new_decl.ty = Type.type; + new_decl.val = opaque_ty.toValue(); + new_namespace.ty = opaque_ty.toType(); - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; }, .Union => { - // TODO use reflection instead of magic numbers here - const struct_val = union_val.val.castTag(.aggregate).?.data; - // layout: containerlayout, - const layout_val = struct_val[0]; - // tag_type: ?type, - const tag_type_val = struct_val[1]; - // fields: []const enumfield, - const fields_val = struct_val[2]; - // decls: []const declaration, - const decls_val = struct_val[3]; + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const layout_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "layout"), + ).?); + const tag_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "tag_type"), + ).?); + const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "fields"), + ).?); + const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "decls"), + ).?); // Decls if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified unions must have no decls", .{}); } - const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout); + const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); + // Because these three things each reference each other, `undefined` + // placeholders are used before being set after the union type gains an + // InternPool index. - const union_obj = try new_decl_arena_allocator.create(Module.Union); - const type_tag = if (!tag_type_val.isNull()) - Type.Tag.union_tagged - else if (layout != .Auto) - Type.Tag.@"union" - else switch (block.sema.mod.optimizeMode()) { - .Debug, .ReleaseSafe => Type.Tag.union_safety_tagged, - .ReleaseFast, .ReleaseSmall => Type.Tag.@"union", - }; - const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union); - union_payload.* = .{ - .base = .{ .tag = type_tag }, - .data = union_obj, - }; - const union_ty = Type.initPayload(&union_payload.base); - const new_union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.type, - .val = new_union_val, + .ty = Type.noreturn, + .val = Value.@"unreachable", }, name_strategy, "union", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl_index); - union_obj.* = .{ + errdefer { + new_decl.has_tv = false; // namespace and val were destroyed by later errdefers + mod.abortAnonDecl(new_decl_index); + } + + const new_namespace_index = try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .ty = undefined, + .file_scope = block.getFileScope(mod), + }); + const new_namespace = mod.namespacePtr(new_namespace_index); + errdefer mod.destroyNamespace(new_namespace_index); + + const union_index = try mod.createUnion(.{ .owner_decl = new_decl_index, - .tag_ty = Type.initTag(.null), + .tag_ty = Type.null, .fields = .{}, .zir_index = inst, .layout = layout, .status = .have_field_types, - .namespace = .{ - .parent = block.namespace, - .ty = union_ty, - .file_scope = block.getFileScope(), + .namespace = new_namespace_index, + }); + const union_obj = mod.unionPtr(union_index); + errdefer mod.destroyUnion(union_index); + + const union_ty = try ip.get(gpa, .{ .union_type = .{ + .index = union_index, + .runtime_tag = if (!tag_type_val.isNull(mod)) + .tagged + else if (layout != .Auto) + .none + else switch (mod.optimizeMode()) { + .Debug, .ReleaseSafe => .safety, + .ReleaseFast, .ReleaseSmall => .none, }, - }; + } }); + // TODO: figure out InternPool removals for incremental compilation + //errdefer ip.remove(union_ty); + + new_decl.ty = Type.type; + new_decl.val = union_ty.toValue(); + new_namespace.ty = union_ty.toType(); // Tag type - var tag_ty_field_names: ?Module.EnumFull.NameMap = null; - var enum_field_names: ?*Module.EnumNumbered.NameMap = null; const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); - if (tag_type_val.optionalValue()) |payload_val| { - var buffer: Value.ToTypeBuffer = undefined; - union_obj.tag_ty = try payload_val.toType(&buffer).copy(new_decl_arena_allocator); + var explicit_tags_seen: []bool = &.{}; + var enum_field_names: []InternPool.NullTerminatedString = &.{}; + if (tag_type_val.optionalValue(mod)) |payload_val| { + union_obj.tag_ty = payload_val.toType(); + + const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) { + .enum_type => |x| x, + else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), + }; - if (union_obj.tag_ty.zigTypeTag() != .Enum) { - return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}); - } - tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(sema.arena); + explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); + @memset(explicit_tags_seen, false); } else { - union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, fields_len, null); - enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields; + enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); } // Fields - try union_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); - - var i: usize = 0; - while (i < fields_len) : (i += 1) { - const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i); - const field_struct_val = elem_val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // name: []const u8 - const name_val = field_struct_val[0]; - // type: type, - const type_val = field_struct_val[1]; - // alignment: comptime_int, - const alignment_val = field_struct_val[2]; - - const field_name = try name_val.toAllocatedBytes( - Type.initTag(.const_slice_u8), - new_decl_arena_allocator, - sema.mod, - ); - - if (enum_field_names) |set| { - set.putAssumeCapacity(field_name, {}); - } - - if (tag_ty_field_names) |*names| { - const enum_has_field = names.orderedRemove(field_name); - if (!enum_has_field) { + try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); + + for (0..fields_len) |i| { + const elem_val = try fields_val.elemValue(mod, i); + const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); + const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "name"), + ).?); + const type_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "type"), + ).?); + const alignment_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "alignment"), + ).?); + + const field_name = try name_val.toIpString(Type.slice_const_u8, mod); + + if (enum_field_names.len != 0) { + enum_field_names[i] = field_name; + } + + if (explicit_tags_seen.len > 0) { + const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; + const enum_index = tag_info.nameIndex(ip, field_name) orelse { const msg = msg: { - const msg = try sema.errMsg(block, src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) }); - errdefer msg.destroy(sema.gpa); + const msg = try sema.errMsg(block, src, "no field named '{}' in enum '{}'", .{ + field_name.fmt(ip), + union_obj.tag_ty.fmt(mod), + }); + errdefer msg.destroy(gpa); try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } + }; + // 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; } const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { // TODO: better source location - return sema.fail(block, src, "duplicate union field {s}", .{field_name}); + return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)}); } - var buffer: Value.ToTypeBuffer = undefined; - const field_ty = try type_val.toType(&buffer).copy(new_decl_arena_allocator); + const field_ty = type_val.toType(); gop.value_ptr.* = .{ .ty = field_ty, - .abi_align = @intCast(u32, (try alignment_val.getUnsignedIntAdvanced(target, sema)).?), + .abi_align = @intCast(u32, (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?), }; - if (field_ty.zigTypeTag() == .Opaque) { + if (field_ty.zigTypeTag(mod) == .Opaque) { const msg = msg: { const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; @@ -19082,23 +20214,23 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in } if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { const msg = msg: { - const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); + const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); + errdefer msg.destroy(gpa); - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), field_ty, .union_field); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty))) { + } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { const msg = msg: { - const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); + const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); + errdefer msg.destroy(gpa); - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl), field_ty); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; @@ -19107,47 +20239,61 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in } } - if (tag_ty_field_names) |names| { - if (names.count() > 0) { + if (explicit_tags_seen.len > 0) { + const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; + if (tag_info.names.len > fields_len) { const msg = msg: { const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{}); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); const enum_ty = union_obj.tag_ty; - for (names.keys()) |field_name| { - const field_index = enum_ty.enumFieldIndex(field_name).?; - try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name}); + for (tag_info.names, 0..) |field_name, field_index| { + if (explicit_tags_seen[field_index]) continue; + try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{ + field_name.fmt(ip), + }); } try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } + } else { + union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, null); } - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; }, .Fn => { - const struct_val: []const Value = union_val.val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // calling_convention: CallingConvention, - const cc = struct_val[0].toEnum(std.builtin.CallingConvention); - // alignment: comptime_int, - const alignment_val = struct_val[1]; - // is_generic: bool, - const is_generic = struct_val[2].toBool(); - // is_var_args: bool, - const is_var_args = struct_val[3].toBool(); - // return_type: ?type, - const return_type_val = struct_val[4]; - // args: []const Param, - const args_val = struct_val[5]; - + const fields = ip.typeOf(union_val.val).toType().structFields(mod); + const calling_convention_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "calling_convention"), + ).?); + const alignment_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "alignment"), + ).?); + const is_generic_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "is_generic"), + ).?); + const is_var_args_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "is_var_args"), + ).?); + const return_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "return_type"), + ).?); + const params_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( + try ip.getOrPutString(gpa, "params"), + ).?); + + const is_generic = is_generic_val.toBool(); if (is_generic) { return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{}); } + const is_var_args = is_var_args_val.toBool(); + const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val); if (is_var_args and cc != .C) { return sema.fail(block, src, "varargs functions must have C calling convention", .{}); } @@ -19156,74 +20302,65 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { return sema.fail(block, src, "alignment must fit in 'u32'", .{}); } - const alignment = @intCast(u29, alignment_val.toUnsignedInt(target)); + const alignment = @intCast(u29, alignment_val.toUnsignedInt(mod)); if (alignment == target_util.defaultFunctionAlignment(target)) { - break :alignment 0; + break :alignment .none; } else { - break :alignment alignment; + break :alignment InternPool.Alignment.fromByteUnits(alignment); } }; - const return_type = return_type_val.optionalValue() orelse + const return_type = return_type_val.optionalValue(mod) orelse return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{}); - var buf: Value.ToTypeBuffer = undefined; - - const args_slice_val = args_val.castTag(.slice).?.data; - const args_len = try sema.usizeCast(block, src, args_slice_val.len.toUnsignedInt(mod.getTarget())); - - const param_types = try sema.arena.alloc(Type, args_len); - const comptime_params = try sema.arena.alloc(bool, args_len); + const args_len = try sema.usizeCast(block, src, params_val.sliceLen(mod)); + const param_types = try sema.arena.alloc(InternPool.Index, args_len); var noalias_bits: u32 = 0; - var i: usize = 0; - while (i < args_len) : (i += 1) { - var arg_buf: Value.ElemValueBuffer = undefined; - const arg = args_slice_val.ptr.elemValueBuffer(mod, i, &arg_buf); - const arg_val = arg.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // is_generic: bool, - const arg_is_generic = arg_val[0].toBool(); - // is_noalias: bool, - const arg_is_noalias = arg_val[1].toBool(); - // type: ?type, - const param_type_opt_val = arg_val[2]; - - if (arg_is_generic) { + for (param_types, 0..) |*param_type, i| { + const elem_val = try params_val.elemValue(mod, i); + const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); + const param_is_generic_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "is_generic"), + ).?); + const param_is_noalias_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "is_noalias"), + ).?); + const opt_param_type_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "type"), + ).?); + + if (param_is_generic_val.toBool()) { return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{}); } - const param_type_val = param_type_opt_val.optionalValue() orelse + const param_type_val = opt_param_type_val.optionalValue(mod) orelse return sema.fail(block, src, "Type.Fn.Param.arg_type must be non-null for @Type", .{}); - const param_type = try param_type_val.toType(&buf).copy(sema.arena); + param_type.* = param_type_val.toIntern(); - if (arg_is_noalias) { - if (!param_type.isPtrAtRuntime()) { + if (param_is_noalias_val.toBool()) { + if (!param_type.toType().isPtrAtRuntime(mod)) { return sema.fail(block, src, "non-pointer parameter declared noalias", .{}); } noalias_bits |= @as(u32, 1) << (std.math.cast(u5, i) orelse return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); } - - param_types[i] = param_type; - comptime_params[i] = false; } - var fn_info = Type.Payload.Function.Data{ + const ty = try mod.funcType(.{ .param_types = param_types, - .comptime_params = comptime_params.ptr, + .comptime_bits = 0, .noalias_bits = noalias_bits, - .return_type = try return_type.toType(&buf).copy(sema.arena), + .return_type = return_type.toIntern(), .alignment = alignment, .cc = cc, .is_var_args = is_var_args, .is_generic = false, + .is_noinline = false, .align_is_generic = false, .cc_is_generic = false, .section_is_generic = false, .addrspace_is_generic = false, - }; - - const ty = try Type.Tag.function.create(sema.arena, fn_info); + }); return sema.addType(ty); }, .Frame => return sema.failWithUseOfAsync(block, src), @@ -19241,22 +20378,34 @@ fn reifyStruct( name_strategy: Zir.Inst.NameStrategy, is_tuple: bool, ) CompileError!Air.Inst.Ref { - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); - - const struct_obj = try new_decl_arena_allocator.create(Module.Struct); - const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); - const new_struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty); const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; + + // Because these three things each reference each other, `undefined` + // placeholders are used before being set after the struct type gains an + // InternPool index. + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.type, - .val = new_struct_val, + .ty = Type.noreturn, + .val = Value.@"unreachable", }, name_strategy, "struct", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl_index); - struct_obj.* = .{ + errdefer { + new_decl.has_tv = false; // namespace and val were destroyed by later errdefers + mod.abortAnonDecl(new_decl_index); + } + + const new_namespace_index = try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .ty = undefined, + .file_scope = block.getFileScope(mod), + }); + const new_namespace = mod.namespacePtr(new_namespace_index); + errdefer mod.destroyNamespace(new_namespace_index); + + const struct_index = try mod.createStruct(.{ .owner_decl = new_decl_index, .fields = .{}, .zir_index = inst, @@ -19264,38 +20413,49 @@ fn reifyStruct( .status = .have_field_types, .known_non_opv = false, .is_tuple = is_tuple, - .namespace = .{ - .parent = block.namespace, - .ty = struct_ty, - .file_scope = block.getFileScope(), - }, - }; + .namespace = new_namespace_index, + }); + const struct_obj = mod.structPtr(struct_index); + errdefer mod.destroyStruct(struct_index); - const target = mod.getTarget(); + const struct_ty = try ip.get(gpa, .{ .struct_type = .{ + .index = struct_index.toOptional(), + .namespace = new_namespace_index.toOptional(), + } }); + // TODO: figure out InternPool removals for incremental compilation + //errdefer ip.remove(struct_ty); + + new_decl.ty = Type.type; + new_decl.val = struct_ty.toValue(); + new_namespace.ty = struct_ty.toType(); // Fields const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); - try struct_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); + try struct_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); var i: usize = 0; while (i < fields_len) : (i += 1) { - const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i); - const field_struct_val = elem_val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // name: []const u8 - const name_val = field_struct_val[0]; - // type: type, - const type_val = field_struct_val[1]; - // default_value: ?*const anyopaque, - const default_value_val = field_struct_val[2]; - // is_comptime: bool, - const is_comptime_val = field_struct_val[3]; - // alignment: comptime_int, - const alignment_val = field_struct_val[4]; + const elem_val = try fields_val.elemValue(mod, i); + const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); + const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "name"), + ).?); + const type_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "type"), + ).?); + const default_value_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "default_value"), + ).?); + const is_comptime_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "is_comptime"), + ).?); + const alignment_val = try elem_val.fieldValue(mod, elem_fields.getIndex( + try ip.getOrPutString(gpa, "alignment"), + ).?); if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { return sema.fail(block, src, "alignment must fit in 'u32'", .{}); } - const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema)).?); + const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?); if (layout == .Packed) { if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); @@ -19305,21 +20465,15 @@ fn reifyStruct( return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}); } - const field_name = try name_val.toAllocatedBytes( - Type.initTag(.const_slice_u8), - new_decl_arena_allocator, - mod, - ); + const field_name = try name_val.toIpString(Type.slice_const_u8, mod); if (is_tuple) { - const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch { - return sema.fail( - block, - src, - "tuple cannot have non-numeric field '{s}'", - .{field_name}, - ); - }; + const field_index = field_name.toUnsigned(ip) orelse return sema.fail( + block, + src, + "tuple cannot have non-numeric field '{}'", + .{field_name.fmt(ip)}, + ); if (field_index >= fields_len) { return sema.fail( @@ -19333,22 +20487,19 @@ fn reifyStruct( const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { // TODO: better source location - return sema.fail(block, src, "duplicate struct field {s}", .{field_name}); + return sema.fail(block, src, "duplicate struct field {}", .{field_name.fmt(ip)}); } - const default_val = if (default_value_val.optionalValue()) |opt_val| blk: { - const payload_val = if (opt_val.pointerDecl()) |opt_decl| - mod.declPtr(opt_decl).val - else - opt_val; - break :blk try payload_val.copy(new_decl_arena_allocator); - } else Value.initTag(.unreachable_value); - if (is_comptime_val.toBool() and default_val.tag() == .unreachable_value) { + const field_ty = type_val.toType(); + const default_val = if (default_value_val.optionalValue(mod)) |opt_val| + (try sema.pointerDeref(block, src, opt_val, try mod.singleConstPtrType(field_ty)) orelse + return sema.failWithNeededComptime(block, src, "struct field default value must be comptime-known")).toIntern() + else + .none; + if (is_comptime_val.toBool() and default_val == .none) { return sema.fail(block, src, "comptime field without default initialization value", .{}); } - var buffer: Value.ToTypeBuffer = undefined; - const field_ty = try type_val.toType(&buffer).copy(new_decl_arena_allocator); gop.value_ptr.* = .{ .ty = field_ty, .abi_align = abi_align, @@ -19357,20 +20508,20 @@ fn reifyStruct( .offset = undefined, }; - if (field_ty.zigTypeTag() == .Opaque) { + if (field_ty.zigTypeTag(mod) == .Opaque) { const msg = msg: { const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } - if (field_ty.zigTypeTag() == .NoReturn) { + if (field_ty.zigTypeTag(mod) == .NoReturn) { const msg = msg: { const msg = try sema.errMsg(block, src, "struct fields cannot be 'noreturn'", .{}); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; @@ -19380,22 +20531,22 @@ fn reifyStruct( if (struct_obj.layout == .Extern and !try sema.validateExternType(field_ty, .struct_field)) { const msg = msg: { const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), field_ty, .struct_field); + try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .struct_field); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } else if (struct_obj.layout == .Packed and !(validatePackedType(field_ty))) { + } else if (struct_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { const msg = msg: { const msg = try sema.errMsg(block, src, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl), field_ty); + try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; @@ -19411,7 +20562,7 @@ fn reifyStruct( sema.resolveTypeLayout(field.ty) catch |err| switch (err) { error.AnalysisFail => { const msg = sema.err orelse return err; - try sema.addFieldErrNote(struct_ty, index, msg, "while checking this field", .{}); + try sema.addFieldErrNote(struct_ty.toType(), index, msg, "while checking this field", .{}); return err; }, else => return err, @@ -19420,30 +20571,27 @@ fn reifyStruct( var fields_bit_sum: u64 = 0; for (struct_obj.fields.values()) |field| { - fields_bit_sum += field.ty.bitSize(target); + fields_bit_sum += field.ty.bitSize(mod); } - if (backing_int_val.optionalValue()) |payload| { - var buf: Value.ToTypeBuffer = undefined; - const backing_int_ty = payload.toType(&buf); + if (backing_int_val.optionalValue(mod)) |payload| { + const backing_int_ty = payload.toType(); try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum); - struct_obj.backing_int_ty = try backing_int_ty.copy(new_decl_arena_allocator); + struct_obj.backing_int_ty = backing_int_ty; } else { - var buf: Type.Payload.Bits = .{ - .base = .{ .tag = .int_unsigned }, - .data = @intCast(u16, fields_bit_sum), - }; - struct_obj.backing_int_ty = try Type.initPayload(&buf.base).copy(new_decl_arena_allocator); + struct_obj.backing_int_ty = try mod.intType(.unsigned, @intCast(u16, fields_bit_sum)); } struct_obj.status = .have_layout; } - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; } fn zirAddrSpaceCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); const addrspace_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; @@ -19455,7 +20603,7 @@ fn zirAddrSpaceCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst try sema.checkPtrOperand(block, ptr_src, ptr_ty); - var ptr_info = ptr_ty.ptrInfo().data; + var ptr_info = ptr_ty.ptrInfo(mod); const src_addrspace = ptr_info.@"addrspace"; if (!target_util.addrSpaceCastIsValid(sema.mod.getTarget(), src_addrspace, dest_addrspace)) { const msg = msg: { @@ -19469,8 +20617,8 @@ fn zirAddrSpaceCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst ptr_info.@"addrspace" = dest_addrspace; const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); - const dest_ty = if (ptr_ty.zigTypeTag() == .Optional) - try Type.optional(sema.arena, dest_ptr_ty) + const dest_ty = if (ptr_ty.zigTypeTag(mod) == .Optional) + try Type.optional(sema.arena, dest_ptr_ty, mod) else dest_ptr_ty; @@ -19499,6 +20647,7 @@ fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.In } fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; @@ -19513,7 +20662,7 @@ fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C errdefer msg.destroy(sema.gpa); const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl), arg_ty, .param_ty); + try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), arg_ty, .param_ty); try sema.addDeclaredHereNote(msg, arg_ty); break :msg msg; @@ -19560,6 +20709,7 @@ fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) } fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ty = try sema.resolveType(block, ty_src, inst_data.operand); @@ -19567,11 +20717,19 @@ fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); - const bytes = try ty.nameAllocArena(anon_decl.arena(), sema.mod); + const bytes = try ty.nameAllocArena(sema.arena, mod); + const decl_ty = try mod.arrayType(.{ + .len = bytes.len, + .child = .u8_type, + .sentinel = .zero_u8, + }); const new_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), - try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + decl_ty, + (try mod.intern(.{ .aggregate = .{ + .ty = decl_ty.toIntern(), + .storage = .{ .bytes = bytes }, + } })).toValue(), 0, // default alignment ); @@ -19591,6 +20749,7 @@ fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; @@ -19605,24 +20764,24 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! if (try sema.resolveMaybeUndefVal(operand)) |val| { const result_val = try sema.floatToInt(block, operand_src, val, operand_ty, dest_ty); return sema.addConstant(dest_ty, result_val); - } else if (dest_ty.zigTypeTag() == .ComptimeInt) { + } else if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_int' must be comptime-known"); } try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); - if (dest_ty.intInfo(sema.mod.getTarget()).bits == 0) { + if (dest_ty.intInfo(mod).bits == 0) { if (block.wantSafety()) { - const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, try sema.addConstant(operand_ty, Value.zero)); + const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, try sema.addConstant(operand_ty, try mod.intValue(operand_ty, 0))); try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds); } - return sema.addConstant(dest_ty, Value.zero); + return sema.addConstant(dest_ty, try mod.intValue(dest_ty, 0)); } const result = try block.addTyOp(if (block.float_mode == .Optimized) .float_to_int_optimized else .float_to_int, dest_ty, operand); if (block.wantSafety()) { const back = try block.addTyOp(.int_to_float, operand_ty, result); const diff = try block.addBinOp(.sub, operand, back); - const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, try sema.addConstant(operand_ty, Value.one)); - const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, try sema.addConstant(operand_ty, Value.negative_one)); + const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, try sema.addConstant(operand_ty, try mod.floatValue(operand_ty, 1.0))); + const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, try sema.addConstant(operand_ty, try mod.floatValue(operand_ty, -1.0))); const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg); try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds); } @@ -19630,6 +20789,7 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! } fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; @@ -19644,7 +20804,7 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! if (try sema.resolveMaybeUndefVal(operand)) |val| { const result_val = try val.intToFloatAdvanced(sema.arena, operand_ty, dest_ty, sema.mod, sema); return sema.addConstant(dest_ty, result_val); - } else if (dest_ty.zigTypeTag() == .ComptimeFloat) { + } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_float' must be comptime-known"); } @@ -19653,6 +20813,7 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! } fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); @@ -19665,41 +20826,48 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ptr_ty = try sema.resolveType(block, src, extra.lhs); try sema.checkPtrType(block, type_src, ptr_ty); - const elem_ty = ptr_ty.elemType2(); - const target = sema.mod.getTarget(); - const ptr_align = try ptr_ty.ptrAlignmentAdvanced(target, sema); + const elem_ty = ptr_ty.elemType2(mod); + const ptr_align = try ptr_ty.ptrAlignmentAdvanced(mod, sema); + + if (ptr_ty.isSlice(mod)) { + const msg = msg: { + const msg = try sema.errMsg(block, type_src, "integer cannot be converted to slice type '{}'", .{ptr_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, type_src, msg, "slice length cannot be inferred from address", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { - const addr = val.toUnsignedInt(target); - if (!ptr_ty.isAllowzeroPtr() and addr == 0) + const addr = val.toUnsignedInt(mod); + if (!ptr_ty.isAllowzeroPtr(mod) and addr == 0) return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{ptr_ty.fmt(sema.mod)}); if (addr != 0 and ptr_align != 0 and addr % ptr_align != 0) return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)}); - const val_payload = try sema.arena.create(Value.Payload.U64); - val_payload.* = .{ - .base = .{ .tag = .int_u64 }, - .data = addr, + const ptr_val = switch (ptr_ty.zigTypeTag(mod)) { + .Optional => (try mod.intern(.{ .opt = .{ + .ty = ptr_ty.toIntern(), + .val = if (addr == 0) .none else (try mod.ptrIntValue(ptr_ty.childType(mod), addr)).toIntern(), + } })).toValue(), + .Pointer => try mod.ptrIntValue(ptr_ty, addr), + else => unreachable, }; - return sema.addConstant(ptr_ty, Value.initPayload(&val_payload.base)); + return sema.addConstant(ptr_ty, ptr_val); } try sema.requireRuntimeBlock(block, src, operand_src); - if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag() == .Fn)) { - if (!ptr_ty.isAllowzeroPtr()) { + if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) { + if (!ptr_ty.isAllowzeroPtr(mod)) { const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); try sema.addSafetyCheck(block, is_non_zero, .cast_to_null); } if (ptr_align > 1) { - const val_payload = try sema.arena.create(Value.Payload.U64); - val_payload.* = .{ - .base = .{ .tag = .int_u64 }, - .data = ptr_align - 1, - }; const align_minus_1 = try sema.addConstant( Type.usize, - Value.initPayload(&val_payload.base), + try mod.intValue(Type.usize, ptr_align - 1), ); const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1); const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize); @@ -19710,6 +20878,8 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const ip = &mod.intern_pool; const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; @@ -19725,22 +20895,27 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat if (disjoint: { // Try avoiding resolving inferred error sets if we can - if (!dest_ty.isAnyError() and dest_ty.errorSetNames().len == 0) break :disjoint true; - if (!operand_ty.isAnyError() and operand_ty.errorSetNames().len == 0) break :disjoint true; - if (dest_ty.isAnyError()) break :disjoint false; - if (operand_ty.isAnyError()) break :disjoint false; - for (dest_ty.errorSetNames()) |dest_err_name| - if (operand_ty.errorSetHasField(dest_err_name)) + if (!dest_ty.isAnyError(mod) and dest_ty.errorSetNames(mod).len == 0) break :disjoint true; + if (!operand_ty.isAnyError(mod) and operand_ty.errorSetNames(mod).len == 0) break :disjoint true; + if (dest_ty.isAnyError(mod)) break :disjoint false; + if (operand_ty.isAnyError(mod)) break :disjoint false; + for (dest_ty.errorSetNames(mod)) |dest_err_name| { + if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name)) break :disjoint false; + } - if (dest_ty.tag() != .error_set_inferred and operand_ty.tag() != .error_set_inferred) + if (!ip.isInferredErrorSetType(dest_ty.toIntern()) and + !ip.isInferredErrorSetType(operand_ty.toIntern())) + { break :disjoint true; + } try sema.resolveInferredErrorSetTy(block, dest_ty_src, dest_ty); try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty); - for (dest_ty.errorSetNames()) |dest_err_name| - if (operand_ty.errorSetHasField(dest_err_name)) + for (dest_ty.errorSetNames(mod)) |dest_err_name| { + if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name)) break :disjoint false; + } break :disjoint true; }) { @@ -19760,15 +20935,15 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat } if (maybe_operand_val) |val| { - if (!dest_ty.isAnyError()) { - const error_name = val.castTag(.@"error").?.data.name; - if (!dest_ty.errorSetHasField(error_name)) { + if (!dest_ty.isAnyError(mod)) { + const error_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; + if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) { const msg = msg: { const msg = try sema.errMsg( block, src, - "'error.{s}' not a member of error set '{}'", - .{ error_name, dest_ty.fmt(sema.mod) }, + "'error.{}' not a member of error set '{}'", + .{ error_name.fmt(ip), dest_ty.fmt(sema.mod) }, ); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, dest_ty); @@ -19778,11 +20953,11 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat } } - return sema.addConstant(dest_ty, val); + return sema.addConstant(dest_ty, try mod.getCoerced(val, dest_ty)); } try sema.requireRuntimeBlock(block, src, operand_src); - if (block.wantSafety() and !dest_ty.isAnyError() and sema.mod.backendSupportsFeature(.error_set_has_value)) { + if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) { const err_int_inst = try block.addBitCast(Type.err_int, operand); const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); try sema.addSafetyCheck(block, ok, .invalid_error_code); @@ -19791,6 +20966,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat } fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; @@ -19799,13 +20975,12 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = try sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); - const target = sema.mod.getTarget(); try sema.checkPtrType(block, dest_ty_src, dest_ty); try sema.checkPtrOperand(block, operand_src, operand_ty); - const operand_info = operand_ty.ptrInfo().data; - const dest_info = dest_ty.ptrInfo().data; + const operand_info = operand_ty.ptrInfo(mod); + const dest_info = dest_ty.ptrInfo(mod); if (!operand_info.mutable and dest_info.mutable) { const msg = msg: { const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{}); @@ -19837,8 +21012,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return sema.failWithOwnedErrorMsg(msg); } - const dest_is_slice = dest_ty.isSlice(); - const operand_is_slice = operand_ty.isSlice(); + const dest_is_slice = dest_ty.isSlice(mod); + const operand_is_slice = operand_ty.isSlice(mod); if (dest_is_slice and !operand_is_slice) { return sema.fail(block, dest_ty_src, "illegal pointer cast to slice", .{}); } @@ -19847,32 +21022,31 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air else operand; - const dest_elem_ty = dest_ty.elemType2(); + const dest_elem_ty = dest_ty.elemType2(mod); try sema.resolveTypeLayout(dest_elem_ty); - const dest_align = dest_ty.ptrAlignment(target); + const dest_align = dest_ty.ptrAlignment(mod); - const operand_elem_ty = operand_ty.elemType2(); + const operand_elem_ty = operand_ty.elemType2(mod); try sema.resolveTypeLayout(operand_elem_ty); - const operand_align = operand_ty.ptrAlignment(target); + const operand_align = operand_ty.ptrAlignment(mod); // If the destination is less aligned than the source, preserve the source alignment const aligned_dest_ty = if (operand_align <= dest_align) dest_ty else blk: { // Unwrap the pointer (or pointer-like optional) type, set alignment, and re-wrap into result - if (dest_ty.zigTypeTag() == .Optional) { - var buf: Type.Payload.ElemType = undefined; - var dest_ptr_info = dest_ty.optionalChild(&buf).ptrInfo().data; + if (dest_ty.zigTypeTag(mod) == .Optional) { + var dest_ptr_info = dest_ty.optionalChild(mod).ptrInfo(mod); dest_ptr_info.@"align" = operand_align; - break :blk try Type.optional(sema.arena, try Type.ptr(sema.arena, sema.mod, dest_ptr_info)); + break :blk try Type.optional(sema.arena, try Type.ptr(sema.arena, mod, dest_ptr_info), mod); } else { - var dest_ptr_info = dest_ty.ptrInfo().data; + var dest_ptr_info = dest_ty.ptrInfo(mod); dest_ptr_info.@"align" = operand_align; - break :blk try Type.ptr(sema.arena, sema.mod, dest_ptr_info); + break :blk try Type.ptr(sema.arena, mod, dest_ptr_info); } }; if (dest_is_slice) { - const operand_elem_size = operand_elem_ty.abiSize(target); - const dest_elem_size = dest_elem_ty.abiSize(target); + const operand_elem_size = operand_elem_ty.abiSize(mod); + const dest_elem_size = dest_elem_ty.abiSize(mod); if (operand_elem_size != dest_elem_size) { return sema.fail(block, dest_ty_src, "TODO: implement @ptrCast between slices changing the length", .{}); } @@ -19884,10 +21058,10 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air errdefer msg.destroy(sema.gpa); try sema.errNote(block, operand_src, msg, "'{}' has alignment '{d}'", .{ - operand_ty.fmt(sema.mod), operand_align, + operand_ty.fmt(mod), operand_align, }); try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{ - dest_ty.fmt(sema.mod), dest_align, + dest_ty.fmt(mod), dest_align, }); try sema.errNote(block, src, msg, "consider using '@alignCast'", .{}); @@ -19897,21 +21071,18 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } if (try sema.resolveMaybeUndefVal(ptr)) |operand_val| { - if (!dest_ty.ptrAllowsZero() and operand_val.isUndef()) { + if (!dest_ty.ptrAllowsZero(mod) and operand_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, operand_src); } - if (!dest_ty.ptrAllowsZero() and operand_val.isNull()) { - return sema.fail(block, operand_src, "null pointer casted to type {}", .{dest_ty.fmt(sema.mod)}); + if (!dest_ty.ptrAllowsZero(mod) and operand_val.isNull(mod)) { + return sema.fail(block, operand_src, "null pointer casted to type '{}'", .{dest_ty.fmt(mod)}); } - if (dest_ty.zigTypeTag() == .Optional and sema.typeOf(ptr).zigTypeTag() != .Optional) { - return sema.addConstant(dest_ty, try Value.Tag.opt_payload.create(sema.arena, operand_val)); - } - return sema.addConstant(aligned_dest_ty, operand_val); + return sema.addConstant(aligned_dest_ty, try mod.getCoerced(operand_val, aligned_dest_ty)); } try sema.requireRuntimeBlock(block, src, null); - if (block.wantSafety() and operand_ty.ptrAllowsZero() and !dest_ty.ptrAllowsZero() and - (try sema.typeHasRuntimeBits(dest_ty.elemType2()) or dest_ty.elemType2().zigTypeTag() == .Fn)) + if (block.wantSafety() and operand_ty.ptrAllowsZero(mod) and !dest_ty.ptrAllowsZero(mod) and + (try sema.typeHasRuntimeBits(dest_ty.elemType2(mod)) or dest_ty.elemType2(mod).zigTypeTag(mod) == .Fn)) { const ptr_int = try block.addUnOp(.ptrtoint, ptr); const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); @@ -19927,6 +21098,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } fn zirConstCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; @@ -19934,12 +21106,12 @@ fn zirConstCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData const operand_ty = sema.typeOf(operand); try sema.checkPtrOperand(block, operand_src, operand_ty); - var ptr_info = operand_ty.ptrInfo().data; + var ptr_info = operand_ty.ptrInfo(mod); ptr_info.mutable = true; - const dest_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); + const dest_ty = try Type.ptr(sema.arena, mod, ptr_info); if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { - return sema.addConstant(dest_ty, operand_val); + return sema.addConstant(dest_ty, try mod.getCoerced(operand_val, dest_ty)); } try sema.requireRuntimeBlock(block, src, null); @@ -19947,6 +21119,7 @@ fn zirConstCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData } fn zirVolatileCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; @@ -19954,9 +21127,9 @@ fn zirVolatileCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD const operand_ty = sema.typeOf(operand); try sema.checkPtrOperand(block, operand_src, operand_ty); - var ptr_info = operand_ty.ptrInfo().data; + var ptr_info = operand_ty.ptrInfo(mod); ptr_info.@"volatile" = false; - const dest_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); + const dest_ty = try Type.ptr(sema.arena, mod, ptr_info); if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { return sema.addConstant(dest_ty, operand_val); @@ -19967,6 +21140,7 @@ fn zirVolatileCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD } fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; @@ -19977,9 +21151,12 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_scalar_ty); const operand_ty = sema.typeOf(operand); const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); - const is_vector = operand_ty.zigTypeTag() == .Vector; + const is_vector = operand_ty.zigTypeTag(mod) == .Vector; const dest_ty = if (is_vector) - try Type.vector(sema.arena, operand_ty.vectorLen(), dest_scalar_ty) + try mod.vectorType(.{ + .len = operand_ty.vectorLen(mod), + .child = dest_scalar_ty.toIntern(), + }) else dest_scalar_ty; @@ -19987,22 +21164,21 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.coerce(block, dest_ty, operand, operand_src); } - const target = sema.mod.getTarget(); - const dest_info = dest_scalar_ty.intInfo(target); + const dest_info = dest_scalar_ty.intInfo(mod); if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { return sema.addConstant(dest_ty, val); } - if (operand_scalar_ty.zigTypeTag() != .ComptimeInt) { - const operand_info = operand_ty.intInfo(target); + if (operand_scalar_ty.zigTypeTag(mod) != .ComptimeInt) { + const operand_info = operand_ty.intInfo(mod); if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { return sema.addConstant(operand_ty, val); } if (operand_info.signedness != dest_info.signedness) { return sema.fail(block, operand_src, "expected {s} integer type, found '{}'", .{ - @tagName(dest_info.signedness), operand_ty.fmt(sema.mod), + @tagName(dest_info.signedness), operand_ty.fmt(mod), }); } if (operand_info.bits < dest_info.bits) { @@ -20011,7 +21187,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, "destination type '{}' has more bits than source type '{}'", - .{ dest_ty.fmt(sema.mod), operand_ty.fmt(sema.mod) }, + .{ dest_ty.fmt(mod), operand_ty.fmt(mod) }, ); errdefer msg.destroy(sema.gpa); try sema.errNote(block, dest_ty_src, msg, "destination type has {d} bits", .{ @@ -20027,23 +21203,22 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } if (try sema.resolveMaybeUndefValIntable(operand)) |val| { - if (val.isUndef()) return sema.addConstUndef(dest_ty); + if (val.isUndef(mod)) return sema.addConstUndef(dest_ty); if (!is_vector) { - return sema.addConstant( + return sema.addConstant(dest_ty, try mod.getCoerced( + try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, mod), dest_ty, - try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, sema.mod), - ); + )); } - var elem_buf: Value.ElemValueBuffer = undefined; - const elems = try sema.arena.alloc(Value, operand_ty.vectorLen()); + const elems = try sema.arena.alloc(InternPool.Index, operand_ty.vectorLen(mod)); for (elems, 0..) |*elem, i| { - const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf); - elem.* = try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, sema.mod); + const elem_val = try val.elemValue(mod, i); + elem.* = try (try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, mod)).intern(dest_scalar_ty, mod); } - return sema.addConstant( - dest_ty, - try Value.Tag.aggregate.create(sema.arena, elems), - ); + return sema.addConstant(dest_ty, (try mod.intern(.{ .aggregate = .{ + .ty = dest_ty.toIntern(), + .storage = .{ .elems = elems }, + } })).toValue()); } try sema.requireRuntimeBlock(block, src, operand_src); @@ -20051,6 +21226,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const align_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; @@ -20061,43 +21237,38 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A try sema.checkPtrOperand(block, ptr_src, ptr_ty); - var ptr_info = ptr_ty.ptrInfo().data; + var ptr_info = ptr_ty.ptrInfo(mod); ptr_info.@"align" = dest_align; - var dest_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); - if (ptr_ty.zigTypeTag() == .Optional) { - dest_ty = try Type.Tag.optional.create(sema.arena, dest_ty); + var dest_ty = try Type.ptr(sema.arena, mod, ptr_info); + if (ptr_ty.zigTypeTag(mod) == .Optional) { + dest_ty = try mod.optionalType(dest_ty.toIntern()); } if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |val| { - if (try val.getUnsignedIntAdvanced(sema.mod.getTarget(), null)) |addr| { + if (try val.getUnsignedIntAdvanced(mod, null)) |addr| { if (addr % dest_align != 0) { return sema.fail(block, ptr_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ addr, dest_align }); } } - return sema.addConstant(dest_ty, val); + return sema.addConstant(dest_ty, try mod.getCoerced(val, dest_ty)); } try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src); if (block.wantSafety() and dest_align > 1 and try sema.typeHasRuntimeBits(ptr_info.pointee_type)) { - const val_payload = try sema.arena.create(Value.Payload.U64); - val_payload.* = .{ - .base = .{ .tag = .int_u64 }, - .data = dest_align - 1, - }; const align_minus_1 = try sema.addConstant( Type.usize, - Value.initPayload(&val_payload.base), + try mod.intValue(Type.usize, dest_align - 1), ); - const actual_ptr = if (ptr_ty.isSlice()) + const actual_ptr = if (ptr_ty.isSlice(mod)) try sema.analyzeSlicePtr(block, ptr_src, ptr, ptr_ty) else ptr; const ptr_int = try block.addUnOp(.ptrtoint, actual_ptr); const remainder = try block.addBinOp(.bit_and, ptr_int, align_minus_1); const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize); - const ok = if (ptr_ty.isSlice()) ok: { + const ok = if (ptr_ty.isSlice(mod)) ok: { const len = try sema.analyzeSliceLen(block, ptr_src, ptr); const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); break :ok try block.addBinOp(.bit_or, len_zero, is_aligned); @@ -20112,51 +21283,52 @@ fn zirBitCount( block: *Block, inst: Zir.Inst.Index, air_tag: Air.Inst.Tag, - comptime comptimeOp: fn (val: Value, ty: Type, target: std.Target) u64, + comptime comptimeOp: fn (val: Value, ty: Type, mod: *Module) u64, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); _ = try sema.checkIntOrVector(block, operand, operand_src); - const target = sema.mod.getTarget(); - const bits = operand_ty.intInfo(target).bits; + const bits = operand_ty.intInfo(mod).bits; if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { return sema.addConstant(operand_ty, val); } - const result_scalar_ty = try Type.smallestUnsignedInt(sema.arena, bits); - switch (operand_ty.zigTypeTag()) { + const result_scalar_ty = try mod.smallestUnsignedInt(bits); + switch (operand_ty.zigTypeTag(mod)) { .Vector => { - const vec_len = operand_ty.vectorLen(); - const result_ty = try Type.vector(sema.arena, vec_len, result_scalar_ty); + const vec_len = operand_ty.vectorLen(mod); + const result_ty = try mod.vectorType(.{ + .len = vec_len, + .child = result_scalar_ty.toIntern(), + }); if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef()) return sema.addConstUndef(result_ty); + if (val.isUndef(mod)) return sema.addConstUndef(result_ty); - var elem_buf: Value.ElemValueBuffer = undefined; - const elems = try sema.arena.alloc(Value, vec_len); - const scalar_ty = operand_ty.scalarType(); + const elems = try sema.arena.alloc(InternPool.Index, vec_len); + const scalar_ty = operand_ty.scalarType(mod); for (elems, 0..) |*elem, i| { - const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf); - const count = comptimeOp(elem_val, scalar_ty, target); - elem.* = try Value.Tag.int_u64.create(sema.arena, count); - } - return sema.addConstant( - result_ty, - try Value.Tag.aggregate.create(sema.arena, elems), - ); + const elem_val = try val.elemValue(mod, i); + const count = comptimeOp(elem_val, scalar_ty, mod); + elem.* = (try mod.intValue(result_scalar_ty, count)).toIntern(); + } + return sema.addConstant(result_ty, (try mod.intern(.{ .aggregate = .{ + .ty = result_ty.toIntern(), + .storage = .{ .elems = elems }, + } })).toValue()); } else { try sema.requireRuntimeBlock(block, src, operand_src); return block.addTyOp(air_tag, result_ty, operand); } }, .Int => { - if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef()) return sema.addConstUndef(result_scalar_ty); - try sema.resolveLazyValue(val); - return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, target)); + if (try sema.resolveMaybeUndefLazyVal(operand)) |val| { + if (val.isUndef(mod)) return sema.addConstUndef(result_scalar_ty); + return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, mod)); } else { try sema.requireRuntimeBlock(block, src, operand_src); return block.addTyOp(air_tag, result_scalar_ty, operand); @@ -20167,20 +21339,20 @@ fn zirBitCount( } fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); - const target = sema.mod.getTarget(); - const bits = scalar_ty.intInfo(target).bits; + const bits = scalar_ty.intInfo(mod).bits; if (bits % 8 != 0) { return sema.fail( block, operand_src, "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", - .{ scalar_ty.fmt(sema.mod), bits }, + .{ scalar_ty.fmt(mod), bits }, ); } @@ -20188,11 +21360,11 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.addConstant(operand_ty, val); } - switch (operand_ty.zigTypeTag()) { + switch (operand_ty.zigTypeTag(mod)) { .Int => { const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef()) return sema.addConstUndef(operand_ty); - const result_val = try val.byteSwap(operand_ty, target, sema.arena); + if (val.isUndef(mod)) return sema.addConstUndef(operand_ty); + const result_val = try val.byteSwap(operand_ty, mod, sema.arena); return sema.addConstant(operand_ty, result_val); } else operand_src; @@ -20201,20 +21373,19 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }, .Vector => { const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef()) + if (val.isUndef(mod)) return sema.addConstUndef(operand_ty); - const vec_len = operand_ty.vectorLen(); - var elem_buf: Value.ElemValueBuffer = undefined; - const elems = try sema.arena.alloc(Value, vec_len); + const vec_len = operand_ty.vectorLen(mod); + const elems = try sema.arena.alloc(InternPool.Index, vec_len); for (elems, 0..) |*elem, i| { - const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf); - elem.* = try elem_val.byteSwap(operand_ty, target, sema.arena); + const elem_val = try val.elemValue(mod, i); + elem.* = try (try elem_val.byteSwap(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod); } - return sema.addConstant( - operand_ty, - try Value.Tag.aggregate.create(sema.arena, elems), - ); + return sema.addConstant(operand_ty, (try mod.intern(.{ .aggregate = .{ + .ty = operand_ty.toIntern(), + .storage = .{ .elems = elems }, + } })).toValue()); } else operand_src; try sema.requireRuntimeBlock(block, src, runtime_src); @@ -20236,12 +21407,12 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! return sema.addConstant(operand_ty, val); } - const target = sema.mod.getTarget(); - switch (operand_ty.zigTypeTag()) { + const mod = sema.mod; + switch (operand_ty.zigTypeTag(mod)) { .Int => { const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef()) return sema.addConstUndef(operand_ty); - const result_val = try val.bitReverse(operand_ty, target, sema.arena); + if (val.isUndef(mod)) return sema.addConstUndef(operand_ty); + const result_val = try val.bitReverse(operand_ty, mod, sema.arena); return sema.addConstant(operand_ty, result_val); } else operand_src; @@ -20250,20 +21421,19 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! }, .Vector => { const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef()) + if (val.isUndef(mod)) return sema.addConstUndef(operand_ty); - const vec_len = operand_ty.vectorLen(); - var elem_buf: Value.ElemValueBuffer = undefined; - const elems = try sema.arena.alloc(Value, vec_len); + const vec_len = operand_ty.vectorLen(mod); + const elems = try sema.arena.alloc(InternPool.Index, vec_len); for (elems, 0..) |*elem, i| { - const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf); - elem.* = try elem_val.bitReverse(scalar_ty, target, sema.arena); + const elem_val = try val.elemValue(mod, i); + elem.* = try (try elem_val.bitReverse(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod); } - return sema.addConstant( - operand_ty, - try Value.Tag.aggregate.create(sema.arena, elems), - ); + return sema.addConstant(operand_ty, (try mod.intern(.{ .aggregate = .{ + .ty = operand_ty.toIntern(), + .storage = .{ .elems = elems }, + } })).toValue()); } else operand_src; try sema.requireRuntimeBlock(block, src, runtime_src); @@ -20293,15 +21463,15 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const ty = try sema.resolveType(block, lhs_src, extra.lhs); - const field_name = try sema.resolveConstString(block, rhs_src, extra.rhs, "name of field must be comptime-known"); - const target = sema.mod.getTarget(); + const field_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, "name of field must be comptime-known"); + const mod = sema.mod; try sema.resolveTypeLayout(ty); - switch (ty.zigTypeTag()) { + switch (ty.zigTypeTag(mod)) { .Struct => {}, else => { const msg = msg: { - const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(sema.mod)}); + const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, ty); break :msg msg; @@ -20310,45 +21480,47 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 }, } - const field_index = if (ty.isTuple()) blk: { - if (mem.eql(u8, field_name, "len")) { + const field_index = if (ty.isTuple(mod)) blk: { + if (mod.intern_pool.stringEqlSlice(field_name, "len")) { return sema.fail(block, src, "no offset available for 'len' field of tuple", .{}); } break :blk try sema.tupleFieldIndex(block, ty, field_name, rhs_src); } else try sema.structFieldIndex(block, ty, field_name, rhs_src); - if (ty.structFieldIsComptime(field_index)) { + if (ty.structFieldIsComptime(field_index, mod)) { return sema.fail(block, src, "no offset available for comptime field", .{}); } - switch (ty.containerLayout()) { + switch (ty.containerLayout(mod)) { .Packed => { var bit_sum: u64 = 0; - const fields = ty.structFields(); + const fields = ty.structFields(mod); for (fields.values(), 0..) |field, i| { if (i == field_index) { return bit_sum; } - bit_sum += field.ty.bitSize(target); + bit_sum += field.ty.bitSize(mod); } else unreachable; }, - else => return ty.structFieldOffset(field_index, target) * 8, + else => return ty.structFieldOffset(field_index, mod) * 8, } } fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .Struct, .Enum, .Union, .Opaque => return, - else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(sema.mod)}), + else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(mod)}), } } /// Returns `true` if the type was a comptime_int. fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { - switch (try ty.zigTypeTagOrPoison()) { + const mod = sema.mod; + switch (try ty.zigTypeTagOrPoison(mod)) { .ComptimeInt => return true, .Int => return false, - else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(sema.mod)}), + else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(mod)}), } } @@ -20358,8 +21530,9 @@ fn checkInvalidPtrArithmetic( src: LazySrcLoc, ty: Type, ) CompileError!void { - switch (try ty.zigTypeTagOrPoison()) { - .Pointer => switch (ty.ptrSize()) { + const mod = sema.mod; + switch (try ty.zigTypeTagOrPoison(mod)) { + .Pointer => switch (ty.ptrSize(mod)) { .One, .Slice => return, .Many, .C => return sema.fail( block, @@ -20397,7 +21570,8 @@ fn checkPtrOperand( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .Pointer => return, .Fn => { const msg = msg: { @@ -20405,7 +21579,7 @@ fn checkPtrOperand( block, ty_src, "expected pointer, found '{}'", - .{ty.fmt(sema.mod)}, + .{ty.fmt(mod)}, ); errdefer msg.destroy(sema.gpa); @@ -20415,10 +21589,10 @@ fn checkPtrOperand( }; return sema.failWithOwnedErrorMsg(msg); }, - .Optional => if (ty.isPtrLikeOptional()) return, + .Optional => if (ty.isPtrLikeOptional(mod)) return, else => {}, } - return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(sema.mod)}); + return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)}); } fn checkPtrType( @@ -20427,7 +21601,8 @@ fn checkPtrType( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .Pointer => return, .Fn => { const msg = msg: { @@ -20435,7 +21610,7 @@ fn checkPtrType( block, ty_src, "expected pointer type, found '{}'", - .{ty.fmt(sema.mod)}, + .{ty.fmt(mod)}, ); errdefer msg.destroy(sema.gpa); @@ -20445,10 +21620,10 @@ fn checkPtrType( }; return sema.failWithOwnedErrorMsg(msg); }, - .Optional => if (ty.isPtrLikeOptional()) return, + .Optional => if (ty.isPtrLikeOptional(mod)) return, else => {}, } - return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(sema.mod)}); + return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)}); } fn checkVectorElemType( @@ -20457,11 +21632,12 @@ fn checkVectorElemType( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .Int, .Float, .Bool => return, - else => if (ty.isPtrAtRuntime()) return, + else => if (ty.isPtrAtRuntime(mod)) return, } - return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(sema.mod)}); + return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(mod)}); } fn checkFloatType( @@ -20470,9 +21646,10 @@ fn checkFloatType( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .ComptimeInt, .ComptimeFloat, .Float => {}, - else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(sema.mod)}), + else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(mod)}), } } @@ -20482,13 +21659,14 @@ fn checkNumericType( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, - .Vector => switch (ty.childType().zigTypeTag()) { + .Vector => switch (ty.childType(mod).zigTypeTag(mod)) { .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}), }, - else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(sema.mod)}), + else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(mod)}), } } @@ -20502,9 +21680,10 @@ fn checkAtomicPtrOperand( ptr_src: LazySrcLoc, ptr_const: bool, ) CompileError!Air.Inst.Ref { - const target = sema.mod.getTarget(); - var diag: target_util.AtomicPtrAlignmentDiagnostics = .{}; - const alignment = target_util.atomicPtrAlignment(target, elem_ty, &diag) catch |err| switch (err) { + const mod = sema.mod; + var diag: Module.AtomicPtrAlignmentDiagnostics = .{}; + const alignment = mod.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, error.FloatTooBig => return sema.fail( block, elem_ty_src, @@ -20521,7 +21700,7 @@ fn checkAtomicPtrOperand( block, elem_ty_src, "expected bool, integer, float, enum, or pointer type; found '{}'", - .{elem_ty.fmt(sema.mod)}, + .{elem_ty.fmt(mod)}, ), }; @@ -20533,10 +21712,10 @@ fn checkAtomicPtrOperand( }; const ptr_ty = sema.typeOf(ptr); - const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison()) { - .Pointer => ptr_ty.ptrInfo().data, + const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison(mod)) { + .Pointer => ptr_ty.ptrInfo(mod), else => { - const wanted_ptr_ty = try Type.ptr(sema.arena, sema.mod, wanted_ptr_data); + const wanted_ptr_ty = try Type.ptr(sema.arena, mod, wanted_ptr_data); _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); unreachable; }, @@ -20546,7 +21725,7 @@ fn checkAtomicPtrOperand( wanted_ptr_data.@"allowzero" = ptr_data.@"allowzero"; wanted_ptr_data.@"volatile" = ptr_data.@"volatile"; - const wanted_ptr_ty = try Type.ptr(sema.arena, sema.mod, wanted_ptr_data); + const wanted_ptr_ty = try Type.ptr(sema.arena, mod, wanted_ptr_data); const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); return casted_ptr; @@ -20560,7 +21739,7 @@ fn checkPtrIsNotComptimeMutable( operand_src: LazySrcLoc, ) CompileError!void { _ = operand_src; - if (ptr_val.isComptimeMutablePtr()) { + if (ptr_val.isComptimeMutablePtr(sema.mod)) { return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{}); } } @@ -20569,7 +21748,7 @@ fn checkComptimeVarStore( sema: *Sema, block: *Block, src: LazySrcLoc, - decl_ref_mut: Value.Payload.DeclRefMut.Data, + decl_ref_mut: InternPool.Key.Ptr.Addr.MutDecl, ) CompileError!void { if (@enumToInt(decl_ref_mut.runtime_index) < @enumToInt(block.runtime_index)) { if (block.runtime_cond) |cond_src| { @@ -20600,20 +21779,21 @@ fn checkIntOrVector( operand: Air.Inst.Ref, operand_src: LazySrcLoc, ) CompileError!Type { + const mod = sema.mod; const operand_ty = sema.typeOf(operand); - switch (try operand_ty.zigTypeTagOrPoison()) { + switch (try operand_ty.zigTypeTagOrPoison(mod)) { .Int => return operand_ty, .Vector => { - const elem_ty = operand_ty.childType(); - switch (try elem_ty.zigTypeTagOrPoison()) { + const elem_ty = operand_ty.childType(mod); + switch (try elem_ty.zigTypeTagOrPoison(mod)) { .Int => return elem_ty, else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ - elem_ty.fmt(sema.mod), + elem_ty.fmt(mod), }), } }, else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ - operand_ty.fmt(sema.mod), + operand_ty.fmt(mod), }), } } @@ -20624,27 +21804,29 @@ fn checkIntOrVectorAllowComptime( operand_ty: Type, operand_src: LazySrcLoc, ) CompileError!Type { - switch (try operand_ty.zigTypeTagOrPoison()) { + const mod = sema.mod; + switch (try operand_ty.zigTypeTagOrPoison(mod)) { .Int, .ComptimeInt => return operand_ty, .Vector => { - const elem_ty = operand_ty.childType(); - switch (try elem_ty.zigTypeTagOrPoison()) { + const elem_ty = operand_ty.childType(mod); + switch (try elem_ty.zigTypeTagOrPoison(mod)) { .Int, .ComptimeInt => return elem_ty, else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ - elem_ty.fmt(sema.mod), + elem_ty.fmt(mod), }), } }, else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ - operand_ty.fmt(sema.mod), + operand_ty.fmt(mod), }), } } fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .ErrorSet => return, - else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(sema.mod)}), + else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(mod)}), } } @@ -20670,11 +21852,12 @@ fn checkSimdBinOp( lhs_src: LazySrcLoc, rhs_src: LazySrcLoc, ) CompileError!SimdBinOp { + const mod = sema.mod; const lhs_ty = sema.typeOf(uncasted_lhs); const rhs_ty = sema.typeOf(uncasted_rhs); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); - var vec_len: ?usize = if (lhs_ty.zigTypeTag() == .Vector) lhs_ty.vectorLen() else null; + var vec_len: ?usize = if (lhs_ty.zigTypeTag(mod) == .Vector) lhs_ty.vectorLen(mod) else null; const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, }); @@ -20688,7 +21871,7 @@ fn checkSimdBinOp( .lhs_val = try sema.resolveMaybeUndefVal(lhs), .rhs_val = try sema.resolveMaybeUndefVal(rhs), .result_ty = result_ty, - .scalar_ty = result_ty.scalarType(), + .scalar_ty = result_ty.scalarType(mod), }; } @@ -20701,8 +21884,9 @@ fn checkVectorizableBinaryOperands( lhs_src: LazySrcLoc, rhs_src: LazySrcLoc, ) CompileError!void { - const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); - const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); + const mod = sema.mod; + const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); + const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); if (lhs_zig_ty_tag != .Vector and rhs_zig_ty_tag != .Vector) return; const lhs_is_vector = switch (lhs_zig_ty_tag) { @@ -20715,8 +21899,8 @@ fn checkVectorizableBinaryOperands( }; if (lhs_is_vector and rhs_is_vector) { - const lhs_len = lhs_ty.arrayLen(); - const rhs_len = rhs_ty.arrayLen(); + const lhs_len = lhs_ty.arrayLen(mod); + const rhs_len = rhs_ty.arrayLen(mod); if (lhs_len != rhs_len) { const msg = msg: { const msg = try sema.errMsg(block, src, "vector length mismatch", .{}); @@ -20730,7 +21914,7 @@ fn checkVectorizableBinaryOperands( } else { const msg = msg: { const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: '{}' and '{}'", .{ - lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod), + lhs_ty.fmt(mod), rhs_ty.fmt(mod), }); errdefer msg.destroy(sema.gpa); if (lhs_is_vector) { @@ -20748,7 +21932,8 @@ fn checkVectorizableBinaryOperands( fn maybeOptionsSrc(sema: *Sema, block: *Block, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc { if (base_src == .unneeded) return .unneeded; - return Module.optionsSrc(sema.gpa, sema.mod.declPtr(block.src_decl), base_src, wanted); + const mod = sema.mod; + return mod.optionsSrc(mod.declPtr(block.src_decl), base_src, wanted); } fn resolveExportOptions( @@ -20756,7 +21941,10 @@ fn resolveExportOptions( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, -) CompileError!std.builtin.ExportOptions { +) CompileError!Module.Export.Options { + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; const export_options_ty = try sema.getBuiltinType("ExportOptions"); const air_ref = try sema.resolveInst(zir_ref); const options = try sema.coerce(block, export_options_ty, air_ref, src); @@ -20766,26 +21954,26 @@ fn resolveExportOptions( const section_src = sema.maybeOptionsSrc(block, src, "section"); const visibility_src = sema.maybeOptionsSrc(block, src, "visibility"); - const name_operand = try sema.fieldVal(block, src, options, "name", name_src); + const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); const name_val = try sema.resolveConstValue(block, name_src, name_operand, "name of exported value must be comptime-known"); - const name_ty = Type.initTag(.const_slice_u8); - const name = try name_val.toAllocatedBytes(name_ty, sema.arena, sema.mod); + const name_ty = Type.slice_const_u8; + const name = try name_val.toAllocatedBytes(name_ty, sema.arena, mod); - const linkage_operand = try sema.fieldVal(block, src, options, "linkage", linkage_src); + const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src); const linkage_val = try sema.resolveConstValue(block, linkage_src, linkage_operand, "linkage of exported value must be comptime-known"); - const linkage = linkage_val.toEnum(std.builtin.GlobalLinkage); + const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val); - const section_operand = try sema.fieldVal(block, src, options, "section", section_src); + const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "section"), section_src); const section_opt_val = try sema.resolveConstValue(block, section_src, section_operand, "linksection of exported value must be comptime-known"); - const section_ty = Type.initTag(.const_slice_u8); - const section = if (section_opt_val.optionalValue()) |section_val| - try section_val.toAllocatedBytes(section_ty, sema.arena, sema.mod) + const section_ty = Type.slice_const_u8; + const section = if (section_opt_val.optionalValue(mod)) |section_val| + try section_val.toAllocatedBytes(section_ty, sema.arena, mod) else null; - const visibility_operand = try sema.fieldVal(block, src, options, "visibility", visibility_src); + const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "visibility"), visibility_src); const visibility_val = try sema.resolveConstValue(block, visibility_src, visibility_operand, "visibility of exported value must be comptime-known"); - const visibility = visibility_val.toEnum(std.builtin.SymbolVisibility); + const visibility = mod.toEnum(std.builtin.SymbolVisibility, visibility_val); if (name.len < 1) { return sema.fail(block, name_src, "exported symbol name cannot be empty", .{}); @@ -20797,10 +21985,10 @@ fn resolveExportOptions( }); } - return std.builtin.ExportOptions{ - .name = name, + return .{ + .name = try ip.getOrPutString(gpa, name), .linkage = linkage, - .section = section, + .section = try ip.getOrPutStringOpt(gpa, section), .visibility = visibility, }; } @@ -20813,11 +22001,12 @@ fn resolveBuiltinEnum( comptime name: []const u8, reason: []const u8, ) CompileError!@field(std.builtin, name) { + const mod = sema.mod; const ty = try sema.getBuiltinType(name); const air_ref = try sema.resolveInst(zir_ref); const coerced = try sema.coerce(block, ty, air_ref, src); const val = try sema.resolveConstValue(block, src, coerced, reason); - return val.toEnum(@field(std.builtin, name)); + return mod.toEnum(@field(std.builtin, name), val); } fn resolveAtomicOrder( @@ -20844,6 +22033,7 @@ fn zirCmpxchg( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data; const air_tag: Air.Inst.Tag = switch (extended.small) { 0 => .cmpxchg_weak, @@ -20861,12 +22051,12 @@ fn zirCmpxchg( // zig fmt: on const expected_value = try sema.resolveInst(extra.expected_value); const elem_ty = sema.typeOf(expected_value); - if (elem_ty.zigTypeTag() == .Float) { + if (elem_ty.zigTypeTag(mod) == .Float) { return sema.fail( block, elem_ty_src, "expected bool, integer, enum, or pointer type; found '{}'", - .{elem_ty.fmt(sema.mod)}, + .{elem_ty.fmt(mod)}, ); } const uncasted_ptr = try sema.resolveInst(extra.ptr); @@ -20888,29 +22078,34 @@ fn zirCmpxchg( return sema.fail(block, failure_order_src, "failure atomic ordering must not be Release or AcqRel", .{}); } - const result_ty = try Type.optional(sema.arena, elem_ty); + const result_ty = try Type.optional(sema.arena, elem_ty, mod); // special case zero bit types if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { - return sema.addConstant(result_ty, Value.null); + return sema.addConstant(result_ty, (try mod.intern(.{ .opt = .{ + .ty = result_ty.toIntern(), + .val = .none, + } })).toValue()); } const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { if (try sema.resolveMaybeUndefVal(expected_value)) |expected_val| { if (try sema.resolveMaybeUndefVal(new_value)) |new_val| { - if (expected_val.isUndef() or new_val.isUndef()) { + if (expected_val.isUndef(mod) or new_val.isUndef(mod)) { // TODO: this should probably cause the memory stored at the pointer // to become undef as well return sema.addConstUndef(result_ty); } const ptr_ty = sema.typeOf(ptr); const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; - const result_val = if (stored_val.eql(expected_val, elem_ty, sema.mod)) blk: { - try sema.storePtr(block, src, ptr, new_value); - break :blk Value.null; - } else try Value.Tag.opt_payload.create(sema.arena, stored_val); - - return sema.addConstant(result_ty, result_val); + const result_val = try mod.intern(.{ .opt = .{ + .ty = result_ty.toIntern(), + .val = if (stored_val.eql(expected_val, elem_ty, mod)) blk: { + try sema.storePtr(block, src, ptr, new_value); + break :blk .none; + } else stored_val.toIntern(), + } }); + return sema.addConstant(result_ty, result_val.toValue()); } else break :rs new_value_src; } else break :rs expected_src; } else ptr_src; @@ -20934,6 +22129,7 @@ fn zirCmpxchg( } fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const len_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; @@ -20942,17 +22138,13 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const scalar = try sema.resolveInst(extra.rhs); const scalar_ty = sema.typeOf(scalar); try sema.checkVectorElemType(block, scalar_src, scalar_ty); - const vector_ty = try Type.Tag.vector.create(sema.arena, .{ + const vector_ty = try mod.vectorType(.{ .len = len, - .elem_type = scalar_ty, + .child = scalar_ty.toIntern(), }); if (try sema.resolveMaybeUndefVal(scalar)) |scalar_val| { - if (scalar_val.isUndef()) return sema.addConstUndef(vector_ty); - - return sema.addConstant( - vector_ty, - try Value.Tag.repeated.create(sema.arena, scalar_val), - ); + if (scalar_val.isUndef(mod)) return sema.addConstUndef(vector_ty); + return sema.addConstant(vector_ty, try sema.splat(vector_ty, scalar_val)); } try sema.requireRuntimeBlock(block, inst_data.src(), scalar_src); @@ -20967,31 +22159,31 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp", "@reduce operation must be comptime-known"); const operand = try sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); - const target = sema.mod.getTarget(); + const mod = sema.mod; - if (operand_ty.zigTypeTag() != .Vector) { - return sema.fail(block, operand_src, "expected vector, found '{}'", .{operand_ty.fmt(sema.mod)}); + if (operand_ty.zigTypeTag(mod) != .Vector) { + return sema.fail(block, operand_src, "expected vector, found '{}'", .{operand_ty.fmt(mod)}); } - const scalar_ty = operand_ty.childType(); + const scalar_ty = operand_ty.childType(mod); // Type-check depending on operation. switch (operation) { - .And, .Or, .Xor => switch (scalar_ty.zigTypeTag()) { + .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(mod)) { .Int, .Bool => {}, else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{}'", .{ - @tagName(operation), operand_ty.fmt(sema.mod), + @tagName(operation), operand_ty.fmt(mod), }), }, - .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag()) { + .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(mod)) { .Int, .Float => {}, else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{}'", .{ - @tagName(operation), operand_ty.fmt(sema.mod), + @tagName(operation), operand_ty.fmt(mod), }), }, } - const vec_len = operand_ty.vectorLen(); + const vec_len = operand_ty.vectorLen(mod); if (vec_len == 0) { // TODO re-evaluate if we should introduce a "neutral value" for some operations, // e.g. zero for add and one for mul. @@ -20999,21 +22191,20 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { - if (operand_val.isUndef()) return sema.addConstUndef(scalar_ty); + if (operand_val.isUndef(mod)) return sema.addConstUndef(scalar_ty); - var accum: Value = try operand_val.elemValue(sema.mod, sema.arena, 0); - var elem_buf: Value.ElemValueBuffer = undefined; + var accum: Value = try operand_val.elemValue(mod, 0); var i: u32 = 1; while (i < vec_len) : (i += 1) { - const elem_val = operand_val.elemValueBuffer(sema.mod, i, &elem_buf); + const elem_val = try operand_val.elemValue(mod, i); switch (operation) { - .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, sema.mod), - .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, sema.mod), - .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, sema.mod), - .Min => accum = accum.numberMin(elem_val, target), - .Max => accum = accum.numberMax(elem_val, target), + .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, mod), + .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, mod), + .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, mod), + .Min => accum = accum.numberMin(elem_val, mod), + .Max => accum = accum.numberMax(elem_val, mod), .Add => accum = try sema.numberAddWrapScalar(accum, elem_val, scalar_ty), - .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, sema.mod), + .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, mod), } } return sema.addConstant(scalar_ty, accum); @@ -21030,6 +22221,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data; const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; @@ -21042,13 +22234,13 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air var mask = try sema.resolveInst(extra.mask); var mask_ty = sema.typeOf(mask); - const mask_len = switch (sema.typeOf(mask).zigTypeTag()) { - .Array, .Vector => sema.typeOf(mask).arrayLen(), + const mask_len = switch (sema.typeOf(mask).zigTypeTag(mod)) { + .Array, .Vector => sema.typeOf(mask).arrayLen(mod), else => return sema.fail(block, mask_src, "expected vector or array, found '{}'", .{sema.typeOf(mask).fmt(sema.mod)}), }; - mask_ty = try Type.Tag.vector.create(sema.arena, .{ - .len = mask_len, - .elem_type = Type.i32, + mask_ty = try mod.vectorType(.{ + .len = @intCast(u32, mask_len), + .child = .i32_type, }); mask = try sema.coerce(block, mask_ty, mask, mask_src); const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask, "shuffle mask must be comptime-known"); @@ -21065,27 +22257,28 @@ fn analyzeShuffle( mask: Value, mask_len: u32, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node }; const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node }; const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node }; var a = a_arg; var b = b_arg; - const res_ty = try Type.Tag.vector.create(sema.arena, .{ + const res_ty = try mod.vectorType(.{ .len = mask_len, - .elem_type = elem_ty, + .child = elem_ty.toIntern(), }); - var maybe_a_len = switch (sema.typeOf(a).zigTypeTag()) { - .Array, .Vector => sema.typeOf(a).arrayLen(), + var maybe_a_len = switch (sema.typeOf(a).zigTypeTag(mod)) { + .Array, .Vector => sema.typeOf(a).arrayLen(mod), .Undefined => null, else => return sema.fail(block, a_src, "expected vector or array with element type '{}', found '{}'", .{ elem_ty.fmt(sema.mod), sema.typeOf(a).fmt(sema.mod), }), }; - var maybe_b_len = switch (sema.typeOf(b).zigTypeTag()) { - .Array, .Vector => sema.typeOf(b).arrayLen(), + var maybe_b_len = switch (sema.typeOf(b).zigTypeTag(mod)) { + .Array, .Vector => sema.typeOf(b).arrayLen(mod), .Undefined => null, else => return sema.fail(block, b_src, "expected vector or array with element type '{}', found '{}'", .{ elem_ty.fmt(sema.mod), @@ -21095,16 +22288,16 @@ fn analyzeShuffle( if (maybe_a_len == null and maybe_b_len == null) { return sema.addConstUndef(res_ty); } - const a_len = maybe_a_len orelse maybe_b_len.?; - const b_len = maybe_b_len orelse a_len; + const a_len = @intCast(u32, maybe_a_len orelse maybe_b_len.?); + const b_len = @intCast(u32, maybe_b_len orelse a_len); - const a_ty = try Type.Tag.vector.create(sema.arena, .{ + const a_ty = try mod.vectorType(.{ .len = a_len, - .elem_type = elem_ty, + .child = elem_ty.toIntern(), }); - const b_ty = try Type.Tag.vector.create(sema.arena, .{ + const b_ty = try mod.vectorType(.{ .len = b_len, - .elem_type = elem_ty, + .child = elem_ty.toIntern(), }); if (maybe_a_len == null) a = try sema.addConstUndef(a_ty) else a = try sema.coerce(block, a_ty, a, a_src); @@ -21115,12 +22308,10 @@ fn analyzeShuffle( .{ b_len, b_src, b_ty }, }; - var i: usize = 0; - while (i < mask_len) : (i += 1) { - var buf: Value.ElemValueBuffer = undefined; - const elem = mask.elemValueBuffer(sema.mod, i, &buf); - if (elem.isUndef()) continue; - const int = elem.toSignedInt(sema.mod.getTarget()); + for (0..@intCast(usize, mask_len)) |i| { + const elem = try mask.elemValue(sema.mod, i); + if (elem.isUndef(mod)) continue; + const int = elem.toSignedInt(mod); var unsigned: u32 = undefined; var chosen: u32 = undefined; if (int >= 0) { @@ -21152,26 +22343,21 @@ fn analyzeShuffle( if (try sema.resolveMaybeUndefVal(a)) |a_val| { if (try sema.resolveMaybeUndefVal(b)) |b_val| { - const values = try sema.arena.alloc(Value, mask_len); - - i = 0; - while (i < mask_len) : (i += 1) { - var buf: Value.ElemValueBuffer = undefined; - const mask_elem_val = mask.elemValueBuffer(sema.mod, i, &buf); - if (mask_elem_val.isUndef()) { - values[i] = Value.undef; + const values = try sema.arena.alloc(InternPool.Index, mask_len); + for (values, 0..) |*value, i| { + const mask_elem_val = try mask.elemValue(sema.mod, i); + if (mask_elem_val.isUndef(mod)) { + value.* = try mod.intern(.{ .undef = elem_ty.toIntern() }); continue; } - const int = mask_elem_val.toSignedInt(sema.mod.getTarget()); + const int = mask_elem_val.toSignedInt(mod); const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int); - if (int >= 0) { - values[i] = try a_val.elemValue(sema.mod, sema.arena, unsigned); - } else { - values[i] = try b_val.elemValue(sema.mod, sema.arena, unsigned); - } + values[i] = try (try (if (int >= 0) a_val else b_val).elemValue(mod, unsigned)).intern(elem_ty, mod); } - const res_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstant(res_ty, res_val); + return sema.addConstant(res_ty, (try mod.intern(.{ .aggregate = .{ + .ty = res_ty.toIntern(), + .storage = .{ .elems = values }, + } })).toValue()); } } @@ -21181,31 +22367,31 @@ fn analyzeShuffle( // to it up to the length of the longer vector. This recursion terminates // in 1 call because these calls to analyzeShuffle guarantee a_len == b_len. if (a_len != b_len) { - const min_len = std.math.min(a_len, b_len); + const min_len = @min(a_len, b_len); const max_src = if (a_len > b_len) a_src else b_src; - const max_len = try sema.usizeCast(block, max_src, std.math.max(a_len, b_len)); + const max_len = try sema.usizeCast(block, max_src, @max(a_len, b_len)); - const expand_mask_values = try sema.arena.alloc(Value, max_len); - i = 0; - while (i < min_len) : (i += 1) { - expand_mask_values[i] = try Value.Tag.int_u64.create(sema.arena, i); + const expand_mask_values = try sema.arena.alloc(InternPool.Index, max_len); + for (@intCast(usize, 0)..@intCast(usize, min_len)) |i| { + expand_mask_values[i] = (try mod.intValue(Type.comptime_int, i)).toIntern(); } - while (i < max_len) : (i += 1) { - expand_mask_values[i] = Value.negative_one; + for (@intCast(usize, min_len)..@intCast(usize, max_len)) |i| { + expand_mask_values[i] = (try mod.intValue(Type.comptime_int, -1)).toIntern(); } - const expand_mask = try Value.Tag.aggregate.create(sema.arena, expand_mask_values); + const expand_mask = try mod.intern(.{ .aggregate = .{ + .ty = (try mod.vectorType(.{ .len = @intCast(u32, max_len), .child = .comptime_int_type })).toIntern(), + .storage = .{ .elems = expand_mask_values }, + } }); if (a_len < b_len) { const undef = try sema.addConstUndef(a_ty); - a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, expand_mask, @intCast(u32, max_len)); + a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, expand_mask.toValue(), @intCast(u32, max_len)); } else { const undef = try sema.addConstUndef(b_ty); - b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, expand_mask, @intCast(u32, max_len)); + b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, expand_mask.toValue(), @intCast(u32, max_len)); } } - const mask_index = @intCast(u32, sema.air_values.items.len); - try sema.air_values.append(sema.gpa, mask); return block.addInst(.{ .tag = .shuffle, .data = .{ .ty_pl = .{ @@ -21213,7 +22399,7 @@ fn analyzeShuffle( .payload = try block.sema.addExtra(Air.Shuffle{ .a = a, .b = b, - .mask = mask_index, + .mask = mask.toIntern(), .mask_len = mask_len, }), } }, @@ -21221,6 +22407,7 @@ fn analyzeShuffle( } fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); @@ -21234,16 +22421,22 @@ fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C const pred_uncoerced = try sema.resolveInst(extra.pred); const pred_ty = sema.typeOf(pred_uncoerced); - const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison()) { - .Vector, .Array => pred_ty.arrayLen(), - else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(sema.mod)}), + const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison(mod)) { + .Vector, .Array => pred_ty.arrayLen(mod), + else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(mod)}), }; - const vec_len = try sema.usizeCast(block, pred_src, vec_len_u64); + const vec_len = @intCast(u32, try sema.usizeCast(block, pred_src, vec_len_u64)); - const bool_vec_ty = try Type.vector(sema.arena, vec_len, Type.bool); + const bool_vec_ty = try mod.vectorType(.{ + .len = vec_len, + .child = .bool_type, + }); const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src); - const vec_ty = try Type.vector(sema.arena, vec_len, elem_ty); + const vec_ty = try mod.vectorType(.{ + .len = vec_len, + .child = elem_ty.toIntern(), + }); const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src); const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src); @@ -21252,45 +22445,40 @@ fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C const maybe_b = try sema.resolveMaybeUndefVal(b); const runtime_src = if (maybe_pred) |pred_val| rs: { - if (pred_val.isUndef()) return sema.addConstUndef(vec_ty); + if (pred_val.isUndef(mod)) return sema.addConstUndef(vec_ty); if (maybe_a) |a_val| { - if (a_val.isUndef()) return sema.addConstUndef(vec_ty); + if (a_val.isUndef(mod)) return sema.addConstUndef(vec_ty); if (maybe_b) |b_val| { - if (b_val.isUndef()) return sema.addConstUndef(vec_ty); + if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty); - var buf: Value.ElemValueBuffer = undefined; - const elems = try sema.gpa.alloc(Value, vec_len); + const elems = try sema.gpa.alloc(InternPool.Index, vec_len); for (elems, 0..) |*elem, i| { - const pred_elem_val = pred_val.elemValueBuffer(sema.mod, i, &buf); + const pred_elem_val = try pred_val.elemValue(mod, i); const should_choose_a = pred_elem_val.toBool(); - if (should_choose_a) { - elem.* = a_val.elemValueBuffer(sema.mod, i, &buf); - } else { - elem.* = b_val.elemValueBuffer(sema.mod, i, &buf); - } + elem.* = try (try (if (should_choose_a) a_val else b_val).elemValue(mod, i)).intern(elem_ty, mod); } - return sema.addConstant( - vec_ty, - try Value.Tag.aggregate.create(sema.arena, elems), - ); + return sema.addConstant(vec_ty, (try mod.intern(.{ .aggregate = .{ + .ty = vec_ty.toIntern(), + .storage = .{ .elems = elems }, + } })).toValue()); } else { break :rs b_src; } } else { if (maybe_b) |b_val| { - if (b_val.isUndef()) return sema.addConstUndef(vec_ty); + if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty); } break :rs a_src; } } else rs: { if (maybe_a) |a_val| { - if (a_val.isUndef()) return sema.addConstUndef(vec_ty); + if (a_val.isUndef(mod)) return sema.addConstUndef(vec_ty); } if (maybe_b) |b_val| { - if (b_val.isUndef()) return sema.addConstUndef(vec_ty); + if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty); } break :rs pred_src; }; @@ -21354,6 +22542,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! } fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; const src = inst_data.src(); @@ -21370,7 +22559,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); - switch (elem_ty.zigTypeTag()) { + switch (elem_ty.zigTypeTag(mod)) { .Enum => if (op != .Xchg) { return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{}); }, @@ -21400,8 +22589,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); break :rs operand_src; }; - if (ptr_val.isComptimeMutablePtr()) { - const target = sema.mod.getTarget(); + if (ptr_val.isComptimeMutablePtr(mod)) { const ptr_ty = sema.typeOf(ptr); const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; const new_val = switch (op) { @@ -21409,12 +22597,12 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A .Xchg => operand_val, .Add => try sema.numberAddWrapScalar(stored_val, operand_val, elem_ty), .Sub => try sema.numberSubWrapScalar(stored_val, operand_val, elem_ty), - .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, sema.mod), - .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, sema.mod), - .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, sema.mod), - .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, sema.mod), - .Max => stored_val.numberMax (operand_val, target), - .Min => stored_val.numberMin (operand_val, target), + .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, mod), + .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, mod), + .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, mod), + .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, mod), + .Max => stored_val.numberMax (operand_val, mod), + .Min => stored_val.numberMin (operand_val, mod), // zig fmt: on }; try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty); @@ -21488,18 +22676,19 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const maybe_mulend1 = try sema.resolveMaybeUndefVal(mulend1); const maybe_mulend2 = try sema.resolveMaybeUndefVal(mulend2); const maybe_addend = try sema.resolveMaybeUndefVal(addend); + const mod = sema.mod; - switch (ty.zigTypeTag()) { - .ComptimeFloat, .Float, .Vector => {}, + switch (ty.scalarType(mod).zigTypeTag(mod)) { + .ComptimeFloat, .Float => {}, else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(sema.mod)}), } const runtime_src = if (maybe_mulend1) |mulend1_val| rs: { if (maybe_mulend2) |mulend2_val| { - if (mulend2_val.isUndef()) return sema.addConstUndef(ty); + if (mulend2_val.isUndef(mod)) return sema.addConstUndef(ty); if (maybe_addend) |addend_val| { - if (addend_val.isUndef()) return sema.addConstUndef(ty); + if (addend_val.isUndef(mod)) return sema.addConstUndef(ty); const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, sema.mod); return sema.addConstant(ty, result_val); } else { @@ -21507,16 +22696,16 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } } else { if (maybe_addend) |addend_val| { - if (addend_val.isUndef()) return sema.addConstUndef(ty); + if (addend_val.isUndef(mod)) return sema.addConstUndef(ty); } break :rs mulend2_src; } } else rs: { if (maybe_mulend2) |mulend2_val| { - if (mulend2_val.isUndef()) return sema.addConstUndef(ty); + if (mulend2_val.isUndef(mod)) return sema.addConstUndef(ty); } if (maybe_addend) |addend_val| { - if (addend_val.isUndef()) return sema.addConstUndef(ty); + if (addend_val.isUndef(mod)) return sema.addConstUndef(ty); } break :rs mulend1_src; }; @@ -21538,6 +22727,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const modifier_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; @@ -21551,7 +22741,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const air_ref = try sema.resolveInst(extra.modifier); const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src); const modifier_val = try sema.resolveConstValue(block, modifier_src, modifier_ref, "call modifier must be comptime-known"); - var modifier = modifier_val.toEnum(std.builtin.CallModifier); + var modifier = mod.toEnum(std.builtin.CallModifier, modifier_val); switch (modifier) { // These can be upgraded to comptime or nosuspend calls. .auto, .never_tail, .no_async => { @@ -21597,32 +22787,19 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const args = try sema.resolveInst(extra.args); const args_ty = sema.typeOf(args); - if (!args_ty.isTuple() and args_ty.tag() != .empty_struct_literal) { + if (!args_ty.isTuple(mod) and args_ty.toIntern() != .empty_struct_type) { return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(sema.mod)}); } - var resolved_args: []Air.Inst.Ref = undefined; - - // Desugar bound functions here - var bound_arg_src: ?LazySrcLoc = null; - if (sema.typeOf(func).tag() == .bound_fn) { - bound_arg_src = func_src; - const bound_func = try sema.resolveValue(block, .unneeded, func, ""); - const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data; - func = bound_data.func_inst; - resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount() + 1); - resolved_args[0] = bound_data.arg0_inst; - for (resolved_args[1..], 0..) |*resolved, i| { - resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty); - } - } else { - resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount()); - for (resolved_args, 0..) |*resolved, i| { - resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty); - } + var resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(mod)); + for (resolved_args, 0..) |*resolved, i| { + resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty); } + + const callee_ty = sema.typeOf(func); + const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false); const ensure_result_used = extra.flags.ensure_result_used; - return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src); + return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, null, null); } fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -21634,19 +22811,21 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const parent_ty = try sema.resolveType(block, ty_src, extra.parent_type); - const field_name = try sema.resolveConstString(block, name_src, extra.field_name, "field name must be comptime-known"); + const field_name = try sema.resolveConstStringIntern(block, name_src, extra.field_name, "field name must be comptime-known"); const field_ptr = try sema.resolveInst(extra.field_ptr); const field_ptr_ty = sema.typeOf(field_ptr); + const mod = sema.mod; + const ip = &mod.intern_pool; - if (parent_ty.zigTypeTag() != .Struct and parent_ty.zigTypeTag() != .Union) { + if (parent_ty.zigTypeTag(mod) != .Struct and parent_ty.zigTypeTag(mod) != .Union) { return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)}); } try sema.resolveTypeLayout(parent_ty); - const field_index = switch (parent_ty.zigTypeTag()) { + const field_index = switch (parent_ty.zigTypeTag(mod)) { .Struct => blk: { - if (parent_ty.isTuple()) { - if (mem.eql(u8, field_name, "len")) { + if (parent_ty.isTuple(mod)) { + if (ip.stringEqlSlice(field_name, "len")) { return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); } break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, name_src); @@ -21658,27 +22837,27 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr else => unreachable, }; - if (parent_ty.zigTypeTag() == .Struct and parent_ty.structFieldIsComptime(field_index)) { + if (parent_ty.zigTypeTag(mod) == .Struct and parent_ty.structFieldIsComptime(field_index, mod)) { return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{}); } try sema.checkPtrOperand(block, ptr_src, field_ptr_ty); - const field_ptr_ty_info = field_ptr_ty.ptrInfo().data; + const field_ptr_ty_info = field_ptr_ty.ptrInfo(mod); var ptr_ty_data: Type.Payload.Pointer.Data = .{ - .pointee_type = parent_ty.structFieldType(field_index), + .pointee_type = parent_ty.structFieldType(field_index, mod), .mutable = field_ptr_ty_info.mutable, .@"addrspace" = field_ptr_ty_info.@"addrspace", }; - if (parent_ty.containerLayout() == .Packed) { + if (parent_ty.containerLayout(mod) == .Packed) { return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{}); } else { ptr_ty_data.@"align" = blk: { - if (parent_ty.castTag(.@"struct")) |struct_obj| { - break :blk struct_obj.data.fields.values()[field_index].abi_align; - } else if (parent_ty.cast(Type.Payload.Union)) |union_obj| { - break :blk union_obj.data.fields.values()[field_index].abi_align; + if (mod.typeToStruct(parent_ty)) |struct_obj| { + break :blk struct_obj.fields.values()[field_index].abi_align; + } else if (mod.typeToUnion(parent_ty)) |union_obj| { + break :blk union_obj.fields.values()[field_index].abi_align; } else { break :blk 0; } @@ -21692,19 +22871,24 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| { - const payload = field_ptr_val.castTag(.field_ptr) orelse { - return sema.fail(block, ptr_src, "pointer value not based on parent struct", .{}); - }; - if (payload.data.field_index != field_index) { + const field = switch (ip.indexToKey(field_ptr_val.toIntern())) { + .ptr => |ptr| switch (ptr.addr) { + .field => |field| field, + else => null, + }, + else => null, + } orelse return sema.fail(block, ptr_src, "pointer value not based on parent struct", .{}); + + if (field.index != field_index) { const msg = msg: { const msg = try sema.errMsg( block, src, - "field '{s}' has index '{d}' but pointer value is index '{d}' of struct '{}'", + "field '{}' has index '{d}' but pointer value is index '{d}' of struct '{}'", .{ - field_name, + field_name.fmt(ip), field_index, - payload.data.field_index, + field.index, parent_ty.fmt(sema.mod), }, ); @@ -21714,7 +22898,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr }; return sema.failWithOwnedErrorMsg(msg); } - return sema.addConstant(result_ptr, payload.data.container_ptr); + return sema.addConstant(result_ptr, field.base.toValue()); } try sema.requireRuntimeBlock(block, src, ptr_src); @@ -21746,69 +22930,267 @@ fn zirMinMax( const rhs = try sema.resolveInst(extra.rhs); try sema.checkNumericType(block, lhs_src, sema.typeOf(lhs)); try sema.checkNumericType(block, rhs_src, sema.typeOf(rhs)); - return sema.analyzeMinMax(block, src, lhs, rhs, air_tag, lhs_src, rhs_src); + return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src }); +} + +fn zirMinMaxMulti( + sema: *Sema, + block: *Block, + extended: Zir.Inst.Extended.InstData, + comptime air_tag: Air.Inst.Tag, +) CompileError!Air.Inst.Ref { + const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); + const src_node = extra.data.src_node; + const src = LazySrcLoc.nodeOffset(src_node); + const operands = sema.code.refSlice(extra.end, extended.small); + + const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); + const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len); + + for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| { + op_src.* = switch (i) { + 0 => .{ .node_offset_builtin_call_arg0 = src_node }, + 1 => .{ .node_offset_builtin_call_arg1 = src_node }, + 2 => .{ .node_offset_builtin_call_arg2 = src_node }, + 3 => .{ .node_offset_builtin_call_arg3 = src_node }, + 4 => .{ .node_offset_builtin_call_arg4 = src_node }, + 5 => .{ .node_offset_builtin_call_arg5 = src_node }, + else => src, // TODO: better source location + }; + air_ref.* = try sema.resolveInst(zir_ref); + try sema.checkNumericType(block, op_src.*, sema.typeOf(air_ref.*)); + } + + return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs); } fn analyzeMinMax( sema: *Sema, block: *Block, src: LazySrcLoc, - lhs: Air.Inst.Ref, - rhs: Air.Inst.Ref, comptime air_tag: Air.Inst.Tag, - lhs_src: LazySrcLoc, - rhs_src: LazySrcLoc, + operands: []const Air.Inst.Ref, + operand_srcs: []const LazySrcLoc, ) CompileError!Air.Inst.Ref { - const simd_op = try sema.checkSimdBinOp(block, src, lhs, rhs, lhs_src, rhs_src); + assert(operands.len == operand_srcs.len); + assert(operands.len > 0); + const mod = sema.mod; - // TODO @max(max_int, undefined) should return max_int + if (operands.len == 1) return operands[0]; - const runtime_src = if (simd_op.lhs_val) |lhs_val| rs: { - if (lhs_val.isUndef()) return sema.addConstUndef(simd_op.result_ty); + const opFunc = switch (air_tag) { + .min => Value.numberMin, + .max => Value.numberMax, + else => @compileError("unreachable"), + }; - const rhs_val = simd_op.rhs_val orelse break :rs rhs_src; + // The set of runtime-known operands. Set up in the loop below. + var runtime_known = try std.DynamicBitSet.initFull(sema.arena, operands.len); + // The current minmax value - initially this will always be comptime-known, then we'll add + // runtime values into the mix later. + var cur_minmax: ?Air.Inst.Ref = null; + var cur_minmax_src: LazySrcLoc = undefined; // defined if cur_minmax not null + // The current known scalar bounds of the value. + var bounds_status: enum { + unknown, // We've only seen undef comptime_ints so far, so do not know the bounds. + defined, // We've seen only integers, so the bounds are defined. + non_integral, // There are floats in the mix, so the bounds aren't defined. + } = .unknown; + var cur_min_scalar: Value = undefined; + var cur_max_scalar: Value = undefined; + + // First, find all comptime-known arguments, and get their min/max + + for (operands, operand_srcs, 0..) |operand, operand_src, operand_idx| { + // Resolve the value now to avoid redundant calls to `checkSimdBinOp` - we'll have to call + // it in the runtime path anyway since the result type may have been refined + const unresolved_uncoerced_val = try sema.resolveMaybeUndefVal(operand) orelse continue; + const uncoerced_val = try sema.resolveLazyValue(unresolved_uncoerced_val); + + runtime_known.unset(operand_idx); + + switch (bounds_status) { + .unknown, .defined => refine_bounds: { + const ty = sema.typeOf(operand); + if (!ty.scalarType(mod).isInt(mod) and !ty.scalarType(mod).eql(Type.comptime_int, mod)) { + bounds_status = .non_integral; + break :refine_bounds; + } + const scalar_bounds: ?[2]Value = bounds: { + if (!ty.isVector(mod)) break :bounds try uncoerced_val.intValueBounds(mod); + var cur_bounds: [2]Value = try Value.intValueBounds(try uncoerced_val.elemValue(mod, 0), mod) orelse break :bounds null; + const len = try sema.usizeCast(block, src, ty.vectorLen(mod)); + for (1..len) |i| { + const elem = try uncoerced_val.elemValue(mod, i); + const elem_bounds = try elem.intValueBounds(mod) orelse break :bounds null; + cur_bounds = .{ + Value.numberMin(elem_bounds[0], cur_bounds[0], mod), + Value.numberMax(elem_bounds[1], cur_bounds[1], mod), + }; + } + break :bounds cur_bounds; + }; + if (scalar_bounds) |bounds| { + if (bounds_status == .unknown) { + cur_min_scalar = bounds[0]; + cur_max_scalar = bounds[1]; + bounds_status = .defined; + } else { + cur_min_scalar = opFunc(cur_min_scalar, bounds[0], mod); + cur_max_scalar = opFunc(cur_max_scalar, bounds[1], mod); + } + } + }, + .non_integral => {}, + } - if (rhs_val.isUndef()) return sema.addConstUndef(simd_op.result_ty); + const cur = cur_minmax orelse { + cur_minmax = operand; + cur_minmax_src = operand_src; + continue; + }; - try sema.resolveLazyValue(lhs_val); - try sema.resolveLazyValue(rhs_val); + const simd_op = try sema.checkSimdBinOp(block, src, cur, operand, cur_minmax_src, operand_src); + const cur_val = try sema.resolveLazyValue(simd_op.lhs_val.?); // cur_minmax is comptime-known + const operand_val = try sema.resolveLazyValue(simd_op.rhs_val.?); // we checked the operand was resolvable above - const opFunc = switch (air_tag) { - .min => Value.numberMin, - .max => Value.numberMax, - else => unreachable, - }; - const target = sema.mod.getTarget(); const vec_len = simd_op.len orelse { - const result_val = opFunc(lhs_val, rhs_val, target); - return sema.addConstant(simd_op.result_ty, result_val); + const result_val = opFunc(cur_val, operand_val, mod); + cur_minmax = try sema.addConstant(simd_op.result_ty, result_val); + continue; }; - var lhs_buf: Value.ElemValueBuffer = undefined; - var rhs_buf: Value.ElemValueBuffer = undefined; - const elems = try sema.arena.alloc(Value, vec_len); + const elems = try sema.arena.alloc(InternPool.Index, vec_len); for (elems, 0..) |*elem, i| { - const lhs_elem_val = lhs_val.elemValueBuffer(sema.mod, i, &lhs_buf); - const rhs_elem_val = rhs_val.elemValueBuffer(sema.mod, i, &rhs_buf); - elem.* = opFunc(lhs_elem_val, rhs_elem_val, target); + const lhs_elem_val = try cur_val.elemValue(mod, i); + const rhs_elem_val = try operand_val.elemValue(mod, i); + const uncoerced_elem = opFunc(lhs_elem_val, rhs_elem_val, mod); + elem.* = (try mod.getCoerced(uncoerced_elem, simd_op.scalar_ty)).toIntern(); } - return sema.addConstant( - simd_op.result_ty, - try Value.Tag.aggregate.create(sema.arena, elems), - ); - } else rs: { - if (simd_op.rhs_val) |rhs_val| { - if (rhs_val.isUndef()) return sema.addConstUndef(simd_op.result_ty); + cur_minmax = try sema.addConstant(simd_op.result_ty, (try mod.intern(.{ .aggregate = .{ + .ty = simd_op.result_ty.toIntern(), + .storage = .{ .elems = elems }, + } })).toValue()); + } + + const opt_runtime_idx = runtime_known.findFirstSet(); + + if (cur_minmax) |ct_minmax_ref| refine: { + // Refine the comptime-known result type based on the bounds. This isn't strictly necessary + // in the runtime case, since we'll refine the type again later, but keeping things as small + // as possible will allow us to emit more optimal AIR (if all the runtime operands have + // smaller types than the non-refined comptime type). + + const val = (try sema.resolveMaybeUndefVal(ct_minmax_ref)).?; + const orig_ty = sema.typeOf(ct_minmax_ref); + + if (opt_runtime_idx == null and orig_ty.scalarType(mod).eql(Type.comptime_int, mod)) { + // If all arguments were `comptime_int`, and there are no runtime args, we'll preserve that type + break :refine; } - break :rs lhs_src; - }; + // We can't refine float types + if (orig_ty.scalarType(mod).isAnyFloat()) break :refine; + + assert(bounds_status == .defined); // there was a non-comptime-int integral comptime-known arg + + const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar); + const refined_ty = if (orig_ty.isVector(mod)) try mod.vectorType(.{ + .len = orig_ty.vectorLen(mod), + .child = refined_scalar_ty.toIntern(), + }) else refined_scalar_ty; + + // Apply the refined type to the current value + if (std.debug.runtime_safety) { + assert(try sema.intFitsInType(val, refined_ty, null)); + } + cur_minmax = try sema.coerceInMemory(val, refined_ty); + } + + const runtime_idx = opt_runtime_idx orelse return cur_minmax.?; + const runtime_src = operand_srcs[runtime_idx]; try sema.requireRuntimeBlock(block, src, runtime_src); - return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs); + + // Now, iterate over runtime operands, emitting a min/max instruction for each. We'll refine the + // type again at the end, based on the comptime-known bound. + + // If the comptime-known part is undef we can avoid emitting actual instructions later + const known_undef = if (cur_minmax) |operand| blk: { + const val = (try sema.resolveMaybeUndefVal(operand)).?; + break :blk val.isUndef(mod); + } else false; + + if (cur_minmax == null) { + // No comptime operands - use the first operand as the starting value + assert(runtime_idx == 0); + cur_minmax = operands[0]; + cur_minmax_src = runtime_src; + runtime_known.unset(0); // don't look at this operand in the loop below + const scalar_ty = sema.typeOf(cur_minmax.?).scalarType(mod); + if (scalar_ty.isInt(mod)) { + cur_min_scalar = try scalar_ty.minInt(mod, scalar_ty); + cur_max_scalar = try scalar_ty.maxInt(mod, scalar_ty); + } + } + + var it = runtime_known.iterator(.{}); + while (it.next()) |idx| { + const lhs = cur_minmax.?; + const lhs_src = cur_minmax_src; + const rhs = operands[idx]; + const rhs_src = operand_srcs[idx]; + const simd_op = try sema.checkSimdBinOp(block, src, lhs, rhs, lhs_src, rhs_src); + if (known_undef) { + cur_minmax = try sema.addConstUndef(simd_op.result_ty); + } else { + cur_minmax = try block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs); + } + // Compute the bounds of this type + switch (bounds_status) { + .unknown, .defined => refine_bounds: { + const scalar_ty = sema.typeOf(rhs).scalarType(mod); + if (scalar_ty.isAnyFloat()) { + bounds_status = .non_integral; + break :refine_bounds; + } + const scalar_min = try scalar_ty.minInt(mod, scalar_ty); + const scalar_max = try scalar_ty.maxInt(mod, scalar_ty); + if (bounds_status == .unknown) { + cur_min_scalar = scalar_min; + cur_max_scalar = scalar_max; + bounds_status = .defined; + } else { + cur_min_scalar = opFunc(cur_min_scalar, scalar_min, mod); + cur_max_scalar = opFunc(cur_max_scalar, scalar_max, mod); + } + }, + .non_integral => {}, + } + } + + // Finally, refine the type based on the known bounds. + const unrefined_ty = sema.typeOf(cur_minmax.?); + if (unrefined_ty.scalarType(mod).isAnyFloat()) { + // We can't refine floats, so we're done. + return cur_minmax.?; + } + assert(bounds_status == .defined); // there were integral runtime operands + const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar); + const refined_ty = if (unrefined_ty.isVector(mod)) try mod.vectorType(.{ + .len = unrefined_ty.vectorLen(mod), + .child = refined_scalar_ty.toIntern(), + }) else refined_scalar_ty; + + if (!refined_ty.eql(unrefined_ty, mod)) { + // We've reduced the type - cast the result down + return block.addTyOp(.intcast, refined_ty, cur_minmax.?); + } + + return cur_minmax.?; } fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref { const mod = sema.mod; - const info = sema.typeOf(ptr).ptrInfo().data; + const info = sema.typeOf(ptr).ptrInfo(mod); if (info.size == .One) { // Already an array pointer. return ptr; @@ -21837,19 +23219,26 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const dest_ptr = try sema.resolveInst(extra.lhs); const src_ptr = try sema.resolveInst(extra.rhs); + const dest_ty = sema.typeOf(dest_ptr); + const src_ty = sema.typeOf(src_ptr); const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr); const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr); const target = sema.mod.getTarget(); + const mod = sema.mod; + + if (dest_ty.isConstPtr(mod)) { + return sema.fail(block, dest_src, "cannot memcpy to constant pointer", .{}); + } if (dest_len == .none and src_len == .none) { const msg = msg: { const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{}); errdefer msg.destroy(sema.gpa); - try sema.errNote(block, dest_src, msg, "destination type {} provides no length", .{ - sema.typeOf(dest_ptr).fmt(sema.mod), + try sema.errNote(block, dest_src, msg, "destination type '{}' provides no length", .{ + dest_ty.fmt(sema.mod), }); - try sema.errNote(block, src_src, msg, "source type {} provides no length", .{ - sema.typeOf(src_ptr).fmt(sema.mod), + try sema.errNote(block, src_src, msg, "source type '{}' provides no length", .{ + src_ty.fmt(sema.mod), }); break :msg msg; }; @@ -21887,16 +23276,24 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); try sema.addSafetyCheck(block, ok, .memcpy_len_mismatch); } + } else if (dest_len != .none) { + if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { + len_val = dest_len_val; + } + } else if (src_len != .none) { + if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { + len_val = src_len_val; + } } const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { - if (!dest_ptr_val.isComptimeMutablePtr()) break :rs dest_src; + if (!dest_ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src; if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |_| { - const len_u64 = (try len_val.?.getUnsignedIntAdvanced(target, sema)).?; + const len_u64 = (try len_val.?.getUnsignedIntAdvanced(mod, sema)).?; const len = try sema.usizeCast(block, dest_src, len_u64); for (0..len) |i| { const elem_index = try sema.addIntUnsigned(Type.usize, i); - const dest_elem_ptr = try sema.elemPtr( + const dest_elem_ptr = try sema.elemPtrOneLayerOnly( block, src, dest_ptr, @@ -21905,7 +23302,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void true, // init false, // oob_safety ); - const src_elem_ptr = try sema.elemPtr( + const src_elem_ptr = try sema.elemPtrOneLayerOnly( block, src, src_ptr, @@ -21929,21 +23326,18 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void } else break :rs src_src; } else dest_src; - const dest_ty = sema.typeOf(dest_ptr); - const src_ty = sema.typeOf(src_ptr); - // If in-memory coercion is not allowed, explode this memcpy call into a // for loop that copies element-wise. // Likewise if this is an iterable rather than a pointer, do the same // lowering. The AIR instruction requires pointers with element types of // equal ABI size. - if (dest_ty.zigTypeTag() != .Pointer or src_ty.zigTypeTag() != .Pointer) { + if (dest_ty.zigTypeTag(mod) != .Pointer or src_ty.zigTypeTag(mod) != .Pointer) { return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{}); } - const dest_elem_ty = dest_ty.elemType2(); - const src_elem_ty = src_ty.elemType2(); + const dest_elem_ty = dest_ty.elemType2(mod); + const src_elem_ty = src_ty.elemType2(mod); if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) { return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{}); } @@ -21954,7 +23348,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void var new_dest_ptr = dest_ptr; var new_src_ptr = src_ptr; if (len_val) |val| { - const len = val.toUnsignedInt(target); + const len = val.toUnsignedInt(mod); if (len == 0) { // This AIR instruction guarantees length > 0 if it is comptime-known. return; @@ -21967,7 +23361,15 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void // Change the src from slice to a many pointer, to avoid multiple ptr // slice extractions in AIR instructions. const new_src_ptr_ty = sema.typeOf(new_src_ptr); - if (new_src_ptr_ty.isSlice()) { + if (new_src_ptr_ty.isSlice(mod)) { + new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); + } + } else if (dest_len == .none and len_val == null) { + // Change the dest to a slice, since its type must have the length. + const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr); + new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, .unneeded, dest_src, dest_src, dest_src, false); + const new_src_ptr_ty = sema.typeOf(new_src_ptr); + if (new_src_ptr_ty.isSlice(mod)) { new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); } } @@ -21986,14 +23388,30 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void // Extract raw pointer from dest slice. The AIR instructions could support them, but // it would cause redundant machine code instructions. const new_dest_ptr_ty = sema.typeOf(new_dest_ptr); - const raw_dest_ptr = if (new_dest_ptr_ty.isSlice()) + const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(mod)) try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty) - else - new_dest_ptr; + else if (new_dest_ptr_ty.ptrSize(mod) == .One) ptr: { + var dest_manyptr_ty_key = mod.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type; + 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 mod.ptrType(dest_manyptr_ty_key), new_dest_ptr, dest_src); + } else new_dest_ptr; + + const new_src_ptr_ty = sema.typeOf(new_src_ptr); + const raw_src_ptr = if (new_src_ptr_ty.isSlice(mod)) + try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty) + else if (new_src_ptr_ty.ptrSize(mod) == .One) ptr: { + var src_manyptr_ty_key = mod.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type; + 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 mod.ptrType(src_manyptr_ty_key), new_src_ptr, src_src); + } else new_src_ptr; // ok1: dest >= src + len // ok2: src >= dest + len - const src_plus_len = try sema.analyzePtrArithmetic(block, src, new_src_ptr, len, .ptr_add, src_src, src); + const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src); const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src); const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len); const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len); @@ -22011,6 +23429,9 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void } fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const src = inst_data.src(); @@ -22019,23 +23440,26 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const dest_ptr = try sema.resolveInst(extra.lhs); const uncoerced_elem = try sema.resolveInst(extra.rhs); const dest_ptr_ty = sema.typeOf(dest_ptr); - try checkIndexable(sema, block, dest_src, dest_ptr_ty); + try checkMemOperand(sema, block, dest_src, dest_ptr_ty); - const dest_elem_ty = dest_ptr_ty.elemType2(); - const target = sema.mod.getTarget(); + if (dest_ptr_ty.isConstPtr(mod)) { + return sema.fail(block, dest_src, "cannot memset constant pointer", .{}); + } + + const dest_elem_ty = dest_ptr_ty.elemType2(mod); const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: { - const len_air_ref = try sema.fieldVal(block, src, dest_ptr, "len", dest_src); + const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, "len"), 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.getUnsignedIntAdvanced(target, sema)).?; + const len_u64 = (try len_val.getUnsignedIntAdvanced(mod, sema)).?; const len = try sema.usizeCast(block, dest_src, len_u64); if (len == 0) { // This AIR instruction guarantees length > 0 if it is comptime-known. return; } - if (!ptr_val.isComptimeMutablePtr()) break :rs dest_src; + if (!ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src; if (try sema.resolveMaybeUndefVal(uncoerced_elem)) |_| { for (0..len) |i| { const elem_index = try sema.addIntUnsigned(Type.usize, i); @@ -22113,6 +23537,7 @@ fn zirVarExtended( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand); const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 }; const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 }; @@ -22148,47 +23573,33 @@ fn zirVarExtended( else uncasted_init; - break :blk (try sema.resolveMaybeUndefVal(init)) orelse - return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime-known"); - } else Value.initTag(.unreachable_value); + break :blk ((try sema.resolveMaybeUndefVal(init)) orelse + return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime-known")).toIntern(); + } else .none; try sema.validateVarType(block, ty_src, var_ty, small.is_extern); - const new_var = try sema.gpa.create(Module.Var); - errdefer sema.gpa.destroy(new_var); - - log.debug("created variable {*} owner_decl: {*} ({s})", .{ - new_var, sema.owner_decl, sema.owner_decl.name, - }); - - new_var.* = .{ - .owner_decl = sema.owner_decl_index, + return sema.addConstant(var_ty, (try mod.intern(.{ .variable = .{ + .ty = var_ty.toIntern(), .init = init_val, + .decl = sema.owner_decl_index, + .lib_name = if (lib_name) |lname| (try mod.intern_pool.getOrPutString( + sema.gpa, + try sema.handleExternLibName(block, ty_src, lname), + )).toOptional() else .none, .is_extern = small.is_extern, - .is_mutable = true, .is_threadlocal = small.is_threadlocal, - .is_weak_linkage = false, - .lib_name = null, - }; - - if (lib_name) |lname| { - new_var.lib_name = try sema.handleExternLibName(block, ty_src, lname); - } - - const result = try sema.addConstant( - var_ty, - try Value.Tag.variable.create(sema.arena, new_var), - ); - return result; + } })).toValue()); } fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); - const target = sema.mod.getTarget(); + const target = mod.getTarget(); const align_src: LazySrcLoc = .{ .node_offset_fn_type_align = inst_data.src_node }; const addrspace_src: LazySrcLoc = .{ .node_offset_fn_type_addrspace = inst_data.src_node }; @@ -22219,10 +23630,10 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A extra_index += body.len; const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29, "alignment must be comptime-known"); - if (val.tag() == .generic_poison) { + if (val.isGenericPoison()) { break :blk null; } - const alignment = @intCast(u32, val.toUnsignedInt(target)); + const alignment = @intCast(u32, val.toUnsignedInt(mod)); try sema.validateAlign(block, align_src, alignment); if (alignment == target_util.defaultFunctionAlignment(target)) { break :blk 0; @@ -22238,7 +23649,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }, else => |e| return e, }; - const alignment = @intCast(u32, align_tv.val.toUnsignedInt(target)); + const alignment = @intCast(u32, align_tv.val.toUnsignedInt(mod)); try sema.validateAlign(block, align_src, alignment); if (alignment == target_util.defaultFunctionAlignment(target)) { break :blk 0; @@ -22255,10 +23666,10 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const addrspace_ty = try sema.getBuiltinType("AddressSpace"); const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty, "addrespace must be comptime-known"); - if (val.tag() == .generic_poison) { + if (val.isGenericPoison()) { break :blk null; } - break :blk val.toEnum(std.builtin.AddressSpace); + break :blk mod.toEnum(std.builtin.AddressSpace, val); } else if (extra.data.bits.has_addrspace_ref) blk: { const addrspace_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; @@ -22268,7 +23679,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }, else => |e| return e, }; - break :blk addrspace_tv.val.toEnum(std.builtin.AddressSpace); + break :blk mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val); } else target_util.defaultAddressSpace(target, .function); const @"linksection": FuncLinkSection = if (extra.data.bits.has_section_body) blk: { @@ -22277,16 +23688,16 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; - const ty = Type.initTag(.const_slice_u8); + const ty = Type.slice_const_u8; const val = try sema.resolveGenericBody(block, section_src, body, inst, ty, "linksection must be comptime-known"); - if (val.tag() == .generic_poison) { + if (val.isGenericPoison()) { break :blk FuncLinkSection{ .generic = {} }; } - break :blk FuncLinkSection{ .explicit = try val.toAllocatedBytes(ty, sema.arena, sema.mod) }; + break :blk FuncLinkSection{ .explicit = try val.toIpString(ty, mod) }; } else if (extra.data.bits.has_section_ref) blk: { const section_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const section_name = sema.resolveConstString(block, section_src, section_ref, "linksection must be comptime-known") catch |err| switch (err) { + const section_name = sema.resolveConstStringIntern(block, section_src, section_ref, "linksection must be comptime-known") catch |err| switch (err) { error.GenericPoison => { break :blk FuncLinkSection{ .generic = {} }; }, @@ -22303,10 +23714,10 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const cc_ty = try sema.getBuiltinType("CallingConvention"); const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, "calling convention must be comptime-known"); - if (val.tag() == .generic_poison) { + if (val.isGenericPoison()) { break :blk null; } - break :blk val.toEnum(std.builtin.CallingConvention); + break :blk mod.toEnum(std.builtin.CallingConvention, val); } else if (extra.data.bits.has_cc_ref) blk: { const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; @@ -22316,7 +23727,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }, else => |e| return e, }; - break :blk cc_tv.val.toEnum(std.builtin.CallingConvention); + break :blk mod.toEnum(std.builtin.CallingConvention, cc_tv.val); } else if (sema.owner_decl.is_exported and has_body) .C else @@ -22329,20 +23740,18 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A extra_index += body.len; const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type, "return type must be comptime-known"); - var buffer: Value.ToTypeBuffer = undefined; - const ty = try val.toType(&buffer).copy(sema.arena); + const ty = val.toType(); break :blk ty; } else if (extra.data.bits.has_ret_ty_ref) blk: { const ret_ty_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref, "return type must be comptime-known") catch |err| switch (err) { error.GenericPoison => { - break :blk Type.initTag(.generic_poison); + break :blk Type.generic_poison; }, else => |e| return e, }; - var buffer: Value.ToTypeBuffer = undefined; - const ty = try ret_ty_tv.val.toType(&buffer).copy(sema.arena); + const ty = ret_ty_tv.val.toType(); break :blk ty; } else Type.void; @@ -22414,13 +23823,14 @@ fn zirCDefine( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const name = try sema.resolveConstString(block, name_src, extra.lhs, "name of macro being undefined must be comptime-known"); const rhs = try sema.resolveInst(extra.rhs); - if (sema.typeOf(rhs).zigTypeTag() != .Void) { + if (sema.typeOf(rhs).zigTypeTag(mod) != .Void) { const value = try sema.resolveConstString(block, val_src, extra.rhs, "value of macro being undefined must be comptime-known"); try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value }); } else { @@ -22486,27 +23896,29 @@ fn resolvePrefetchOptions( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, ) CompileError!std.builtin.PrefetchOptions { + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; const options_ty = try sema.getBuiltinType("PrefetchOptions"); const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src); - const target = sema.mod.getTarget(); const rw_src = sema.maybeOptionsSrc(block, src, "rw"); const locality_src = sema.maybeOptionsSrc(block, src, "locality"); const cache_src = sema.maybeOptionsSrc(block, src, "cache"); - const rw = try sema.fieldVal(block, src, options, "rw", rw_src); + const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "rw"), rw_src); const rw_val = try sema.resolveConstValue(block, rw_src, rw, "prefetch read/write must be comptime-known"); - const locality = try sema.fieldVal(block, src, options, "locality", locality_src); + const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "locality"), locality_src); const locality_val = try sema.resolveConstValue(block, locality_src, locality, "prefetch locality must be comptime-known"); - const cache = try sema.fieldVal(block, src, options, "cache", cache_src); + const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "cache"), cache_src); const cache_val = try sema.resolveConstValue(block, cache_src, cache, "prefetch cache must be comptime-known"); return std.builtin.PrefetchOptions{ - .rw = rw_val.toEnum(std.builtin.PrefetchOptions.Rw), - .locality = @intCast(u2, locality_val.toUnsignedInt(target)), - .cache = cache_val.toEnum(std.builtin.PrefetchOptions.Cache), + .rw = mod.toEnum(std.builtin.PrefetchOptions.Rw, rw_val), + .locality = @intCast(u2, locality_val.toUnsignedInt(mod)), + .cache = mod.toEnum(std.builtin.PrefetchOptions.Cache, cache_val), }; } @@ -22549,36 +23961,42 @@ fn resolveExternOptions( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, -) CompileError!std.builtin.ExternOptions { +) CompileError!struct { + name: InternPool.NullTerminatedString, + library_name: InternPool.OptionalNullTerminatedString = .none, + linkage: std.builtin.GlobalLinkage = .Strong, + is_thread_local: bool = false, +} { + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; const options_inst = try sema.resolveInst(zir_ref); const extern_options_ty = try sema.getBuiltinType("ExternOptions"); const options = try sema.coerce(block, extern_options_ty, options_inst, src); - const mod = sema.mod; const name_src = sema.maybeOptionsSrc(block, src, "name"); const library_src = sema.maybeOptionsSrc(block, src, "library"); const linkage_src = sema.maybeOptionsSrc(block, src, "linkage"); const thread_local_src = sema.maybeOptionsSrc(block, src, "thread_local"); - const name_ref = try sema.fieldVal(block, src, options, "name", name_src); + const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); const name_val = try sema.resolveConstValue(block, name_src, name_ref, "name of the extern symbol must be comptime-known"); - const name = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, mod); + const name = try name_val.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod); - const library_name_inst = try sema.fieldVal(block, src, options, "library_name", library_src); + const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "library_name"), library_src); const library_name_val = try sema.resolveConstValue(block, library_src, library_name_inst, "library in which extern symbol is must be comptime-known"); - const linkage_ref = try sema.fieldVal(block, src, options, "linkage", linkage_src); + const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src); const linkage_val = try sema.resolveConstValue(block, linkage_src, linkage_ref, "linkage of the extern symbol must be comptime-known"); - const linkage = linkage_val.toEnum(std.builtin.GlobalLinkage); + const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val); - const is_thread_local = try sema.fieldVal(block, src, options, "is_thread_local", thread_local_src); + const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "is_thread_local"), thread_local_src); const is_thread_local_val = try sema.resolveConstValue(block, thread_local_src, is_thread_local, "threadlocality of the extern symbol must be comptime-known"); - const library_name = if (!library_name_val.isNull()) blk: { - const payload = library_name_val.castTag(.opt_payload).?.data; - const library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, mod); + const library_name = if (library_name_val.optionalValue(mod)) |payload| blk: { + const library_name = try payload.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod); if (library_name.len == 0) { - return sema.fail(block, library_src, "library name name cannot be empty", .{}); + return sema.fail(block, library_src, "library name cannot be empty", .{}); } break :blk try sema.handleExternLibName(block, library_src, library_name); } else null; @@ -22591,9 +24009,9 @@ fn resolveExternOptions( return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{}); } - return std.builtin.ExternOptions{ - .name = name, - .library_name = library_name, + return .{ + .name = try ip.getOrPutString(gpa, name), + .library_name = try ip.getOrPutStringOpt(gpa, library_name), .linkage = linkage, .is_thread_local = is_thread_local_val.toBool(), }; @@ -22604,14 +24022,25 @@ fn zirBuiltinExtern( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; var ty = try sema.resolveType(block, ty_src, extra.lhs); - if (!ty.isPtrAtRuntime()) { + if (!ty.isPtrAtRuntime(mod)) { return sema.fail(block, ty_src, "expected (optional) pointer", .{}); } + if (!try sema.validateExternType(ty.childType(mod), .other)) { + const msg = msg: { + const msg = try sema.errMsg(block, ty_src, "extern symbol cannot have type '{}'", .{ty.fmt(mod)}); + errdefer msg.destroy(sema.gpa); + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), ty, .other); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } const options = sema.resolveExternOptions(block, .unneeded, extra.rhs) catch |err| switch (err) { error.NeededSourceLocation => { @@ -22621,52 +24050,51 @@ fn zirBuiltinExtern( else => |e| return e, }; - if (options.linkage == .Weak and !ty.ptrAllowsZero()) { - ty = try Type.optional(sema.arena, ty); + if (options.linkage == .Weak and !ty.ptrAllowsZero(mod)) { + ty = try Type.optional(sema.arena, ty, mod); } // TODO check duplicate extern - const new_decl_index = try sema.mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null); - errdefer sema.mod.destroyDecl(new_decl_index); - const new_decl = sema.mod.declPtr(new_decl_index); - new_decl.name = try sema.gpa.dupeZ(u8, options.name); + const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null); + errdefer mod.destroyDecl(new_decl_index); + const new_decl = mod.declPtr(new_decl_index); + new_decl.name = options.name; { - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); - - const new_var = try new_decl_arena_allocator.create(Module.Var); - new_var.* = .{ - .owner_decl = sema.owner_decl_index, - .init = Value.initTag(.unreachable_value), + const new_var = try mod.intern(.{ .variable = .{ + .ty = ty.toIntern(), + .init = .none, + .decl = sema.owner_decl_index, .is_extern = true, - .is_mutable = false, + .is_const = true, .is_threadlocal = options.is_thread_local, .is_weak_linkage = options.linkage == .Weak, - .lib_name = null, - }; + } }); new_decl.src_line = sema.owner_decl.src_line; // We only access this decl through the decl_ref with the correct type created // below, so this type doesn't matter - new_decl.ty = Type.Tag.init(.anyopaque); - new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var); + new_decl.ty = ty; + new_decl.val = new_var.toValue(); new_decl.@"align" = 0; - new_decl.@"linksection" = null; + new_decl.@"linksection" = .none; new_decl.has_tv = true; new_decl.analysis = .complete; - new_decl.generation = sema.mod.generation; - - try new_decl.finalizeNewArena(&new_decl_arena); + new_decl.generation = mod.generation; } - try sema.mod.declareDeclDependency(sema.owner_decl_index, new_decl_index); + try mod.declareDeclDependency(sema.owner_decl_index, new_decl_index); try sema.ensureDeclAnalyzed(new_decl_index); - const ref = try Value.Tag.decl_ref.create(sema.arena, new_decl_index); - return sema.addConstant(ty, ref); + return sema.addConstant(ty, try mod.getCoerced((try mod.intern(.{ .ptr = .{ + .ty = switch (mod.intern_pool.indexToKey(ty.toIntern())) { + .ptr_type => ty.toIntern(), + .opt_type => |child_type| child_type, + else => unreachable, + }, + .addr = .{ .decl = new_decl_index }, + } })).toValue(), ty)); } fn zirWorkItem( @@ -22743,17 +24171,27 @@ fn validateVarType( var_ty: Type, is_extern: bool, ) CompileError!void { - if (try sema.validateRunTimeType(var_ty, is_extern)) return; - const mod = sema.mod; + if (is_extern and !try sema.validateExternType(var_ty, .other)) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)}); + errdefer msg.destroy(sema.gpa); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), var_ty, .other); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + + if (try sema.validateRunTimeType(var_ty, is_extern)) return; const msg = msg: { const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); const src_decl = mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(src_decl), var_ty); - if (var_ty.zigTypeTag() == .ComptimeInt or var_ty.zigTypeTag() == .ComptimeFloat) { + try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), var_ty); + if (var_ty.zigTypeTag(mod) == .ComptimeInt or var_ty.zigTypeTag(mod) == .ComptimeFloat) { try sema.errNote(block, src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{}); } @@ -22767,8 +24205,9 @@ fn validateRunTimeType( var_ty: Type, is_extern: bool, ) CompileError!bool { + const mod = sema.mod; var ty = var_ty; - while (true) switch (ty.zigTypeTag()) { + while (true) switch (ty.zigTypeTag(mod)) { .Bool, .Int, .Float, @@ -22791,23 +24230,22 @@ fn validateRunTimeType( => return false, .Pointer => { - const elem_ty = ty.childType(); - switch (elem_ty.zigTypeTag()) { + const elem_ty = ty.childType(mod); + switch (elem_ty.zigTypeTag(mod)) { .Opaque => return true, - .Fn => return elem_ty.isFnOrHasRuntimeBits(), + .Fn => return elem_ty.isFnOrHasRuntimeBits(mod), else => ty = elem_ty, } }, .Opaque => return is_extern, .Optional => { - var buf: Type.Payload.ElemType = undefined; - const child_ty = ty.optionalChild(&buf); + const child_ty = ty.optionalChild(mod); return sema.validateRunTimeType(child_ty, is_extern); }, - .Array, .Vector => ty = ty.elemType(), + .Array, .Vector => ty = ty.childType(mod), - .ErrorUnion => ty = ty.errorUnionPayload(), + .ErrorUnion => ty = ty.errorUnionPayload(mod), .Struct, .Union => { const resolved_ty = try sema.resolveTypeFields(ty); @@ -22817,12 +24255,10 @@ fn validateRunTimeType( }; } -const TypeSet = std.HashMapUnmanaged(Type, void, Type.HashContext64, std.hash_map.default_max_load_percentage); +const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void); fn explainWhyTypeIsComptime( sema: *Sema, - block: *Block, - src: LazySrcLoc, msg: *Module.ErrorMsg, src_loc: Module.SrcLoc, ty: Type, @@ -22831,20 +24267,18 @@ fn explainWhyTypeIsComptime( defer type_set.deinit(sema.gpa); try sema.resolveTypeFully(ty); - return sema.explainWhyTypeIsComptimeInner(block, src, msg, src_loc, ty, &type_set); + return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set); } fn explainWhyTypeIsComptimeInner( sema: *Sema, - block: *Block, - src: LazySrcLoc, msg: *Module.ErrorMsg, src_loc: Module.SrcLoc, ty: Type, type_set: *TypeSet, ) CompileError!void { const mod = sema.mod; - switch (ty.zigTypeTag()) { + switch (ty.zigTypeTag(mod)) { .Bool, .Int, .Float, @@ -22878,12 +24312,12 @@ fn explainWhyTypeIsComptimeInner( }, .Array, .Vector => { - try sema.explainWhyTypeIsComptimeInner(block, src, msg, src_loc, ty.elemType(), type_set); + try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set); }, .Pointer => { - const elem_ty = ty.elemType2(); - if (elem_ty.zigTypeTag() == .Fn) { - const fn_info = elem_ty.fnInfo(); + const elem_ty = ty.elemType2(mod); + if (elem_ty.zigTypeTag(mod) == .Fn) { + const fn_info = mod.typeToFunc(elem_ty).?; if (fn_info.is_generic) { try mod.errNoteNonLazy(src_loc, msg, "function is generic", .{}); } @@ -22891,36 +24325,34 @@ fn explainWhyTypeIsComptimeInner( .Inline => try mod.errNoteNonLazy(src_loc, msg, "function has inline calling convention", .{}), else => {}, } - if (fn_info.return_type.comptimeOnly()) { + if (fn_info.return_type.toType().comptimeOnly(mod)) { try mod.errNoteNonLazy(src_loc, msg, "function has a comptime-only return type", .{}); } return; } - try sema.explainWhyTypeIsComptimeInner(block, src, msg, src_loc, ty.elemType(), type_set); + try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set); }, .Optional => { - var buf: Type.Payload.ElemType = undefined; - try sema.explainWhyTypeIsComptimeInner(block, src, msg, src_loc, ty.optionalChild(&buf), type_set); + try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(mod), type_set); }, .ErrorUnion => { - try sema.explainWhyTypeIsComptimeInner(block, src, msg, src_loc, ty.errorUnionPayload(), type_set); + try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(mod), type_set); }, .Struct => { - if ((try type_set.getOrPutContext(sema.gpa, ty, .{ .mod = mod })).found_existing) return; + if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; - if (ty.castTag(.@"struct")) |payload| { - const struct_obj = payload.data; + if (mod.typeToStruct(ty)) |struct_obj| { for (struct_obj.fields.values(), 0..) |field, i| { - const field_src_loc = struct_obj.fieldSrcLoc(sema.mod, .{ + const field_src_loc = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = i, .range = .type, }); if (try sema.typeRequiresComptime(field.ty)) { try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{}); - try sema.explainWhyTypeIsComptimeInner(block, src, msg, field_src_loc, field.ty, type_set); + try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field.ty, type_set); } } } @@ -22928,19 +24360,18 @@ fn explainWhyTypeIsComptimeInner( }, .Union => { - if ((try type_set.getOrPutContext(sema.gpa, ty, .{ .mod = mod })).found_existing) return; + if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; - if (ty.cast(Type.Payload.Union)) |payload| { - const union_obj = payload.data; + if (mod.typeToUnion(ty)) |union_obj| { for (union_obj.fields.values(), 0..) |field, i| { - const field_src_loc = union_obj.fieldSrcLoc(sema.mod, .{ + const field_src_loc = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = i, .range = .type, }); if (try sema.typeRequiresComptime(field.ty)) { try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{}); - try sema.explainWhyTypeIsComptimeInner(block, src, msg, field_src_loc, field.ty, type_set); + try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field.ty, type_set); } } } @@ -22965,7 +24396,8 @@ fn validateExternType( ty: Type, position: ExternPosition, ) !bool { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .Type, .ComptimeFloat, .ComptimeInt, @@ -22983,8 +24415,8 @@ fn validateExternType( .Float, .AnyFrame, => return true, - .Pointer => return !ty.isSlice(), - .Int => switch (ty.intInfo(sema.mod.getTarget()).bits) { + .Pointer => return !(ty.isSlice(mod) or try sema.typeRequiresComptime(ty)), + .Int => switch (ty.intInfo(mod).bits) { 8, 16, 32, 64, 128 => return true, else => return false, }, @@ -22993,20 +24425,18 @@ fn validateExternType( const target = sema.mod.getTarget(); // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI. // The goal is to experiment with more integrated CPU/GPU code. - if (ty.fnCallingConvention() == .Kernel and (target.cpu.arch == .nvptx or target.cpu.arch == .nvptx64)) { + if (ty.fnCallingConvention(mod) == .Kernel and (target.cpu.arch == .nvptx or target.cpu.arch == .nvptx64)) { return true; } - return !Type.fnCallingConventionAllowsZigTypes(target, ty.fnCallingConvention()); + return !target_util.fnCallConvAllowsZigTypes(target, ty.fnCallingConvention(mod)); }, .Enum => { - var buf: Type.Payload.Bits = undefined; - return sema.validateExternType(ty.intTagType(&buf), position); + return sema.validateExternType(ty.intTagType(mod), position); }, - .Struct, .Union => switch (ty.containerLayout()) { + .Struct, .Union => switch (ty.containerLayout(mod)) { .Extern => return true, .Packed => { - const target = sema.mod.getTarget(); - const bit_size = try ty.bitSizeAdvanced(target, sema); + const bit_size = try ty.bitSizeAdvanced(mod, sema); switch (bit_size) { 8, 16, 32, 64, 128 => return true, else => return false, @@ -23016,10 +24446,10 @@ fn validateExternType( }, .Array => { if (position == .ret_ty or position == .param_ty) return false; - return sema.validateExternType(ty.elemType2(), .element); + return sema.validateExternType(ty.elemType2(mod), .element); }, - .Vector => return sema.validateExternType(ty.elemType2(), .element), - .Optional => return ty.isPtrLikeOptional(), + .Vector => return sema.validateExternType(ty.elemType2(mod), .element), + .Optional => return ty.isPtrLikeOptional(mod), } } @@ -23031,7 +24461,7 @@ fn explainWhyTypeIsNotExtern( position: ExternPosition, ) CompileError!void { const mod = sema.mod; - switch (ty.zigTypeTag()) { + switch (ty.zigTypeTag(mod)) { .Opaque, .Bool, .Float, @@ -23049,13 +24479,21 @@ fn explainWhyTypeIsNotExtern( .Frame, => return, - .Pointer => try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{}), + .Pointer => { + if (ty.isSlice(mod)) { + try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); + } else { + const pointee_ty = ty.childType(mod); + try mod.errNoteNonLazy(src_loc, msg, "pointer to comptime-only type '{}'", .{pointee_ty.fmt(sema.mod)}); + try sema.explainWhyTypeIsComptime(msg, src_loc, pointee_ty); + } + }, .Void => try mod.errNoteNonLazy(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}), .NoReturn => try mod.errNoteNonLazy(src_loc, msg, "'noreturn' is only allowed as a return type", .{}), - .Int => if (ty.intInfo(sema.mod.getTarget()).bits > 128) { - try mod.errNoteNonLazy(src_loc, msg, "only integers with less than 128 bits are extern compatible", .{}); - } else { + .Int => if (!std.math.isPowerOfTwo(ty.intInfo(mod).bits)) { try mod.errNoteNonLazy(src_loc, msg, "only integers with power of two bits are extern compatible", .{}); + } else { + try mod.errNoteNonLazy(src_loc, msg, "only integers with 8, 16, 32, 64 and 128 bits are extern compatible", .{}); }, .Fn => { if (position != .other) { @@ -23063,7 +24501,7 @@ fn explainWhyTypeIsNotExtern( try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{}); return; } - switch (ty.fnCallingConvention()) { + switch (ty.fnCallingConvention(mod)) { .Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}), .Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}), .Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}), @@ -23071,8 +24509,7 @@ fn explainWhyTypeIsNotExtern( } }, .Enum => { - var buf: Type.Payload.Bits = undefined; - const tag_ty = ty.intTagType(&buf); + const tag_ty = ty.intTagType(mod); try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)}); try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position); }, @@ -23084,17 +24521,17 @@ fn explainWhyTypeIsNotExtern( } else if (position == .param_ty) { return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{}); } - try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), .element); + try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element); }, - .Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), .element), + .Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element), .Optional => try mod.errNoteNonLazy(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. -fn validatePackedType(ty: Type) bool { - switch (ty.zigTypeTag()) { +fn validatePackedType(ty: Type, mod: *Module) bool { + switch (ty.zigTypeTag(mod)) { .Type, .ComptimeFloat, .ComptimeInt, @@ -23110,7 +24547,7 @@ fn validatePackedType(ty: Type) bool { .Fn, .Array, => return false, - .Optional => return ty.isPtrLikeOptional(), + .Optional => return ty.isPtrLikeOptional(mod), .Void, .Bool, .Float, @@ -23118,8 +24555,8 @@ fn validatePackedType(ty: Type) bool { .Vector, .Enum, => return true, - .Pointer => return !ty.isSlice(), - .Struct, .Union => return ty.containerLayout() == .Packed, + .Pointer => return !ty.isSlice(mod), + .Struct, .Union => return ty.containerLayout(mod) == .Packed, } } @@ -23130,7 +24567,7 @@ fn explainWhyTypeIsNotPacked( ty: Type, ) CompileError!void { const mod = sema.mod; - switch (ty.zigTypeTag()) { + switch (ty.zigTypeTag(mod)) { .Void, .Bool, .Float, @@ -23188,6 +24625,7 @@ pub const PanicId = enum { for_len_mismatch, memcpy_len_mismatch, memcpy_alias, + noreturn_returned, }; fn addSafetyCheck( @@ -23274,31 +24712,36 @@ fn addSafetyCheckExtra( fn panicWithMsg( sema: *Sema, block: *Block, - src: LazySrcLoc, msg_inst: Air.Inst.Ref, ) !void { const mod = sema.mod; - const arena = sema.arena; if (!mod.backendSupportsFeature(.panic_fn)) { - _ = try block.addNoOp(.breakpoint); - _ = try block.addNoOp(.unreach); + _ = try block.addNoOp(.trap); return; } const panic_fn = try sema.getBuiltin("panic"); const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); const target = mod.getTarget(); - const ptr_stack_trace_ty = try Type.ptr(arena, mod, .{ - .pointee_type = stack_trace_ty, - .@"addrspace" = target_util.defaultAddressSpace(target, .global_constant), // TODO might need a place that is more dynamic + const ptr_stack_trace_ty = try mod.ptrType(.{ + .child = stack_trace_ty.toIntern(), + .flags = .{ + .address_space = target_util.defaultAddressSpace(target, .global_constant), // TODO might need a place that is more dynamic + }, }); - const null_stack_trace = try sema.addConstant( - try Type.optional(arena, ptr_stack_trace_ty), - Value.null, - ); - const args: [3]Air.Inst.Ref = .{ msg_inst, null_stack_trace, .null_value }; - _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null); + const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern()); + const null_stack_trace = try sema.addConstant(opt_ptr_stack_trace_ty, (try mod.intern(.{ .opt = .{ + .ty = opt_ptr_stack_trace_ty.toIntern(), + .val = .none, + } })).toValue()); + + const opt_usize_ty = try mod.optionalType(.usize_type); + const null_ret_addr = try sema.addConstant(opt_usize_ty, (try mod.intern(.{ .opt = .{ + .ty = opt_usize_ty.toIntern(), + .val = .none, + } })).toValue()); + try sema.callBuiltin(block, panic_fn, .auto, &.{ msg_inst, null_stack_trace, null_ret_addr }); } fn panicUnwrapError( @@ -23330,14 +24773,13 @@ fn panicUnwrapError( { if (!sema.mod.backendSupportsFeature(.panic_unwrap_error)) { - _ = try fail_block.addNoOp(.breakpoint); - _ = try fail_block.addNoOp(.unreach); + _ = try fail_block.addNoOp(.trap); } else { const panic_fn = try sema.getBuiltin("panicUnwrapError"); const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand); const err_return_trace = try sema.getErrorReturnTrace(&fail_block); const args: [2]Air.Inst.Ref = .{ err_return_trace, err }; - _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, &args, null); + try sema.callBuiltin(&fail_block, panic_fn, .auto, &args); } } try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); @@ -23358,20 +24800,6 @@ fn panicIndexOutOfBounds( try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len }); } -fn panicStartLargerThanEnd( - sema: *Sema, - parent_block: *Block, - start: Air.Inst.Ref, - end: Air.Inst.Ref, -) !void { - assert(!parent_block.is_comptime); - const ok = try parent_block.addBinOp(.cmp_lte, start, end); - if (!sema.mod.comp.formatted_panics) { - return sema.addSafetyCheck(parent_block, ok, .start_index_greater_than_end); - } - try sema.safetyCheckFormatted(parent_block, ok, "panicStartGreaterThanEnd", &.{ start, end }); -} - fn panicInactiveUnionField( sema: *Sema, parent_block: *Block, @@ -23395,11 +24823,12 @@ fn panicSentinelMismatch( sentinel_index: Air.Inst.Ref, ) !void { assert(!parent_block.is_comptime); + const mod = sema.mod; const expected_sentinel_val = maybe_sentinel orelse return; const expected_sentinel = try sema.addConstant(sentinel_ty, expected_sentinel_val); const ptr_ty = sema.typeOf(ptr); - const actual_sentinel = if (ptr_ty.isSlice()) + const actual_sentinel = if (ptr_ty.isSlice(mod)) try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index) else blk: { const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null); @@ -23407,7 +24836,7 @@ fn panicSentinelMismatch( break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr); }; - const ok = if (sentinel_ty.zigTypeTag() == .Vector) ok: { + const ok = if (sentinel_ty.zigTypeTag(mod) == .Vector) ok: { const eql = try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq); break :ok try parent_block.addInst(.{ @@ -23417,12 +24846,12 @@ fn panicSentinelMismatch( .operation = .And, } }, }); - } else if (sentinel_ty.isSelfComparable(true)) + } else if (sentinel_ty.isSelfComparable(mod, true)) try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel) else { const panic_fn = try sema.getBuiltin("checkNonScalarSentinel"); const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel }; - _ = try sema.analyzeCall(parent_block, panic_fn, sema.src, sema.src, .auto, false, &args, null); + try sema.callBuiltin(parent_block, panic_fn, .auto, &args); return; }; @@ -23456,11 +24885,10 @@ fn safetyCheckFormatted( defer fail_block.instructions.deinit(gpa); if (!sema.mod.backendSupportsFeature(.safety_check_formatted)) { - _ = try fail_block.addNoOp(.breakpoint); - _ = try fail_block.addNoOp(.unreach); + _ = try fail_block.addNoOp(.trap); } else { const panic_fn = try sema.getBuiltin(func); - _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, args, null); + try sema.callBuiltin(&fail_block, panic_fn, .auto, args); } try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); } @@ -23470,16 +24898,18 @@ fn safetyPanic( block: *Block, panic_id: PanicId, ) CompileError!void { + const mod = sema.mod; + const gpa = sema.gpa; const panic_messages_ty = try sema.getBuiltinType("panic_messages"); const msg_decl_index = (try sema.namespaceLookup( block, sema.src, - panic_messages_ty.getNamespace().?, - @tagName(panic_id), + panic_messages_ty.getNamespaceIndex(mod).unwrap().?, + try mod.intern_pool.getOrPutString(gpa, @tagName(panic_id)), )).?; const msg_inst = try sema.analyzeDeclVal(block, sema.src, msg_decl_index); - try sema.panicWithMsg(block, sema.src, msg_inst); + try sema.panicWithMsg(block, msg_inst); } fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { @@ -23507,37 +24937,38 @@ fn fieldVal( block: *Block, src: LazySrcLoc, object: Air.Inst.Ref, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { // When editing this function, note that there is corresponding logic to be edited // in `fieldPtr`. This function takes a value and returns a value. - const arena = sema.arena; + const mod = sema.mod; + const ip = &mod.intern_pool; const object_src = src; // TODO better source location const object_ty = sema.typeOf(object); // Zig allows dereferencing a single pointer during field lookup. Note that // we don't actually need to generate the dereference some field lookups, like the // length of arrays and other comptime operations. - const is_pointer_to = object_ty.isSinglePointer(); + const is_pointer_to = object_ty.isSinglePointer(mod); const inner_ty = if (is_pointer_to) - object_ty.childType() + object_ty.childType(mod) else object_ty; - switch (inner_ty.zigTypeTag()) { + switch (inner_ty.zigTypeTag(mod)) { .Array => { - if (mem.eql(u8, field_name, "len")) { + if (ip.stringEqlSlice(field_name, "len")) { return sema.addConstant( Type.usize, - try Value.Tag.int_u64.create(arena, inner_ty.arrayLen()), + try mod.intValue(Type.usize, inner_ty.arrayLen(mod)), ); - } else if (mem.eql(u8, field_name, "ptr") and is_pointer_to) { - const ptr_info = object_ty.ptrInfo().data; - const result_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = ptr_info.pointee_type.childType(), + } else if (ip.stringEqlSlice(field_name, "ptr") and is_pointer_to) { + const ptr_info = object_ty.ptrInfo(mod); + const result_ty = try Type.ptr(sema.arena, mod, .{ + .pointee_type = ptr_info.pointee_type.childType(mod), .sentinel = ptr_info.sentinel, .@"align" = ptr_info.@"align", .@"addrspace" = ptr_info.@"addrspace", @@ -23554,21 +24985,21 @@ fn fieldVal( return sema.fail( block, field_name_src, - "no member named '{s}' in '{}'", - .{ field_name, object_ty.fmt(sema.mod) }, + "no member named '{}' in '{}'", + .{ field_name.fmt(ip), object_ty.fmt(mod) }, ); } }, .Pointer => { - const ptr_info = inner_ty.ptrInfo().data; + const ptr_info = inner_ty.ptrInfo(mod); if (ptr_info.size == .Slice) { - if (mem.eql(u8, field_name, "ptr")) { + if (ip.stringEqlSlice(field_name, "ptr")) { const slice = if (is_pointer_to) try sema.analyzeLoad(block, src, object, object_src) else object; return sema.analyzeSlicePtr(block, object_src, slice, inner_ty); - } else if (mem.eql(u8, field_name, "len")) { + } else if (ip.stringEqlSlice(field_name, "len")) { const slice = if (is_pointer_to) try sema.analyzeLoad(block, src, object, object_src) else @@ -23578,8 +25009,8 @@ fn fieldVal( return sema.fail( block, field_name_src, - "no member named '{s}' in '{}'", - .{ field_name, object_ty.fmt(sema.mod) }, + "no member named '{}' in '{}'", + .{ field_name.fmt(ip), object_ty.fmt(mod) }, ); } } @@ -23591,66 +25022,74 @@ fn fieldVal( object; const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?; - var to_type_buffer: Value.ToTypeBuffer = undefined; - const child_type = val.toType(&to_type_buffer); + const child_type = val.toType(); - switch (try child_type.zigTypeTagOrPoison()) { + switch (try child_type.zigTypeTagOrPoison(mod)) { .ErrorSet => { - const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: { - if (payload.data.names.getEntry(field_name)) |entry| { - break :blk entry.key_ptr.*; - } - const msg = msg: { - const msg = try sema.errMsg(block, src, "no error named '{s}' in '{}'", .{ - field_name, child_type.fmt(sema.mod), - }); - errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, child_type); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(msg); - } else (try sema.mod.getErrorValue(field_name)).key; + switch (ip.indexToKey(child_type.toIntern())) { + .error_set_type => |error_set_type| blk: { + if (error_set_type.nameIndex(ip, field_name) != null) break :blk; + const msg = msg: { + const msg = try sema.errMsg(block, src, "no error named '{}' in '{}'", .{ + field_name.fmt(ip), child_type.fmt(mod), + }); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, child_type); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + }, + .inferred_error_set_type => { + return sema.fail(block, src, "TODO handle inferred error sets here", .{}); + }, + .simple_type => |t| { + assert(t == .anyerror); + _ = try mod.getErrorValue(field_name); + }, + else => unreachable, + } - return sema.addConstant( - if (!child_type.isAnyError()) - try child_type.copy(arena) - else - try Type.Tag.error_set_single.create(arena, name), - try Value.Tag.@"error".create(arena, .{ .name = name }), - ); + const error_set_type = if (!child_type.isAnyError(mod)) + child_type + else + try mod.singleErrorSetType(field_name); + return sema.addConstant(error_set_type, (try mod.intern(.{ .err = .{ + .ty = error_set_type.toIntern(), + .name = field_name, + } })).toValue()); }, .Union => { - if (child_type.getNamespace()) |namespace| { + if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { return inst; } } const union_ty = try sema.resolveTypeFields(child_type); - if (union_ty.unionTagType()) |enum_ty| { - if (enum_ty.enumFieldIndex(field_name)) |field_index_usize| { + if (union_ty.unionTagType(mod)) |enum_ty| { + if (enum_ty.enumFieldIndex(field_name, mod)) |field_index_usize| { const field_index = @intCast(u32, field_index_usize); return sema.addConstant( enum_ty, - try Value.Tag.enum_field_index.create(sema.arena, field_index), + try mod.enumValueFieldIndex(enum_ty, field_index), ); } } return sema.failWithBadMemberAccess(block, union_ty, field_name_src, field_name); }, .Enum => { - if (child_type.getNamespace()) |namespace| { + if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { return inst; } } - const field_index_usize = child_type.enumFieldIndex(field_name) orelse + const field_index_usize = child_type.enumFieldIndex(field_name, mod) orelse return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); const field_index = @intCast(u32, field_index_usize); - const enum_val = try Value.Tag.enum_field_index.create(arena, field_index); - return sema.addConstant(try child_type.copy(arena), enum_val); + const enum_val = try mod.enumValueFieldIndex(child_type, field_index); + return sema.addConstant(child_type, enum_val); }, .Struct, .Opaque => { - if (child_type.getNamespace()) |namespace| { + if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { return inst; } @@ -23659,10 +25098,10 @@ fn fieldVal( }, else => { const msg = msg: { - const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)}); + const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(mod)}); errdefer msg.destroy(sema.gpa); - if (child_type.isSlice()) try sema.errNote(block, src, msg, "slice values have 'len' and 'ptr' members", .{}); - if (child_type.zigTypeTag() == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{}); + if (child_type.isSlice(mod)) try sema.errNote(block, src, msg, "slice values have 'len' and 'ptr' members", .{}); + if (child_type.zigTypeTag(mod) == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -23693,50 +25132,52 @@ fn fieldPtr( block: *Block, src: LazySrcLoc, object_ptr: Air.Inst.Ref, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, initializing: bool, ) CompileError!Air.Inst.Ref { // When editing this function, note that there is corresponding logic to be edited // in `fieldVal`. This function takes a pointer and returns a pointer. + const mod = sema.mod; + const ip = &mod.intern_pool; const object_ptr_src = src; // TODO better source location const object_ptr_ty = sema.typeOf(object_ptr); - const object_ty = switch (object_ptr_ty.zigTypeTag()) { - .Pointer => object_ptr_ty.elemType(), - else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(sema.mod)}), + const object_ty = switch (object_ptr_ty.zigTypeTag(mod)) { + .Pointer => object_ptr_ty.childType(mod), + else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(mod)}), }; // Zig allows dereferencing a single pointer during field lookup. Note that // we don't actually need to generate the dereference some field lookups, like the // length of arrays and other comptime operations. - const is_pointer_to = object_ty.isSinglePointer(); + const is_pointer_to = object_ty.isSinglePointer(mod); const inner_ty = if (is_pointer_to) - object_ty.childType() + object_ty.childType(mod) else object_ty; - switch (inner_ty.zigTypeTag()) { + switch (inner_ty.zigTypeTag(mod)) { .Array => { - if (mem.eql(u8, field_name, "len")) { + if (ip.stringEqlSlice(field_name, "len")) { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); return sema.analyzeDeclRef(try anon_decl.finish( Type.usize, - try Value.Tag.int_u64.create(anon_decl.arena(), inner_ty.arrayLen()), + try mod.intValue(Type.usize, inner_ty.arrayLen(mod)), 0, // default alignment )); } else { return sema.fail( block, field_name_src, - "no member named '{s}' in '{}'", - .{ field_name, object_ty.fmt(sema.mod) }, + "no member named '{}' in '{}'", + .{ field_name.fmt(ip), object_ty.fmt(mod) }, ); } }, - .Pointer => if (inner_ty.isSlice()) { + .Pointer => if (inner_ty.isSlice(mod)) { const inner_ptr = if (is_pointer_to) try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) else @@ -23744,47 +25185,44 @@ fn fieldPtr( const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty; - if (mem.eql(u8, field_name, "ptr")) { - const buf = try sema.arena.create(Type.SlicePtrFieldTypeBuffer); - const slice_ptr_ty = inner_ty.slicePtrFieldType(buf); + if (ip.stringEqlSlice(field_name, "ptr")) { + const slice_ptr_ty = inner_ty.slicePtrFieldType(mod); - const result_ty = try Type.ptr(sema.arena, sema.mod, .{ + const result_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = slice_ptr_ty, - .mutable = attr_ptr_ty.ptrIsMutable(), - .@"volatile" = attr_ptr_ty.isVolatilePtr(), - .@"addrspace" = attr_ptr_ty.ptrAddressSpace(), + .mutable = attr_ptr_ty.ptrIsMutable(mod), + .@"volatile" = attr_ptr_ty.isVolatilePtr(mod), + .@"addrspace" = attr_ptr_ty.ptrAddressSpace(mod), }); if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { - return sema.addConstant( - result_ty, - try Value.Tag.field_ptr.create(sema.arena, .{ - .container_ptr = val, - .container_ty = inner_ty, - .field_index = Value.Payload.Slice.ptr_index, - }), - ); + return sema.addConstant(result_ty, (try mod.intern(.{ .ptr = .{ + .ty = result_ty.toIntern(), + .addr = .{ .field = .{ + .base = val.toIntern(), + .index = Value.slice_ptr_index, + } }, + } })).toValue()); } try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); - } else if (mem.eql(u8, field_name, "len")) { - const result_ty = try Type.ptr(sema.arena, sema.mod, .{ + } else if (ip.stringEqlSlice(field_name, "len")) { + const result_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = Type.usize, - .mutable = attr_ptr_ty.ptrIsMutable(), - .@"volatile" = attr_ptr_ty.isVolatilePtr(), - .@"addrspace" = attr_ptr_ty.ptrAddressSpace(), + .mutable = attr_ptr_ty.ptrIsMutable(mod), + .@"volatile" = attr_ptr_ty.isVolatilePtr(mod), + .@"addrspace" = attr_ptr_ty.ptrAddressSpace(mod), }); if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { - return sema.addConstant( - result_ty, - try Value.Tag.field_ptr.create(sema.arena, .{ - .container_ptr = val, - .container_ty = inner_ty, - .field_index = Value.Payload.Slice.len_index, - }), - ); + return sema.addConstant(result_ty, (try mod.intern(.{ .ptr = .{ + .ty = result_ty.toIntern(), + .addr = .{ .field = .{ + .base = val.toIntern(), + .index = Value.slice_len_index, + } }, + } })).toValue()); } try sema.requireRuntimeBlock(block, src, null); @@ -23793,8 +25231,8 @@ fn fieldPtr( return sema.fail( block, field_name_src, - "no member named '{s}' in '{}'", - .{ field_name, object_ty.fmt(sema.mod) }, + "no member named '{}' in '{}'", + .{ field_name.fmt(ip), object_ty.fmt(mod) }, ); } }, @@ -23807,47 +25245,59 @@ fn fieldPtr( result; const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?; - var to_type_buffer: Value.ToTypeBuffer = undefined; - const child_type = val.toType(&to_type_buffer); + const child_type = val.toType(); - switch (child_type.zigTypeTag()) { + switch (child_type.zigTypeTag(mod)) { .ErrorSet => { - // TODO resolve inferred error sets - const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: { - if (payload.data.names.getEntry(field_name)) |entry| { - break :blk entry.key_ptr.*; - } - return sema.fail(block, src, "no error named '{s}' in '{}'", .{ - field_name, child_type.fmt(sema.mod), - }); - } else (try sema.mod.getErrorValue(field_name)).key; + switch (ip.indexToKey(child_type.toIntern())) { + .error_set_type => |error_set_type| blk: { + if (error_set_type.nameIndex(ip, field_name) != null) { + break :blk; + } + return sema.fail(block, src, "no error named '{}' in '{}'", .{ + field_name.fmt(ip), child_type.fmt(mod), + }); + }, + .inferred_error_set_type => { + return sema.fail(block, src, "TODO handle inferred error sets here", .{}); + }, + .simple_type => |t| { + assert(t == .anyerror); + _ = try mod.getErrorValue(field_name); + }, + else => unreachable, + } var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); + const error_set_type = if (!child_type.isAnyError(mod)) + child_type + else + try mod.singleErrorSetType(field_name); return sema.analyzeDeclRef(try anon_decl.finish( - if (!child_type.isAnyError()) - try child_type.copy(anon_decl.arena()) - else - try Type.Tag.error_set_single.create(anon_decl.arena(), name), - try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }), + error_set_type, + (try mod.intern(.{ .err = .{ + .ty = error_set_type.toIntern(), + .name = field_name, + } })).toValue(), 0, // default alignment )); }, .Union => { - if (child_type.getNamespace()) |namespace| { + if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { return inst; } } const union_ty = try sema.resolveTypeFields(child_type); - if (union_ty.unionTagType()) |enum_ty| { - if (enum_ty.enumFieldIndex(field_name)) |field_index| { + if (union_ty.unionTagType(mod)) |enum_ty| { + if (enum_ty.enumFieldIndex(field_name, mod)) |field_index| { const field_index_u32 = @intCast(u32, field_index); var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); return sema.analyzeDeclRef(try anon_decl.finish( - try enum_ty.copy(anon_decl.arena()), - try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32), + enum_ty, + try mod.enumValueFieldIndex(enum_ty, field_index_u32), 0, // default alignment )); } @@ -23855,32 +25305,32 @@ fn fieldPtr( return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); }, .Enum => { - if (child_type.getNamespace()) |namespace| { + if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { return inst; } } - const field_index = child_type.enumFieldIndex(field_name) orelse { + const field_index = child_type.enumFieldIndex(field_name, mod) orelse { return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); }; const field_index_u32 = @intCast(u32, field_index); var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); return sema.analyzeDeclRef(try anon_decl.finish( - try child_type.copy(anon_decl.arena()), - try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32), + child_type, + try mod.enumValueFieldIndex(child_type, field_index_u32), 0, // default alignment )); }, .Struct, .Opaque => { - if (child_type.getNamespace()) |namespace| { + if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { return inst; } } return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); }, - else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)}), + else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(mod)}), } }, .Struct => { @@ -23902,66 +25352,77 @@ fn fieldPtr( return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); } +const ResolvedFieldCallee = union(enum) { + /// The LHS of the call was an actual field with this value. + direct: Air.Inst.Ref, + /// This is a method call, with the function and first argument given. + method: struct { + func_inst: Air.Inst.Ref, + arg0_inst: Air.Inst.Ref, + }, +}; + fn fieldCallBind( sema: *Sema, block: *Block, src: LazySrcLoc, raw_ptr: Air.Inst.Ref, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, -) CompileError!Air.Inst.Ref { +) CompileError!ResolvedFieldCallee { // When editing this function, note that there is corresponding logic to be edited // in `fieldVal`. This function takes a pointer and returns a pointer. + const mod = sema.mod; + const ip = &mod.intern_pool; const raw_ptr_src = src; // TODO better source location const raw_ptr_ty = sema.typeOf(raw_ptr); - const inner_ty = if (raw_ptr_ty.zigTypeTag() == .Pointer and (raw_ptr_ty.ptrSize() == .One or raw_ptr_ty.ptrSize() == .C)) - raw_ptr_ty.childType() + const inner_ty = if (raw_ptr_ty.zigTypeTag(mod) == .Pointer and (raw_ptr_ty.ptrSize(mod) == .One or raw_ptr_ty.ptrSize(mod) == .C)) + raw_ptr_ty.childType(mod) else - return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(sema.mod)}); + return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(mod)}); // Optionally dereference a second pointer to get the concrete type. - const is_double_ptr = inner_ty.zigTypeTag() == .Pointer and inner_ty.ptrSize() == .One; - const concrete_ty = if (is_double_ptr) inner_ty.childType() else inner_ty; + const is_double_ptr = inner_ty.zigTypeTag(mod) == .Pointer and inner_ty.ptrSize(mod) == .One; + const concrete_ty = if (is_double_ptr) inner_ty.childType(mod) else inner_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) else raw_ptr; - const arena = sema.arena; find_field: { - switch (concrete_ty.zigTypeTag()) { + switch (concrete_ty.zigTypeTag(mod)) { .Struct => { const struct_ty = try sema.resolveTypeFields(concrete_ty); - if (struct_ty.castTag(.@"struct")) |struct_obj| { - const field_index_usize = struct_obj.data.fields.getIndex(field_name) orelse + if (mod.typeToStruct(struct_ty)) |struct_obj| { + const field_index_usize = struct_obj.fields.getIndex(field_name) orelse break :find_field; const field_index = @intCast(u32, field_index_usize); - const field = struct_obj.data.fields.values()[field_index]; + const field = struct_obj.fields.values()[field_index]; return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr); - } else if (struct_ty.isTuple()) { - if (mem.eql(u8, field_name, "len")) { - return sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount()); + } else if (struct_ty.isTuple(mod)) { + if (ip.stringEqlSlice(field_name, "len")) { + return .{ .direct = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount(mod)) }; + } + if (field_name.toUnsigned(ip)) |field_index| { + if (field_index >= struct_ty.structFieldCount(mod)) break :find_field; + return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(field_index, mod), field_index, object_ptr); } - if (std.fmt.parseUnsigned(u32, field_name, 10)) |field_index| { - if (field_index >= struct_ty.structFieldCount()) break :find_field; - return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(field_index), field_index, object_ptr); - } else |_| {} } else { - const max = struct_ty.structFieldCount(); - var i: u32 = 0; - while (i < max) : (i += 1) { - if (mem.eql(u8, struct_ty.structFieldName(i), field_name)) { - return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(i), i, object_ptr); + const max = struct_ty.structFieldCount(mod); + for (0..max) |i_usize| { + const i = @intCast(u32, i_usize); + if (field_name == struct_ty.structFieldName(i, mod)) { + return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(i, mod), i, object_ptr); } } } }, .Union => { const union_ty = try sema.resolveTypeFields(concrete_ty); - const fields = union_ty.unionFields(); + const fields = union_ty.unionFields(mod); const field_index_usize = fields.getIndex(field_name) orelse break :find_field; const field_index = @intCast(u32, field_index_usize); const field = fields.values()[field_index]; @@ -23970,84 +25431,72 @@ fn fieldCallBind( }, .Type => { const namespace = try sema.analyzeLoad(block, src, object_ptr, src); - return sema.fieldVal(block, src, namespace, field_name, field_name_src); + return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) }; }, else => {}, } } // If we get here, we need to look for a decl in the struct type instead. - const found_decl = switch (concrete_ty.zigTypeTag()) { + const found_decl = switch (concrete_ty.zigTypeTag(mod)) { .Struct, .Opaque, .Union, .Enum => found_decl: { - if (concrete_ty.getNamespace()) |namespace| { + if (concrete_ty.getNamespaceIndex(mod).unwrap()) |namespace| { if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| { try sema.addReferencedBy(block, src, decl_idx); - const inst = try sema.analyzeDeclRef(decl_idx); - - const decl_val = try sema.analyzeLoad(block, src, inst, src); + const decl_val = try sema.analyzeDeclVal(block, src, decl_idx); const decl_type = sema.typeOf(decl_val); - if (decl_type.zigTypeTag() == .Fn and - decl_type.fnParamLen() >= 1) - { - const first_param_type = decl_type.fnParamType(0); - const first_param_tag = first_param_type.tag(); + if (mod.typeToFunc(decl_type)) |func_type| f: { + if (func_type.param_types.len == 0) break :f; + + const first_param_type = func_type.param_types[0].toType(); // zig fmt: off - if (first_param_tag == .var_args_param or - first_param_tag == .generic_poison or ( - first_param_type.zigTypeTag() == .Pointer and - (first_param_type.ptrSize() == .One or - first_param_type.ptrSize() == .C) and - first_param_type.childType().eql(concrete_ty, sema.mod))) + if (first_param_type.isGenericPoison() or ( + first_param_type.zigTypeTag(mod) == .Pointer and + (first_param_type.ptrSize(mod) == .One or + first_param_type.ptrSize(mod) == .C) and + first_param_type.childType(mod).eql(concrete_ty, mod))) { // zig fmt: on + // Note that if the param type is generic poison, we know that it must + // specifically be `anytype` since it's the first parameter, meaning we + // can safely assume it can be a pointer. // TODO: bound fn calls on rvalues should probably // generate a by-value argument somehow. - const ty = Type.Tag.bound_fn.init(); - const value = try Value.Tag.bound_fn.create(arena, .{ + return .{ .method = .{ .func_inst = decl_val, .arg0_inst = object_ptr, - }); - return sema.addConstant(ty, value); - } else if (first_param_type.eql(concrete_ty, sema.mod)) { + } }; + } else if (first_param_type.eql(concrete_ty, mod)) { const deref = try sema.analyzeLoad(block, src, object_ptr, src); - const ty = Type.Tag.bound_fn.init(); - const value = try Value.Tag.bound_fn.create(arena, .{ + return .{ .method = .{ .func_inst = decl_val, .arg0_inst = deref, - }); - return sema.addConstant(ty, value); - } else if (first_param_type.zigTypeTag() == .Optional) { - var opt_buf: Type.Payload.ElemType = undefined; - const child = first_param_type.optionalChild(&opt_buf); - if (child.eql(concrete_ty, sema.mod)) { + } }; + } else if (first_param_type.zigTypeTag(mod) == .Optional) { + const child = first_param_type.optionalChild(mod); + if (child.eql(concrete_ty, mod)) { const deref = try sema.analyzeLoad(block, src, object_ptr, src); - const ty = Type.Tag.bound_fn.init(); - const value = try Value.Tag.bound_fn.create(arena, .{ + return .{ .method = .{ .func_inst = decl_val, .arg0_inst = deref, - }); - return sema.addConstant(ty, value); - } else if (child.zigTypeTag() == .Pointer and - child.ptrSize() == .One and - child.childType().eql(concrete_ty, sema.mod)) + } }; + } else if (child.zigTypeTag(mod) == .Pointer and + child.ptrSize(mod) == .One and + child.childType(mod).eql(concrete_ty, mod)) { - const ty = Type.Tag.bound_fn.init(); - const value = try Value.Tag.bound_fn.create(arena, .{ + return .{ .method = .{ .func_inst = decl_val, .arg0_inst = object_ptr, - }); - return sema.addConstant(ty, value); + } }; } - } else if (first_param_type.zigTypeTag() == .ErrorUnion and - first_param_type.errorUnionPayload().eql(concrete_ty, sema.mod)) + } else if (first_param_type.zigTypeTag(mod) == .ErrorUnion and + first_param_type.errorUnionPayload(mod).eql(concrete_ty, mod)) { const deref = try sema.analyzeLoad(block, src, object_ptr, src); - const ty = Type.Tag.bound_fn.init(); - const value = try Value.Tag.bound_fn.create(arena, .{ + return .{ .method = .{ .func_inst = decl_val, .arg0_inst = deref, - }); - return sema.addConstant(ty, value); + } }; } } break :found_decl decl_idx; @@ -24059,12 +25508,15 @@ fn fieldCallBind( }; const msg = msg: { - const msg = try sema.errMsg(block, src, "no field or member function named '{s}' in '{}'", .{ field_name, concrete_ty.fmt(sema.mod) }); + const msg = try sema.errMsg(block, src, "no field or member function named '{}' in '{}'", .{ + field_name.fmt(ip), + concrete_ty.fmt(mod), + }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, concrete_ty); if (found_decl) |decl_idx| { - const decl = sema.mod.declPtr(decl_idx); - try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "'{s}' is not a member function", .{field_name}); + const decl = mod.declPtr(decl_idx); + try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "'{}' is not a member function", .{field_name.fmt(ip)}); } break :msg msg; }; @@ -24079,55 +25531,56 @@ fn finishFieldCallBind( field_ty: Type, field_index: u32, object_ptr: Air.Inst.Ref, -) CompileError!Air.Inst.Ref { +) CompileError!ResolvedFieldCallee { + const mod = sema.mod; const arena = sema.arena; - const ptr_field_ty = try Type.ptr(arena, sema.mod, .{ + const ptr_field_ty = try Type.ptr(arena, mod, .{ .pointee_type = field_ty, - .mutable = ptr_ty.ptrIsMutable(), - .@"addrspace" = ptr_ty.ptrAddressSpace(), + .mutable = ptr_ty.ptrIsMutable(mod), + .@"addrspace" = ptr_ty.ptrAddressSpace(mod), }); - const container_ty = ptr_ty.childType(); - if (container_ty.zigTypeTag() == .Struct) { - if (container_ty.structFieldValueComptime(field_index)) |default_val| { - return sema.addConstant(field_ty, default_val); + const container_ty = ptr_ty.childType(mod); + if (container_ty.zigTypeTag(mod) == .Struct) { + if (try container_ty.structFieldValueComptime(mod, field_index)) |default_val| { + return .{ .direct = try sema.addConstant(field_ty, default_val) }; } } if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| { - const pointer = try sema.addConstant( - ptr_field_ty, - try Value.Tag.field_ptr.create(arena, .{ - .container_ptr = struct_ptr_val, - .container_ty = container_ty, - .field_index = field_index, - }), - ); - return sema.analyzeLoad(block, src, pointer, src); + const pointer = try sema.addConstant(ptr_field_ty, (try mod.intern(.{ .ptr = .{ + .ty = ptr_field_ty.toIntern(), + .addr = .{ .field = .{ + .base = struct_ptr_val.toIntern(), + .index = field_index, + } }, + } })).toValue()); + return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) }; } try sema.requireRuntimeBlock(block, src, null); const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty); - return sema.analyzeLoad(block, src, ptr_inst, src); + return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) }; } fn namespaceLookup( sema: *Sema, block: *Block, src: LazySrcLoc, - namespace: *Namespace, - decl_name: []const u8, + namespace: Namespace.Index, + decl_name: InternPool.NullTerminatedString, ) CompileError!?Decl.Index { + const mod = sema.mod; const gpa = sema.gpa; if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| { - const decl = sema.mod.declPtr(decl_index); - if (!decl.is_pub and decl.getFileScope() != block.getFileScope()) { + const decl = mod.declPtr(decl_index); + if (!decl.is_pub and decl.getFileScope(mod) != block.getFileScope(mod)) { const msg = msg: { - const msg = try sema.errMsg(block, src, "'{s}' is not marked 'pub'", .{ - decl_name, + const msg = try sema.errMsg(block, src, "'{}' is not marked 'pub'", .{ + decl_name.fmt(&mod.intern_pool), }); errdefer msg.destroy(gpa); - try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{}); + try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -24141,8 +25594,8 @@ fn namespaceLookupRef( sema: *Sema, block: *Block, src: LazySrcLoc, - namespace: *Namespace, - decl_name: []const u8, + namespace: Namespace.Index, + decl_name: InternPool.NullTerminatedString, ) CompileError!?Air.Inst.Ref { const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null; try sema.addReferencedBy(block, src, decl); @@ -24153,8 +25606,8 @@ fn namespaceLookupVal( sema: *Sema, block: *Block, src: LazySrcLoc, - namespace: *Namespace, - decl_name: []const u8, + namespace: Namespace.Index, + decl_name: InternPool.NullTerminatedString, ) CompileError!?Air.Inst.Ref { const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null; return try sema.analyzeDeclVal(block, src, decl); @@ -24165,29 +25618,30 @@ fn structFieldPtr( block: *Block, src: LazySrcLoc, struct_ptr: Air.Inst.Ref, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, unresolved_struct_ty: Type, initializing: bool, ) CompileError!Air.Inst.Ref { - assert(unresolved_struct_ty.zigTypeTag() == .Struct); + const mod = sema.mod; + assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct); const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); try sema.resolveStructLayout(struct_ty); - if (struct_ty.isTuple()) { - if (mem.eql(u8, field_name, "len")) { - const len_inst = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount()); + if (struct_ty.isTuple(mod)) { + if (mod.intern_pool.stringEqlSlice(field_name, "len")) { + const len_inst = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount(mod)); return sema.analyzeRef(block, src, len_inst); } const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); - } else if (struct_ty.isAnonStruct()) { + } else if (struct_ty.isAnonStruct(mod)) { const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); } - const struct_obj = struct_ty.castTag(.@"struct").?.data; + const struct_obj = mod.typeToStruct(struct_ty).?; const field_index_big = struct_obj.fields.getIndex(field_name) orelse return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); @@ -24206,14 +25660,15 @@ fn structFieldPtrByIndex( struct_ty: Type, initializing: bool, ) CompileError!Air.Inst.Ref { - if (struct_ty.isAnonStruct()) { + const mod = sema.mod; + if (struct_ty.isAnonStruct(mod)) { return sema.tupleFieldPtr(block, src, struct_ptr, field_src, field_index, initializing); } - const struct_obj = struct_ty.castTag(.@"struct").?.data; + const struct_obj = mod.typeToStruct(struct_ty).?; const field = struct_obj.fields.values()[field_index]; const struct_ptr_ty = sema.typeOf(struct_ptr); - const struct_ptr_ty_info = struct_ptr_ty.ptrInfo().data; + const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod); var ptr_ty_data: Type.Payload.Pointer.Data = .{ .pointee_type = field.ty, @@ -24222,7 +25677,7 @@ fn structFieldPtrByIndex( .@"addrspace" = struct_ptr_ty_info.@"addrspace", }; - const target = sema.mod.getTarget(); + const target = mod.getTarget(); if (struct_obj.layout == .Packed) { comptime assert(Type.packed_struct_layout_version == 2); @@ -24234,7 +25689,7 @@ fn structFieldPtrByIndex( if (i == field_index) { ptr_ty_data.bit_offset = running_bits; } - running_bits += @intCast(u16, f.ty.bitSize(target)); + running_bits += @intCast(u16, f.ty.bitSize(mod)); } ptr_ty_data.host_size = (running_bits + 7) / 8; @@ -24248,7 +25703,7 @@ fn structFieldPtrByIndex( const parent_align = if (struct_ptr_ty_info.@"align" != 0) struct_ptr_ty_info.@"align" else - struct_ptr_ty_info.pointee_type.abiAlignment(target); + struct_ptr_ty_info.pointee_type.abiAlignment(mod); ptr_ty_data.@"align" = parent_align; // If the field happens to be byte-aligned, simplify the pointer type. @@ -24262,8 +25717,8 @@ fn structFieldPtrByIndex( if (parent_align != 0 and ptr_ty_data.bit_offset % 8 == 0 and target.cpu.arch.endian() == .Little) { - const elem_size_bytes = ptr_ty_data.pointee_type.abiSize(target); - const elem_size_bits = ptr_ty_data.pointee_type.bitSize(target); + const elem_size_bytes = ptr_ty_data.pointee_type.abiSize(mod); + const elem_size_bits = ptr_ty_data.pointee_type.bitSize(mod); if (elem_size_bytes * 8 == elem_size_bits) { const byte_offset = ptr_ty_data.bit_offset / 8; const new_align = @as(u32, 1) << @intCast(u5, @ctz(byte_offset | parent_align)); @@ -24276,25 +25731,25 @@ fn structFieldPtrByIndex( ptr_ty_data.@"align" = field.abi_align; } - const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); + const ptr_field_ty = try Type.ptr(sema.arena, mod, ptr_ty_data); if (field.is_comptime) { - const val = try Value.Tag.comptime_field_ptr.create(sema.arena, .{ - .field_ty = try field.ty.copy(sema.arena), - .field_val = try field.default_val.copy(sema.arena), - }); - return sema.addConstant(ptr_field_ty, val); + const val = try mod.intern(.{ .ptr = .{ + .ty = ptr_field_ty.toIntern(), + .addr = .{ .comptime_field = field.default_val }, + } }); + return sema.addConstant(ptr_field_ty, val.toValue()); } if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { - return sema.addConstant( - ptr_field_ty, - try Value.Tag.field_ptr.create(sema.arena, .{ - .container_ptr = struct_ptr_val, - .container_ty = struct_ptr_ty.childType(), - .field_index = field_index, - }), - ); + const val = try mod.intern(.{ .ptr = .{ + .ty = ptr_field_ty.toIntern(), + .addr = .{ .field = .{ + .base = try struct_ptr_val.intern(struct_ptr_ty, mod), + .index = field_index, + } }, + } }); + return sema.addConstant(ptr_field_ty, val.toValue()); } try sema.requireRuntimeBlock(block, src, null); @@ -24306,21 +25761,17 @@ fn structFieldVal( block: *Block, src: LazySrcLoc, struct_byval: Air.Inst.Ref, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, unresolved_struct_ty: Type, ) CompileError!Air.Inst.Ref { - assert(unresolved_struct_ty.zigTypeTag() == .Struct); + const mod = sema.mod; + assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct); const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); - switch (struct_ty.tag()) { - .tuple, .empty_struct_literal => return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty), - .anon_struct => { - const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); - return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty); - }, - .@"struct" => { - const struct_obj = struct_ty.castTag(.@"struct").?.data; + switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; if (struct_obj.is_tuple) return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); const field_index_usize = struct_obj.fields.getIndex(field_name) orelse @@ -24329,22 +25780,28 @@ fn structFieldVal( const field = struct_obj.fields.values()[field_index]; if (field.is_comptime) { - return sema.addConstant(field.ty, field.default_val); + return sema.addConstant(field.ty, field.default_val.toValue()); } if (try sema.resolveMaybeUndefVal(struct_byval)) |struct_val| { - if (struct_val.isUndef()) return sema.addConstUndef(field.ty); + if (struct_val.isUndef(mod)) return sema.addConstUndef(field.ty); if ((try sema.typeHasOnePossibleValue(field.ty))) |opv| { return sema.addConstant(field.ty, opv); } - - const field_values = struct_val.castTag(.aggregate).?.data; - return sema.addConstant(field.ty, field_values[field_index]); + return sema.addConstant(field.ty, try struct_val.fieldValue(mod, field_index)); } try sema.requireRuntimeBlock(block, src, null); return block.addStructFieldVal(struct_byval, field_index, field.ty); }, + .anon_struct_type => |anon_struct| { + if (anon_struct.names.len == 0) { + return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); + } else { + const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); + return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty); + } + }, else => unreachable, } } @@ -24354,12 +25811,13 @@ fn tupleFieldVal( block: *Block, src: LazySrcLoc, tuple_byval: Air.Inst.Ref, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, tuple_ty: Type, ) CompileError!Air.Inst.Ref { - if (mem.eql(u8, field_name, "len")) { - return sema.addIntUnsigned(Type.usize, tuple_ty.structFieldCount()); + const mod = sema.mod; + if (mod.intern_pool.stringEqlSlice(field_name, "len")) { + return sema.addIntUnsigned(Type.usize, tuple_ty.structFieldCount(mod)); } const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src); return sema.tupleFieldValByIndex(block, src, tuple_byval, field_index, tuple_ty); @@ -24370,19 +25828,20 @@ fn tupleFieldIndex( sema: *Sema, block: *Block, tuple_ty: Type, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, ) CompileError!u32 { - assert(!std.mem.eql(u8, field_name, "len")); - if (std.fmt.parseUnsigned(u32, field_name, 10)) |field_index| { - if (field_index < tuple_ty.structFieldCount()) return field_index; - return sema.fail(block, field_name_src, "index '{s}' out of bounds of tuple '{}'", .{ - field_name, tuple_ty.fmt(sema.mod), + const mod = sema.mod; + assert(!mod.intern_pool.stringEqlSlice(field_name, "len")); + if (field_name.toUnsigned(&mod.intern_pool)) |field_index| { + if (field_index < tuple_ty.structFieldCount(mod)) return field_index; + return sema.fail(block, field_name_src, "index '{}' out of bounds of tuple '{}'", .{ + field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod), }); - } else |_| {} + } - return sema.fail(block, field_name_src, "no field named '{s}' in tuple '{}'", .{ - field_name, tuple_ty.fmt(sema.mod), + return sema.fail(block, field_name_src, "no field named '{}' in tuple '{}'", .{ + field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod), }); } @@ -24394,22 +25853,29 @@ fn tupleFieldValByIndex( field_index: u32, tuple_ty: Type, ) CompileError!Air.Inst.Ref { - const field_ty = tuple_ty.structFieldType(field_index); + const mod = sema.mod; + const field_ty = tuple_ty.structFieldType(field_index, mod); - if (tuple_ty.structFieldValueComptime(field_index)) |default_value| { + if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| { return sema.addConstant(field_ty, default_value); } if (try sema.resolveMaybeUndefVal(tuple_byval)) |tuple_val| { - if (tuple_val.isUndef()) return sema.addConstUndef(field_ty); if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| { return sema.addConstant(field_ty, opv); } - const field_values = tuple_val.castTag(.aggregate).?.data; - return sema.addConstant(field_ty, field_values[field_index]); + return switch (mod.intern_pool.indexToKey(tuple_val.toIntern())) { + .undef => sema.addConstUndef(field_ty), + .aggregate => |aggregate| sema.addConstant(field_ty, switch (aggregate.storage) { + .bytes => |bytes| try mod.intValue(Type.u8, bytes[0]), + .elems => |elems| elems[field_index].toValue(), + .repeated_elem => |elem| elem.toValue(), + }), + else => unreachable, + }; } - if (tuple_ty.structFieldValueComptime(field_index)) |default_val| { + if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| { return sema.addConstant(field_ty, default_val); } @@ -24422,33 +25888,38 @@ fn unionFieldPtr( block: *Block, src: LazySrcLoc, union_ptr: Air.Inst.Ref, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, unresolved_union_ty: Type, initializing: bool, ) CompileError!Air.Inst.Ref { const arena = sema.arena; - assert(unresolved_union_ty.zigTypeTag() == .Union); + const mod = sema.mod; + const ip = &mod.intern_pool; + + assert(unresolved_union_ty.zigTypeTag(mod) == .Union); const union_ptr_ty = sema.typeOf(union_ptr); const union_ty = try sema.resolveTypeFields(unresolved_union_ty); - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); const field = union_obj.fields.values()[field_index]; - const ptr_field_ty = try Type.ptr(arena, sema.mod, .{ + const ptr_field_ty = try Type.ptr(arena, mod, .{ .pointee_type = field.ty, - .mutable = union_ptr_ty.ptrIsMutable(), - .@"volatile" = union_ptr_ty.isVolatilePtr(), - .@"addrspace" = union_ptr_ty.ptrAddressSpace(), + .mutable = union_ptr_ty.ptrIsMutable(mod), + .@"volatile" = union_ptr_ty.isVolatilePtr(mod), + .@"addrspace" = union_ptr_ty.ptrAddressSpace(mod), }); - const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name).?); + const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name, mod).?); - if (initializing and field.ty.zigTypeTag() == .NoReturn) { + if (initializing and field.ty.zigTypeTag(mod) == .NoReturn) { const msg = msg: { const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{}); errdefer msg.destroy(sema.gpa); - try sema.addFieldErrNote(union_ty, field_index, msg, "field '{s}' declared here", .{field_name}); + try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ + field_name.fmt(ip), + }); try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; }; @@ -24460,21 +25931,20 @@ fn unionFieldPtr( .Auto => if (!initializing) { const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse break :ct; - if (union_val.isUndef()) { + if (union_val.isUndef(mod)) { return sema.failWithUseOfUndef(block, src); } - const tag_and_val = union_val.castTag(.@"union").?.data; - var field_tag_buf: Value.Payload.U32 = .{ - .base = .{ .tag = .enum_field_index }, - .data = enum_field_index, - }; - const field_tag = Value.initPayload(&field_tag_buf.base); - const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod); + const un = ip.indexToKey(union_val.toIntern()).un; + const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); + const tag_matches = un.tag == field_tag.toIntern(); if (!tag_matches) { const msg = msg: { - const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data; - const active_field_name = union_obj.tag_ty.enumFieldName(active_index); - const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name }); + const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?; + const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod); + const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{ + field_name.fmt(ip), + active_field_name.fmt(ip), + }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; @@ -24484,28 +25954,27 @@ fn unionFieldPtr( }, .Packed, .Extern => {}, } - return sema.addConstant( - ptr_field_ty, - try Value.Tag.field_ptr.create(arena, .{ - .container_ptr = union_ptr_val, - .container_ty = union_ty, - .field_index = field_index, - }), - ); + return sema.addConstant(ptr_field_ty, (try mod.intern(.{ .ptr = .{ + .ty = ptr_field_ty.toIntern(), + .addr = .{ .field = .{ + .base = union_ptr_val.toIntern(), + .index = field_index, + } }, + } })).toValue()); } try sema.requireRuntimeBlock(block, src, null); if (!initializing and union_obj.layout == .Auto and block.wantSafety() and - union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1) + union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1) { - const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); + const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val); // TODO would it be better if get_union_tag supported pointers to unions? const union_val = try block.addTyOp(.load, union_ty, union_ptr); const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val); try sema.panicInactiveUnionField(block, active_tag, wanted_tag); } - if (field.ty.zigTypeTag() == .NoReturn) { + if (field.ty.zigTypeTag(mod) == .NoReturn) { _ = try block.addNoOp(.unreach); return Air.Inst.Ref.unreachable_value; } @@ -24517,37 +25986,37 @@ fn unionFieldVal( block: *Block, src: LazySrcLoc, union_byval: Air.Inst.Ref, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, unresolved_union_ty: Type, ) CompileError!Air.Inst.Ref { - assert(unresolved_union_ty.zigTypeTag() == .Union); + const mod = sema.mod; + const ip = &mod.intern_pool; + assert(unresolved_union_ty.zigTypeTag(mod) == .Union); const union_ty = try sema.resolveTypeFields(unresolved_union_ty); - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); const field = union_obj.fields.values()[field_index]; - const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name).?); + const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name, mod).?); if (try sema.resolveMaybeUndefVal(union_byval)) |union_val| { - if (union_val.isUndef()) return sema.addConstUndef(field.ty); + if (union_val.isUndef(mod)) return sema.addConstUndef(field.ty); - const tag_and_val = union_val.castTag(.@"union").?.data; - var field_tag_buf: Value.Payload.U32 = .{ - .base = .{ .tag = .enum_field_index }, - .data = enum_field_index, - }; - const field_tag = Value.initPayload(&field_tag_buf.base); - const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod); + const un = ip.indexToKey(union_val.toIntern()).un; + const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); + const tag_matches = un.tag == field_tag.toIntern(); switch (union_obj.layout) { .Auto => { if (tag_matches) { - return sema.addConstant(field.ty, tag_and_val.val); + return sema.addConstant(field.ty, un.val.toValue()); } else { const msg = msg: { - const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data; - const active_field_name = union_obj.tag_ty.enumFieldName(active_index); - const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name }); + const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?; + const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod); + const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{ + field_name.fmt(ip), active_field_name.fmt(ip), + }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; @@ -24557,10 +26026,10 @@ fn unionFieldVal( }, .Packed, .Extern => { if (tag_matches) { - return sema.addConstant(field.ty, tag_and_val.val); + return sema.addConstant(field.ty, un.val.toValue()); } else { - const old_ty = union_ty.unionFieldType(tag_and_val.tag, sema.mod); - if (try sema.bitCastVal(block, src, tag_and_val.val, old_ty, field.ty, 0)) |new_val| { + const old_ty = union_ty.unionFieldType(un.tag.toValue(), mod); + if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field.ty, 0)) |new_val| { return sema.addConstant(field.ty, new_val); } } @@ -24570,14 +26039,14 @@ fn unionFieldVal( try sema.requireRuntimeBlock(block, src, null); if (union_obj.layout == .Auto and block.wantSafety() and - union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1) + union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1) { - const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); + const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val); const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval); try sema.panicInactiveUnionField(block, active_tag, wanted_tag); } - if (field.ty.zigTypeTag() == .NoReturn) { + if (field.ty.zigTypeTag(mod) == .NoReturn) { _ = try block.addNoOp(.unreach); return Air.Inst.Ref.unreachable_value; } @@ -24594,24 +26063,22 @@ fn elemPtr( init: bool, oob_safety: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const indexable_ptr_src = src; // TODO better source location const indexable_ptr_ty = sema.typeOf(indexable_ptr); - const target = sema.mod.getTarget(); - const indexable_ty = switch (indexable_ptr_ty.zigTypeTag()) { - .Pointer => indexable_ptr_ty.elemType(), - else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(sema.mod)}), + const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(mod)) { + .Pointer => indexable_ptr_ty.childType(mod), + else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(mod)}), }; - if (!indexable_ty.isIndexable()) { - return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(sema.mod)}); - } + try checkIndexable(sema, block, src, indexable_ty); - switch (indexable_ty.zigTypeTag()) { + switch (indexable_ty.zigTypeTag(mod)) { .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), .Struct => { // Tuple field access. const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known"); - const index = @intCast(u32, index_val.toUnsignedInt(target)); + const index = @intCast(u32, index_val.toUnsignedInt(mod)); return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init); }, else => { @@ -24634,13 +26101,11 @@ fn elemPtrOneLayerOnly( ) CompileError!Air.Inst.Ref { const indexable_src = src; // TODO better source location const indexable_ty = sema.typeOf(indexable); - const target = sema.mod.getTarget(); + const mod = sema.mod; - if (!indexable_ty.isIndexable()) { - return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(sema.mod)}); - } + try checkIndexable(sema, block, src, indexable_ty); - switch (indexable_ty.ptrSize()) { + switch (indexable_ty.ptrSize(mod)) { .Slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), .Many, .C => { const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable); @@ -24648,9 +26113,9 @@ fn elemPtrOneLayerOnly( const runtime_src = rs: { const ptr_val = maybe_ptr_val orelse break :rs indexable_src; const index_val = maybe_index_val orelse break :rs elem_index_src; - const index = @intCast(usize, index_val.toUnsignedInt(target)); - const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod); + const index = @intCast(usize, index_val.toUnsignedInt(mod)); const result_ty = try sema.elemPtrType(indexable_ty, index); + const elem_ptr = try ptr_val.elemPtr(result_ty, index, mod); return sema.addConstant(result_ty, elem_ptr); }; const result_ty = try sema.elemPtrType(indexable_ty, null); @@ -24659,8 +26124,19 @@ fn elemPtrOneLayerOnly( return block.addPtrElemPtr(indexable, elem_index, result_ty); }, .One => { - assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable - return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety); + const child_ty = indexable_ty.childType(mod); + switch (child_ty.zigTypeTag(mod)) { + .Array, .Vector => { + return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety); + }, + .Struct => { + assert(child_ty.isTuple(mod)); + const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known"); + const index = @intCast(u32, index_val.toUnsignedInt(mod)); + return sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false); + }, + else => unreachable, // Guaranteed by checkIndexable + } }, } } @@ -24676,18 +26152,16 @@ fn elemVal( ) CompileError!Air.Inst.Ref { const indexable_src = src; // TODO better source location const indexable_ty = sema.typeOf(indexable); - const target = sema.mod.getTarget(); + const mod = sema.mod; - if (!indexable_ty.isIndexable()) { - return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(sema.mod)}); - } + try checkIndexable(sema, block, src, indexable_ty); // TODO in case of a vector of pointers, we need to detect whether the element // index is a scalar or vector instead of unconditionally casting to usize. const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src); - switch (indexable_ty.zigTypeTag()) { - .Pointer => switch (indexable_ty.ptrSize()) { + switch (indexable_ty.zigTypeTag(mod)) { + .Pointer => switch (indexable_ty.ptrSize(mod)) { .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); @@ -24696,10 +26170,14 @@ fn elemVal( const runtime_src = rs: { const indexable_val = maybe_indexable_val orelse break :rs indexable_src; const index_val = maybe_index_val orelse break :rs elem_index_src; - const index = @intCast(usize, index_val.toUnsignedInt(target)); - const elem_ptr_val = try indexable_val.elemPtr(indexable_ty, sema.arena, index, sema.mod); - if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, indexable_ty)) |elem_val| { - return sema.addConstant(indexable_ty.elemType2(), elem_val); + const index = @intCast(usize, index_val.toUnsignedInt(mod)); + const elem_ty = indexable_ty.elemType2(mod); + const many_ptr_ty = try mod.manyConstPtrType(elem_ty); + const many_ptr_val = try mod.getCoerced(indexable_val, many_ptr_ty); + const elem_ptr_ty = try mod.singleConstPtrType(elem_ty); + const elem_ptr_val = try many_ptr_val.elemPtr(elem_ptr_ty, index, mod); + if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty)) |elem_val| { + return sema.addConstant(elem_ty, try mod.getCoerced(elem_val, elem_ty)); } break :rs indexable_src; }; @@ -24708,7 +26186,15 @@ fn elemVal( return block.addBinOp(.ptr_elem_val, indexable, elem_index); }, .One => { - assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable + arr_sent: { + const inner_ty = indexable_ty.childType(mod); + if (inner_ty.zigTypeTag(mod) != .Array) break :arr_sent; + const sentinel = inner_ty.sentinel(mod) 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(mod)); + if (index != inner_ty.arrayLen(mod)) break :arr_sent; + return sema.addConstant(inner_ty.childType(mod), 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); }, @@ -24721,7 +26207,7 @@ fn elemVal( .Struct => { // Tuple field access. const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known"); - const index = @intCast(u32, index_val.toUnsignedInt(target)); + const index = @intCast(u32, index_val.toUnsignedInt(mod)); return sema.tupleField(block, indexable_src, indexable, elem_index_src, index); }, else => unreachable, @@ -24736,6 +26222,7 @@ fn validateRuntimeElemAccess( parent_ty: Type, parent_src: LazySrcLoc, ) CompileError!void { + const mod = sema.mod; const valid_rt = try sema.validateRunTimeType(elem_ty, false); if (!valid_rt) { const msg = msg: { @@ -24743,12 +26230,12 @@ fn validateRuntimeElemAccess( block, elem_index_src, "values of type '{}' must be comptime-known, but index value is runtime-known", - .{parent_ty.fmt(sema.mod)}, + .{parent_ty.fmt(mod)}, ); errdefer msg.destroy(sema.gpa); - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsComptime(block, elem_index_src, msg, parent_src.toSrcLoc(src_decl), parent_ty); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsComptime(msg, parent_src.toSrcLoc(src_decl, mod), parent_ty); break :msg msg; }; @@ -24765,10 +26252,11 @@ fn tupleFieldPtr( field_index: u32, init: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const tuple_ptr_ty = sema.typeOf(tuple_ptr); - const tuple_ty = tuple_ptr_ty.childType(); + const tuple_ty = tuple_ptr_ty.childType(mod); _ = try sema.resolveTypeFields(tuple_ty); - const field_count = tuple_ty.structFieldCount(); + const field_count = tuple_ty.structFieldCount(mod); if (field_count == 0) { return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{}); @@ -24780,31 +26268,29 @@ fn tupleFieldPtr( }); } - const field_ty = tuple_ty.structFieldType(field_index); - const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{ + const field_ty = tuple_ty.structFieldType(field_index, mod); + const ptr_field_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = field_ty, - .mutable = tuple_ptr_ty.ptrIsMutable(), - .@"volatile" = tuple_ptr_ty.isVolatilePtr(), - .@"addrspace" = tuple_ptr_ty.ptrAddressSpace(), + .mutable = tuple_ptr_ty.ptrIsMutable(mod), + .@"volatile" = tuple_ptr_ty.isVolatilePtr(mod), + .@"addrspace" = tuple_ptr_ty.ptrAddressSpace(mod), }); - if (tuple_ty.structFieldValueComptime(field_index)) |default_val| { - const val = try Value.Tag.comptime_field_ptr.create(sema.arena, .{ - .field_ty = field_ty, - .field_val = default_val, - }); - return sema.addConstant(ptr_field_ty, val); + if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| { + return sema.addConstant(ptr_field_ty, (try mod.intern(.{ .ptr = .{ + .ty = ptr_field_ty.toIntern(), + .addr = .{ .comptime_field = default_val.toIntern() }, + } })).toValue()); } if (try sema.resolveMaybeUndefVal(tuple_ptr)) |tuple_ptr_val| { - return sema.addConstant( - ptr_field_ty, - try Value.Tag.field_ptr.create(sema.arena, .{ - .container_ptr = tuple_ptr_val, - .container_ty = tuple_ty, - .field_index = field_index, - }), - ); + return sema.addConstant(ptr_field_ty, (try mod.intern(.{ .ptr = .{ + .ty = ptr_field_ty.toIntern(), + .addr = .{ .field = .{ + .base = tuple_ptr_val.toIntern(), + .index = field_index, + } }, + } })).toValue()); } if (!init) { @@ -24823,8 +26309,9 @@ fn tupleField( field_index_src: LazySrcLoc, field_index: u32, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const tuple_ty = try sema.resolveTypeFields(sema.typeOf(tuple)); - const field_count = tuple_ty.structFieldCount(); + const field_count = tuple_ty.structFieldCount(mod); if (field_count == 0) { return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{}); @@ -24836,15 +26323,15 @@ fn tupleField( }); } - const field_ty = tuple_ty.structFieldType(field_index); + const field_ty = tuple_ty.structFieldType(field_index, mod); - if (tuple_ty.structFieldValueComptime(field_index)) |default_value| { + if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| { return sema.addConstant(field_ty, default_value); // comptime field } if (try sema.resolveMaybeUndefVal(tuple)) |tuple_val| { - if (tuple_val.isUndef()) return sema.addConstUndef(field_ty); - return sema.addConstant(field_ty, tuple_val.fieldValue(tuple_ty, field_index)); + if (tuple_val.isUndef(mod)) return sema.addConstUndef(field_ty); + return sema.addConstant(field_ty, try tuple_val.fieldValue(mod, field_index)); } try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src); @@ -24863,11 +26350,12 @@ fn elemValArray( elem_index: Air.Inst.Ref, oob_safety: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const array_ty = sema.typeOf(array); - const array_sent = array_ty.sentinel(); - const array_len = array_ty.arrayLen(); + const array_sent = array_ty.sentinel(mod); + const array_len = array_ty.arrayLen(mod); const array_len_s = array_len + @boolToInt(array_sent != null); - const elem_ty = array_ty.childType(); + const elem_ty = array_ty.childType(mod); if (array_len_s == 0) { return sema.fail(block, array_src, "indexing into empty array is not allowed", .{}); @@ -24876,10 +26364,9 @@ fn elemValArray( const maybe_undef_array_val = try sema.resolveMaybeUndefVal(array); // index must be defined since it can access out of bounds const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); - const target = sema.mod.getTarget(); if (maybe_index_val) |index_val| { - const index = @intCast(usize, index_val.toUnsignedInt(target)); + const index = @intCast(usize, index_val.toUnsignedInt(mod)); if (array_sent) |s| { if (index == array_len) { return sema.addConstant(elem_ty, s); @@ -24891,12 +26378,12 @@ fn elemValArray( } } if (maybe_undef_array_val) |array_val| { - if (array_val.isUndef()) { + if (array_val.isUndef(mod)) { return sema.addConstUndef(elem_ty); } if (maybe_index_val) |index_val| { - const index = @intCast(usize, index_val.toUnsignedInt(target)); - const elem_val = try array_val.elemValue(sema.mod, sema.arena, index); + const index = @intCast(usize, index_val.toUnsignedInt(mod)); + const elem_val = try array_val.elemValue(mod, index); return sema.addConstant(elem_ty, elem_val); } } @@ -24927,11 +26414,11 @@ fn elemPtrArray( init: bool, oob_safety: bool, ) CompileError!Air.Inst.Ref { - const target = sema.mod.getTarget(); + const mod = sema.mod; const array_ptr_ty = sema.typeOf(array_ptr); - const array_ty = array_ptr_ty.childType(); - const array_sent = array_ty.sentinel() != null; - const array_len = array_ty.arrayLen(); + const array_ty = array_ptr_ty.childType(mod); + const array_sent = array_ty.sentinel(mod) != null; + const array_len = array_ty.arrayLen(mod); const array_len_s = array_len + @boolToInt(array_sent); if (array_len_s == 0) { @@ -24941,7 +26428,7 @@ fn elemPtrArray( const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(array_ptr); // The index must not be undefined since it can be out of bounds. const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { - const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(target)); + const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod)); 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 }); @@ -24952,17 +26439,17 @@ fn elemPtrArray( const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset); if (maybe_undef_array_ptr_val) |array_ptr_val| { - if (array_ptr_val.isUndef()) { + if (array_ptr_val.isUndef(mod)) { return sema.addConstUndef(elem_ptr_ty); } if (offset) |index| { - const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, sema.mod); + const elem_ptr = try array_ptr_val.elemPtr(elem_ptr_ty, index, mod); return sema.addConstant(elem_ptr_ty, elem_ptr); } } if (!init) { - try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(), array_ty, array_ptr_src); + try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(mod), array_ty, array_ptr_src); } const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src; @@ -24988,32 +26475,33 @@ fn elemValSlice( elem_index: Air.Inst.Ref, oob_safety: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const slice_ty = sema.typeOf(slice); - const slice_sent = slice_ty.sentinel() != null; - const elem_ty = slice_ty.elemType2(); + const slice_sent = slice_ty.sentinel(mod) != null; + const elem_ty = slice_ty.elemType2(mod); var runtime_src = slice_src; // 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 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); - const target = sema.mod.getTarget(); if (maybe_slice_val) |slice_val| { runtime_src = elem_index_src; - const slice_len = slice_val.sliceLen(sema.mod); + const slice_len = slice_val.sliceLen(mod); const slice_len_s = slice_len + @boolToInt(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 = @intCast(usize, index_val.toUnsignedInt(target)); + const index = @intCast(usize, index_val.toUnsignedInt(mod)); if (index >= slice_len_s) { const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); } - const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, sema.mod); - if (try sema.pointerDeref(block, slice_src, elem_ptr_val, slice_ty)) |elem_val| { + const elem_ptr_ty = try sema.elemPtrType(slice_ty, index); + const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod); + if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| { return sema.addConstant(elem_ty, elem_val); } runtime_src = slice_src; @@ -25025,7 +26513,7 @@ fn elemValSlice( try sema.requireRuntimeBlock(block, src, runtime_src); if (oob_safety and block.wantSafety()) { const len_inst = if (maybe_slice_val) |slice_val| - try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)) + try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod)) else try block.addTyOp(.slice_len, Type.usize, slice); const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; @@ -25045,24 +26533,24 @@ fn elemPtrSlice( elem_index: Air.Inst.Ref, oob_safety: bool, ) CompileError!Air.Inst.Ref { - const target = sema.mod.getTarget(); + const mod = sema.mod; const slice_ty = sema.typeOf(slice); - const slice_sent = slice_ty.sentinel() != null; + const slice_sent = slice_ty.sentinel(mod) != null; const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(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, index_val.toUnsignedInt(target)); + const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod)); break :o index; } else null; const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset); if (maybe_undef_slice_val) |slice_val| { - if (slice_val.isUndef()) { + if (slice_val.isUndef(mod)) { return sema.addConstUndef(elem_ptr_ty); } - const slice_len = slice_val.sliceLen(sema.mod); + const slice_len = slice_val.sliceLen(mod); const slice_len_s = slice_len + @boolToInt(slice_sent); if (slice_len_s == 0) { return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); @@ -25072,7 +26560,7 @@ fn elemPtrSlice( const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); } - const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, sema.mod); + const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod); return sema.addConstant(elem_ptr_ty, elem_ptr_val); } } @@ -25084,8 +26572,8 @@ fn elemPtrSlice( if (oob_safety and block.wantSafety()) { const len_inst = len: { if (maybe_undef_slice_val) |slice_val| - if (!slice_val.isUndef()) - break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)); + if (!slice_val.isUndef(mod)) + break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod)); break :len try block.addTyOp(.slice_len, Type.usize, slice); }; const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; @@ -25127,16 +26615,17 @@ const CoerceOpts = struct { fn get(info: @This(), sema: *Sema) !?Module.SrcLoc { if (info.func_inst == .none) return null; + const mod = sema.mod; const fn_decl = (try sema.funcDeclSrc(info.func_inst)) orelse return null; - const param_src = Module.paramSrc(0, sema.gpa, fn_decl, info.param_i); + const param_src = Module.paramSrc(0, mod, fn_decl, info.param_i); if (param_src == .node_offset_param) { return Module.SrcLoc{ - .file_scope = fn_decl.getFileScope(), + .file_scope = fn_decl.getFileScope(mod), .parent_decl_node = fn_decl.src_node, .lazy = LazySrcLoc.nodeOffset(param_src.node_offset_param), }; } - return param_src.toSrcLoc(fn_decl); + return param_src.toSrcLoc(fn_decl, mod); } } = .{}, }; @@ -25149,35 +26638,30 @@ fn coerceExtra( inst_src: LazySrcLoc, opts: CoerceOpts, ) CoersionError!Air.Inst.Ref { - switch (dest_ty_unresolved.tag()) { - .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src), - .generic_poison => return inst, - else => {}, - } + if (dest_ty_unresolved.isGenericPoison()) return inst; + const mod = sema.mod; const dest_ty_src = inst_src; // TODO better source location const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved); const inst_ty = try sema.resolveTypeFields(sema.typeOf(inst)); - const target = sema.mod.getTarget(); + const target = mod.getTarget(); // If the types are the same, we can return the operand. - if (dest_ty.eql(inst_ty, sema.mod)) + if (dest_ty.eql(inst_ty, mod)) return inst; - const arena = sema.arena; const maybe_inst_val = try sema.resolveMaybeUndefVal(inst); var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src); if (in_memory_result == .ok) { if (maybe_inst_val) |val| { - // Keep the comptime Value representation; take the new type. - return sema.addConstant(dest_ty, val); + return sema.coerceInMemory(val, dest_ty); } try sema.requireRuntimeBlock(block, inst_src, null); return block.addBitCast(dest_ty, inst); } - const is_undef = inst_ty.zigTypeTag() == .Undefined; + const is_undef = inst_ty.zigTypeTag(mod) == .Undefined; - switch (dest_ty.zigTypeTag()) { + switch (dest_ty.zigTypeTag(mod)) { .Optional => optional: { // undefined sets the optional bit also to undefined. if (is_undef) { @@ -25185,18 +26669,22 @@ fn coerceExtra( } // null to ?T - if (inst_ty.zigTypeTag() == .Null) { - return sema.addConstant(dest_ty, Value.null); + if (inst_ty.zigTypeTag(mod) == .Null) { + return sema.addConstant(dest_ty, (try mod.intern(.{ .opt = .{ + .ty = dest_ty.toIntern(), + .val = .none, + } })).toValue()); } // cast from ?*T and ?[*]T to ?*anyopaque // but don't do it if the source type is a double pointer - if (dest_ty.isPtrLikeOptional() and dest_ty.elemType2().tag() == .anyopaque and - inst_ty.isPtrAtRuntime()) + if (dest_ty.isPtrLikeOptional(mod) and + dest_ty.elemType2(mod).toIntern() == .anyopaque_type and + inst_ty.isPtrAtRuntime(mod)) anyopaque_check: { if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional; - const elem_ty = inst_ty.elemType2(); - if (elem_ty.zigTypeTag() == .Pointer or elem_ty.isPtrLikeOptional()) { + const elem_ty = inst_ty.elemType2(mod); + if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) { in_memory_result = .{ .double_ptr_to_anyopaque = .{ .actual = inst_ty, .wanted = dest_ty, @@ -25205,12 +26693,12 @@ fn coerceExtra( } // Let the logic below handle wrapping the optional now that // it has been checked to correctly coerce. - if (!inst_ty.isPtrLikeOptional()) break :anyopaque_check; + if (!inst_ty.isPtrLikeOptional(mod)) break :anyopaque_check; return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); } // T to ?T - const child_type = try dest_ty.optionalChildAlloc(sema.arena); + const child_type = dest_ty.optionalChild(mod); const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { error.NotCoercible => { if (in_memory_result == .no_match) { @@ -25224,12 +26712,12 @@ fn coerceExtra( return try sema.wrapOptional(block, dest_ty, intermediate, inst_src); }, .Pointer => pointer: { - const dest_info = dest_ty.ptrInfo().data; + const dest_info = dest_ty.ptrInfo(mod); // Function body to function pointer. - if (inst_ty.zigTypeTag() == .Fn) { + if (inst_ty.zigTypeTag(mod) == .Fn) { const fn_val = try sema.resolveConstValue(block, .unneeded, inst, ""); - const fn_decl = fn_val.pointerDecl().?; + const fn_decl = fn_val.pointerDecl(mod).?; const inst_as_ptr = try sema.analyzeDeclRef(fn_decl); return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); } @@ -25237,13 +26725,13 @@ fn coerceExtra( // *T to *[1]T single_item: { if (dest_info.size != .One) break :single_item; - if (!inst_ty.isSinglePointer()) break :single_item; + if (!inst_ty.isSinglePointer(mod)) break :single_item; if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; - const ptr_elem_ty = inst_ty.childType(); + const ptr_elem_ty = inst_ty.childType(mod); const array_ty = dest_info.pointee_type; - if (array_ty.zigTypeTag() != .Array) break :single_item; - const array_elem_ty = array_ty.childType(); - if (array_ty.arrayLen() != 1) break :single_item; + if (array_ty.zigTypeTag(mod) != .Array) break :single_item; + const array_elem_ty = array_ty.childType(mod); + if (array_ty.arrayLen(mod) != 1) break :single_item; const dest_is_mut = dest_info.mutable; switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { .ok => {}, @@ -25254,11 +26742,11 @@ fn coerceExtra( // Coercions where the source is a single pointer to an array. src_array_ptr: { - if (!inst_ty.isSinglePointer()) break :src_array_ptr; + if (!inst_ty.isSinglePointer(mod)) break :src_array_ptr; if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; - const array_ty = inst_ty.childType(); - if (array_ty.zigTypeTag() != .Array) break :src_array_ptr; - const array_elem_type = array_ty.childType(); + const array_ty = inst_ty.childType(mod); + if (array_ty.zigTypeTag(mod) != .Array) break :src_array_ptr; + const array_elem_type = array_ty.childType(mod); const dest_is_mut = dest_info.mutable; const dst_elem_type = dest_info.pointee_type; @@ -25276,8 +26764,8 @@ fn coerceExtra( } if (dest_info.sentinel) |dest_sent| { - if (array_ty.sentinel()) |inst_sent| { - if (!dest_sent.eql(inst_sent, dst_elem_type, sema.mod)) { + if (array_ty.sentinel(mod)) |inst_sent| { + if (!dest_sent.eql(inst_sent, dst_elem_type, mod)) { in_memory_result = .{ .ptr_sentinel = .{ .actual = inst_sent, .wanted = dest_sent, @@ -25287,7 +26775,7 @@ fn coerceExtra( } } else { in_memory_result = .{ .ptr_sentinel = .{ - .actual = Value.initTag(.unreachable_value), + .actual = Value.@"unreachable", .wanted = dest_sent, .ty = dst_elem_type, } }; @@ -25313,11 +26801,11 @@ fn coerceExtra( } // coercion from C pointer - if (inst_ty.isCPtr()) src_c_ptr: { + if (inst_ty.isCPtr(mod)) src_c_ptr: { if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr; // In this case we must add a safety check because the C pointer // could be null. - const src_elem_ty = inst_ty.childType(); + const src_elem_ty = inst_ty.childType(mod); const dest_is_mut = dest_info.mutable; const dst_elem_type = dest_info.pointee_type; switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { @@ -25329,17 +26817,18 @@ fn coerceExtra( // cast from *T and [*]T to *anyopaque // but don't do it if the source type is a double pointer - if (dest_info.pointee_type.tag() == .anyopaque and inst_ty.zigTypeTag() == .Pointer) { + if (dest_info.pointee_type.toIntern() == .anyopaque_type and inst_ty.zigTypeTag(mod) == .Pointer) to_anyopaque: { if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; - const elem_ty = inst_ty.elemType2(); - if (elem_ty.zigTypeTag() == .Pointer or elem_ty.isPtrLikeOptional()) { + const elem_ty = inst_ty.elemType2(mod); + if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) { in_memory_result = .{ .double_ptr_to_anyopaque = .{ .actual = inst_ty, .wanted = dest_ty, } }; break :pointer; } - if (inst_ty.isSlice()) { + if (dest_ty.isSlice(mod)) break :to_anyopaque; + if (inst_ty.isSlice(mod)) { in_memory_result = .{ .slice_to_anyopaque = .{ .actual = inst_ty, .wanted = dest_ty, @@ -25351,9 +26840,9 @@ fn coerceExtra( switch (dest_info.size) { // coercion to C pointer - .C => switch (inst_ty.zigTypeTag()) { + .C => switch (inst_ty.zigTypeTag(mod)) { .Null => { - return sema.addConstant(dest_ty, Value.null); + return sema.addConstant(dest_ty, try mod.getCoerced(Value.null, dest_ty)); }, .ComptimeInt => { const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { @@ -25363,7 +26852,7 @@ fn coerceExtra( return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); }, .Int => { - const ptr_size_ty = switch (inst_ty.intInfo(target).signedness) { + const ptr_size_ty = switch (inst_ty.intInfo(mod).signedness) { .signed => Type.isize, .unsigned => Type.usize, }; @@ -25379,7 +26868,7 @@ fn coerceExtra( }, .Pointer => p: { if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; - const inst_info = inst_ty.ptrInfo().data; + const inst_info = inst_ty.ptrInfo(mod); switch (try sema.coerceInMemoryAllowed( block, dest_info.pointee_type, @@ -25395,7 +26884,7 @@ fn coerceExtra( if (inst_info.size == .Slice) { assert(dest_info.sentinel == null); if (inst_info.sentinel == null or - !inst_info.sentinel.?.eql(Value.zero, dest_info.pointee_type, sema.mod)) + !inst_info.sentinel.?.eql(try mod.intValue(dest_info.pointee_type, 0), dest_info.pointee_type, mod)) break :p; const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); @@ -25405,11 +26894,11 @@ fn coerceExtra( }, else => {}, }, - .One => switch (dest_info.pointee_type.zigTypeTag()) { + .One => switch (dest_info.pointee_type.zigTypeTag(mod)) { .Union => { // pointer to anonymous struct to pointer to union - if (inst_ty.isSinglePointer() and - inst_ty.childType().isAnonStruct() and + if (inst_ty.isSinglePointer(mod) and + inst_ty.childType(mod).isAnonStruct(mod) and sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) { return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src); @@ -25417,8 +26906,8 @@ fn coerceExtra( }, .Struct => { // pointer to anonymous struct to pointer to struct - if (inst_ty.isSinglePointer() and - inst_ty.childType().isAnonStruct() and + if (inst_ty.isSinglePointer(mod) and + inst_ty.childType(mod).isAnonStruct(mod) and sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) { return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src) catch |err| switch (err) { @@ -25429,8 +26918,8 @@ fn coerceExtra( }, .Array => { // pointer to tuple to pointer to array - if (inst_ty.isSinglePointer() and - inst_ty.childType().isTuple() and + if (inst_ty.isSinglePointer(mod) and + inst_ty.childType(mod).isTuple(mod) and sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) { return sema.coerceTupleToArrayPtrs(block, dest_ty, dest_ty_src, inst, inst_src); @@ -25439,38 +26928,38 @@ fn coerceExtra( else => {}, }, .Slice => to_slice: { - if (inst_ty.zigTypeTag() == .Array) { + if (inst_ty.zigTypeTag(mod) == .Array) { return sema.fail( block, inst_src, "array literal requires address-of operator (&) to coerce to slice type '{}'", - .{dest_ty.fmt(sema.mod)}, + .{dest_ty.fmt(mod)}, ); } - if (!inst_ty.isSinglePointer()) break :to_slice; - const inst_child_ty = inst_ty.childType(); - if (!inst_child_ty.isTuple()) break :to_slice; + if (!inst_ty.isSinglePointer(mod)) break :to_slice; + const inst_child_ty = inst_ty.childType(mod); + if (!inst_child_ty.isTuple(mod)) break :to_slice; // empty tuple to zero-length slice // note that this allows coercing to a mutable slice. - if (inst_child_ty.structFieldCount() == 0) { + if (inst_child_ty.structFieldCount(mod) == 0) { // Optional slice is represented with a null pointer so // we use a dummy pointer value with the required alignment. - const slice_val = try Value.Tag.slice.create(sema.arena, .{ - .ptr = if (dest_info.@"align" != 0) - try Value.Tag.int_u64.create(sema.arena, dest_info.@"align") + return sema.addConstant(dest_ty, (try mod.intern(.{ .ptr = .{ + .ty = dest_ty.toIntern(), + .addr = .{ .int = (if (dest_info.@"align" != 0) + try mod.intValue(Type.usize, dest_info.@"align") else - try dest_info.pointee_type.lazyAbiAlignment(target, sema.arena), - .len = Value.zero, - }); - return sema.addConstant(dest_ty, slice_val); + try mod.getCoerced(try dest_info.pointee_type.lazyAbiAlignment(mod), Type.usize)).toIntern() }, + .len = (try mod.intValue(Type.usize, 0)).toIntern(), + } })).toValue()); } // pointer to tuple to slice if (dest_info.mutable) { const err_msg = err_msg: { - const err_msg = try sema.errMsg(block, inst_src, "cannot cast pointer to tuple to '{}'", .{dest_ty.fmt(sema.mod)}); + const err_msg = try sema.errMsg(block, inst_src, "cannot cast pointer to tuple to '{}'", .{dest_ty.fmt(mod)}); errdefer err_msg.deinit(sema.gpa); try sema.errNote(block, dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{}); break :err_msg err_msg; @@ -25480,9 +26969,9 @@ fn coerceExtra( return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); }, .Many => p: { - if (!inst_ty.isSlice()) break :p; + if (!inst_ty.isSlice(mod)) break :p; if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; - const inst_info = inst_ty.ptrInfo().data; + const inst_info = inst_ty.ptrInfo(mod); switch (try sema.coerceInMemoryAllowed( block, @@ -25498,7 +26987,11 @@ fn coerceExtra( } if (dest_info.sentinel == null or inst_info.sentinel == null or - !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, sema.mod)) + !dest_info.sentinel.?.eql( + try mod.getCoerced(inst_info.sentinel.?, dest_info.pointee_type), + dest_info.pointee_type, + mod, + )) break :p; const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); @@ -25506,25 +26999,25 @@ fn coerceExtra( }, } }, - .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) { + .Int, .ComptimeInt => switch (inst_ty.zigTypeTag(mod)) { .Float, .ComptimeFloat => float: { if (is_undef) { return sema.addConstUndef(dest_ty); } const val = (try sema.resolveMaybeUndefVal(inst)) orelse { - if (dest_ty.zigTypeTag() == .ComptimeInt) { + if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { if (!opts.report_err) return error.NotCoercible; return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime-known"); } break :float; }; - if (val.floatHasFraction()) { + if (val.floatHasFraction(mod)) { return sema.fail( block, inst_src, "fractional component prevents float value '{}' from coercion to type '{}'", - .{ val.fmtValue(inst_ty, sema.mod), dest_ty.fmt(sema.mod) }, + .{ val.fmtValue(inst_ty, mod), dest_ty.fmt(mod) }, ); } const result_val = try sema.floatToInt(block, inst_src, val, inst_ty, dest_ty); @@ -25538,19 +27031,19 @@ fn coerceExtra( // comptime-known integer to other number if (!(try sema.intFitsInType(val, dest_ty, null))) { if (!opts.report_err) return error.NotCoercible; - return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }); + return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) }); } - return try sema.addConstant(dest_ty, val); + return try sema.addConstant(dest_ty, try mod.getCoerced(val, dest_ty)); } - if (dest_ty.zigTypeTag() == .ComptimeInt) { + if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { if (!opts.report_err) return error.NotCoercible; if (opts.no_cast_to_comptime_int) return inst; return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime-known"); } // integer widening - const dst_info = dest_ty.intInfo(target); - const src_info = inst_ty.intInfo(target); + const dst_info = dest_ty.intInfo(mod); + const src_info = inst_ty.intInfo(mod); if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or // small enough unsigned ints can get casted to large enough signed ints (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) @@ -25564,10 +27057,10 @@ fn coerceExtra( }, else => {}, }, - .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag()) { + .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) { .ComptimeFloat => { const val = try sema.resolveConstValue(block, .unneeded, inst, ""); - const result_val = try val.floatCast(sema.arena, dest_ty, target); + const result_val = try val.floatCast(dest_ty, mod); return try sema.addConstant(dest_ty, result_val); }, .Float => { @@ -25575,17 +27068,17 @@ fn coerceExtra( return sema.addConstUndef(dest_ty); } if (try sema.resolveMaybeUndefVal(inst)) |val| { - const result_val = try val.floatCast(sema.arena, dest_ty, target); - if (!val.eql(result_val, inst_ty, sema.mod)) { + const result_val = try val.floatCast(dest_ty, mod); + if (!val.eql(try result_val.floatCast(inst_ty, mod), inst_ty, mod)) { return sema.fail( block, inst_src, "type '{}' cannot represent float value '{}'", - .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }, + .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) }, ); } return try sema.addConstant(dest_ty, result_val); - } else if (dest_ty.zigTypeTag() == .ComptimeFloat) { + } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { if (!opts.report_err) return error.NotCoercible; return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime-known"); } @@ -25603,13 +27096,13 @@ fn coerceExtra( return sema.addConstUndef(dest_ty); } const val = (try sema.resolveMaybeUndefVal(inst)) orelse { - if (dest_ty.zigTypeTag() == .ComptimeFloat) { + if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { if (!opts.report_err) return error.NotCoercible; return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime-known"); } break :int; }; - const result_val = try val.intToFloatAdvanced(sema.arena, inst_ty, dest_ty, sema.mod, sema); + const result_val = try val.intToFloatAdvanced(sema.arena, inst_ty, dest_ty, mod, sema); // TODO implement this compile error //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty); //if (!int_again_val.eql(val, inst_ty, mod)) { @@ -25617,7 +27110,7 @@ fn coerceExtra( // block, // inst_src, // "type '{}' cannot represent integer value '{}'", - // .{ dest_ty.fmt(sema.mod), val }, + // .{ dest_ty.fmt(mod), val }, // ); //} return try sema.addConstant(dest_ty, result_val); @@ -25627,18 +27120,18 @@ fn coerceExtra( }, else => {}, }, - .Enum => switch (inst_ty.zigTypeTag()) { + .Enum => switch (inst_ty.zigTypeTag(mod)) { .EnumLiteral => { // enum literal to enum const val = try sema.resolveConstValue(block, .unneeded, inst, ""); - const bytes = val.castTag(.enum_literal).?.data; - const field_index = dest_ty.enumFieldIndex(bytes) orelse { + const string = mod.intern_pool.indexToKey(val.toIntern()).enum_literal; + const field_index = dest_ty.enumFieldIndex(string, mod) orelse { const msg = msg: { const msg = try sema.errMsg( block, inst_src, - "no field named '{s}' in enum '{}'", - .{ bytes, dest_ty.fmt(sema.mod) }, + "no field named '{}' in enum '{}'", + .{ string.fmt(&mod.intern_pool), dest_ty.fmt(mod) }, ); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, dest_ty); @@ -25648,13 +27141,13 @@ fn coerceExtra( }; return sema.addConstant( dest_ty, - try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)), + try mod.enumValueFieldIndex(dest_ty, @intCast(u32, field_index)), ); }, .Union => blk: { // union to its own tag type - const union_tag_ty = inst_ty.unionTagType() orelse break :blk; - if (union_tag_ty.eql(dest_ty, sema.mod)) { + const union_tag_ty = inst_ty.unionTagType(mod) orelse break :blk; + if (union_tag_ty.eql(dest_ty, mod)) { return sema.unionToTag(block, dest_ty, inst, inst_src); } }, @@ -25663,27 +27156,33 @@ fn coerceExtra( }, else => {}, }, - .ErrorUnion => switch (inst_ty.zigTypeTag()) { + .ErrorUnion => switch (inst_ty.zigTypeTag(mod)) { .ErrorUnion => eu: { if (maybe_inst_val) |inst_val| { - switch (inst_val.tag()) { + switch (inst_val.toIntern()) { .undef => return sema.addConstUndef(dest_ty), - .eu_payload => { - const payload = try sema.addConstant( - inst_ty.errorUnionPayload(), - inst_val.castTag(.eu_payload).?.data, - ); - return sema.wrapErrorUnionPayload(block, dest_ty, payload, inst_src) catch |err| switch (err) { - error.NotCoercible => break :eu, - else => |e| return e, - }; - }, - else => { - const error_set = try sema.addConstant( - inst_ty.errorUnionSet(), - inst_val, - ); - return sema.wrapErrorUnionSet(block, dest_ty, error_set, inst_src); + else => switch (mod.intern_pool.indexToKey(inst_val.toIntern())) { + .error_union => |error_union| switch (error_union.val) { + .err_name => |err_name| { + const error_set_ty = inst_ty.errorUnionSet(mod); + const error_set_val = try sema.addConstant(error_set_ty, (try mod.intern(.{ .err = .{ + .ty = error_set_ty.toIntern(), + .name = err_name, + } })).toValue()); + return sema.wrapErrorUnionSet(block, dest_ty, error_set_val, inst_src); + }, + .payload => |payload| { + const payload_val = try sema.addConstant( + inst_ty.errorUnionPayload(mod), + payload.toValue(), + ); + return sema.wrapErrorUnionPayload(block, dest_ty, payload_val, inst_src) catch |err| switch (err) { + error.NotCoercible => break :eu, + else => |e| return e, + }; + }, + }, + else => unreachable, }, } } @@ -25703,10 +27202,10 @@ fn coerceExtra( }; }, }, - .Union => switch (inst_ty.zigTypeTag()) { + .Union => switch (inst_ty.zigTypeTag(mod)) { .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), .Struct => { - if (inst_ty.isAnonStruct()) { + if (inst_ty.isAnonStruct(mod)) { return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src); } }, @@ -25715,13 +27214,13 @@ fn coerceExtra( }, else => {}, }, - .Array => switch (inst_ty.zigTypeTag()) { + .Array => switch (inst_ty.zigTypeTag(mod)) { .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), .Struct => { if (inst == .empty_struct) { return sema.arrayInitEmpty(block, inst_src, dest_ty); } - if (inst_ty.isTuple()) { + if (inst_ty.isTuple(mod)) { return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); } }, @@ -25730,10 +27229,10 @@ fn coerceExtra( }, else => {}, }, - .Vector => switch (inst_ty.zigTypeTag()) { + .Vector => switch (inst_ty.zigTypeTag(mod)) { .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), .Struct => { - if (inst_ty.isTuple()) { + if (inst_ty.isTuple(mod)) { return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); } }, @@ -25746,7 +27245,7 @@ fn coerceExtra( if (inst == .empty_struct) { return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src); } - if (inst_ty.isTupleOrAnonStruct()) { + if (inst_ty.isTupleOrAnonStruct(mod)) { return sema.coerceTupleToStruct(block, dest_ty, inst, inst_src) catch |err| switch (err) { error.NotCoercible => break :blk, else => |e| return e, @@ -25765,35 +27264,34 @@ fn coerceExtra( if (!opts.report_err) return error.NotCoercible; - if (opts.is_ret and dest_ty.zigTypeTag() == .NoReturn) { + if (opts.is_ret and dest_ty.zigTypeTag(mod) == .NoReturn) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{}); errdefer msg.destroy(sema.gpa); const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; - const src_decl = sema.mod.declPtr(sema.func.?.owner_decl); - try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "'noreturn' declared here", .{}); + const src_decl = mod.declPtr(sema.func.?.owner_decl); + try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "'noreturn' declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } const msg = msg: { - const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod) }); + const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), inst_ty.fmt(mod) }); errdefer msg.destroy(sema.gpa); // E!T to T - if (inst_ty.zigTypeTag() == .ErrorUnion and - (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) + if (inst_ty.zigTypeTag(mod) == .ErrorUnion and + (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) { try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{}); try sema.errNote(block, inst_src, msg, "consider using 'try', 'catch', or 'if'", .{}); } // ?T to T - var buf: Type.Payload.ElemType = undefined; - if (inst_ty.zigTypeTag() == .Optional and - (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(&buf), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) + if (inst_ty.zigTypeTag(mod) == .Optional and + (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) { try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{}); try sema.errNote(block, inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); @@ -25802,18 +27300,18 @@ fn coerceExtra( try in_memory_result.report(sema, block, inst_src, msg); // Add notes about function return type - if (opts.is_ret and sema.mod.test_functions.get(sema.func.?.owner_decl) == null) { + if (opts.is_ret and mod.test_functions.get(sema.func.?.owner_decl) == null) { const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; - const src_decl = sema.mod.declPtr(sema.func.?.owner_decl); - if (inst_ty.isError() and !dest_ty.isError()) { - try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "function cannot return an error", .{}); + const src_decl = mod.declPtr(sema.func.?.owner_decl); + if (inst_ty.isError(mod) and !dest_ty.isError(mod)) { + try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function cannot return an error", .{}); } else { - try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "function return type declared here", .{}); + try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function return type declared here", .{}); } } if (try opts.param_src.get(sema)) |param_src| { - try sema.mod.errNoteNonLazy(param_src, msg, "parameter type declared here", .{}); + try mod.errNoteNonLazy(param_src, msg, "parameter type declared here", .{}); } // TODO maybe add "cannot store an error in type '{}'" note @@ -25823,6 +27321,14 @@ fn coerceExtra( return sema.failWithOwnedErrorMsg(msg); } +fn coerceInMemory( + sema: *Sema, + val: Value, + dst_ty: Type, +) CompileError!Air.Inst.Ref { + return sema.addConstant(dst_ty, try sema.mod.getCoerced(val, dst_ty)); +} + const InMemoryCoercionResult = union(enum) { ok, no_match: Pair, @@ -25836,7 +27342,7 @@ const InMemoryCoercionResult = union(enum) { optional_shape: Pair, optional_child: PairAndChild, from_anyerror, - missing_error: []const []const u8, + missing_error: []const InternPool.NullTerminatedString, /// true if wanted is var args fn_var_args: bool, /// true if wanted is generic @@ -25936,6 +27442,7 @@ const InMemoryCoercionResult = union(enum) { } fn report(res: *const InMemoryCoercionResult, sema: *Sema, block: *Block, src: LazySrcLoc, msg: *Module.ErrorMsg) !void { + const mod = sema.mod; var cur = res; while (true) switch (cur.*) { .ok => unreachable, @@ -25952,7 +27459,7 @@ const InMemoryCoercionResult = union(enum) { }, .error_union_payload => |pair| { try sema.errNote(block, src, msg, "error union payload '{}' cannot cast into error union payload '{}'", .{ - pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod), + pair.actual.fmt(mod), pair.wanted.fmt(mod), }); cur = pair.child; }, @@ -25963,20 +27470,20 @@ const InMemoryCoercionResult = union(enum) { break; }, .array_sentinel => |sentinel| { - if (sentinel.actual.tag() != .unreachable_value) { + if (sentinel.actual.toIntern() != .unreachable_value) { try sema.errNote(block, src, msg, "array sentinel '{}' cannot cast into array sentinel '{}'", .{ - sentinel.actual.fmtValue(sentinel.ty, sema.mod), sentinel.wanted.fmtValue(sentinel.ty, sema.mod), + sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod), }); } else { try sema.errNote(block, src, msg, "destination array requires '{}' sentinel", .{ - sentinel.wanted.fmtValue(sentinel.ty, sema.mod), + sentinel.wanted.fmtValue(sentinel.ty, mod), }); } break; }, .array_elem => |pair| { try sema.errNote(block, src, msg, "array element type '{}' cannot cast into array element type '{}'", .{ - pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod), + pair.actual.fmt(mod), pair.wanted.fmt(mod), }); cur = pair.child; }, @@ -25988,21 +27495,19 @@ const InMemoryCoercionResult = union(enum) { }, .vector_elem => |pair| { try sema.errNote(block, src, msg, "vector element type '{}' cannot cast into vector element type '{}'", .{ - pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod), + pair.actual.fmt(mod), pair.wanted.fmt(mod), }); cur = pair.child; }, .optional_shape => |pair| { - var buf_actual: Type.Payload.ElemType = undefined; - var buf_wanted: Type.Payload.ElemType = undefined; try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{ - pair.actual.optionalChild(&buf_actual).fmt(sema.mod), pair.wanted.optionalChild(&buf_wanted).fmt(sema.mod), + pair.actual.optionalChild(mod).fmt(mod), pair.wanted.optionalChild(mod).fmt(mod), }); break; }, .optional_child => |pair| { try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{ - pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod), + pair.actual.fmt(mod), pair.wanted.fmt(mod), }); cur = pair.child; }, @@ -26012,7 +27517,7 @@ const InMemoryCoercionResult = union(enum) { }, .missing_error => |missing_errors| { for (missing_errors) |err| { - try sema.errNote(block, src, msg, "'error.{s}' not a member of destination error set", .{err}); + try sema.errNote(block, src, msg, "'error.{}' not a member of destination error set", .{err.fmt(&mod.intern_pool)}); } break; }, @@ -26066,7 +27571,7 @@ const InMemoryCoercionResult = union(enum) { }, .fn_param => |param| { try sema.errNote(block, src, msg, "parameter {d} '{}' cannot cast into '{}'", .{ - param.index, param.actual.fmt(sema.mod), param.wanted.fmt(sema.mod), + param.index, param.actual.fmt(mod), param.wanted.fmt(mod), }); cur = param.child; }, @@ -26076,13 +27581,13 @@ const InMemoryCoercionResult = union(enum) { }, .fn_return_type => |pair| { try sema.errNote(block, src, msg, "return type '{}' cannot cast into return type '{}'", .{ - pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod), + pair.actual.fmt(mod), pair.wanted.fmt(mod), }); cur = pair.child; }, .ptr_child => |pair| { try sema.errNote(block, src, msg, "pointer type child '{}' cannot cast into pointer type child '{}'", .{ - pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod), + pair.actual.fmt(mod), pair.wanted.fmt(mod), }); cur = pair.child; }, @@ -26091,13 +27596,13 @@ const InMemoryCoercionResult = union(enum) { break; }, .ptr_sentinel => |sentinel| { - if (sentinel.actual.tag() != .unreachable_value) { + if (sentinel.actual.toIntern() != .unreachable_value) { try sema.errNote(block, src, msg, "pointer sentinel '{}' cannot cast into pointer sentinel '{}'", .{ - sentinel.actual.fmtValue(sentinel.ty, sema.mod), sentinel.wanted.fmtValue(sentinel.ty, sema.mod), + sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod), }); } else { try sema.errNote(block, src, msg, "destination pointer requires '{}' sentinel", .{ - sentinel.wanted.fmtValue(sentinel.ty, sema.mod), + sentinel.wanted.fmtValue(sentinel.ty, mod), }); } break; @@ -26117,15 +27622,15 @@ const InMemoryCoercionResult = union(enum) { break; }, .ptr_allowzero => |pair| { - const wanted_allow_zero = pair.wanted.ptrAllowsZero(); - const actual_allow_zero = pair.actual.ptrAllowsZero(); + const wanted_allow_zero = pair.wanted.ptrAllowsZero(mod); + const actual_allow_zero = pair.actual.ptrAllowsZero(mod); if (actual_allow_zero and !wanted_allow_zero) { try sema.errNote(block, src, msg, "'{}' could have null values which are illegal in type '{}'", .{ - pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod), + pair.actual.fmt(mod), pair.wanted.fmt(mod), }); } else { try sema.errNote(block, src, msg, "mutable '{}' allows illegal null values stored to type '{}'", .{ - pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod), + pair.actual.fmt(mod), pair.wanted.fmt(mod), }); } break; @@ -26151,13 +27656,13 @@ const InMemoryCoercionResult = union(enum) { }, .double_ptr_to_anyopaque => |pair| { try sema.errNote(block, src, msg, "cannot implicitly cast double pointer '{}' to anyopaque pointer '{}'", .{ - pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod), + pair.actual.fmt(mod), pair.wanted.fmt(mod), }); break; }, .slice_to_anyopaque => |pair| { try sema.errNote(block, src, msg, "cannot implicitly cast slice '{}' to anyopaque pointer '{}'", .{ - pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod), + pair.actual.fmt(mod), pair.wanted.fmt(mod), }); try sema.errNote(block, src, msg, "consider using '.ptr'", .{}); break; @@ -26194,13 +27699,18 @@ fn coerceInMemoryAllowed( dest_src: LazySrcLoc, src_src: LazySrcLoc, ) CompileError!InMemoryCoercionResult { - if (dest_ty.eql(src_ty, sema.mod)) + const mod = sema.mod; + + if (dest_ty.eql(src_ty, mod)) return .ok; + const dest_tag = dest_ty.zigTypeTag(mod); + const src_tag = src_ty.zigTypeTag(mod); + // Differently-named integers with the same number of bits. - if (dest_ty.zigTypeTag() == .Int and src_ty.zigTypeTag() == .Int) { - const dest_info = dest_ty.intInfo(target); - const src_info = src_ty.intInfo(target); + if (dest_tag == .Int and src_tag == .Int) { + const dest_info = dest_ty.intInfo(mod); + const src_info = src_ty.intInfo(mod); if (dest_info.signedness == src_info.signedness and dest_info.bits == src_info.bits) @@ -26223,7 +27733,7 @@ fn coerceInMemoryAllowed( } // Differently-named floats with the same number of bits. - if (dest_ty.zigTypeTag() == .Float and src_ty.zigTypeTag() == .Float) { + if (dest_tag == .Float and src_tag == .Float) { const dest_bits = dest_ty.floatBits(target); const src_bits = src_ty.floatBits(target); if (dest_bits == src_bits) { @@ -26232,10 +27742,8 @@ fn coerceInMemoryAllowed( } // Pointers / Pointer-like Optionals - var dest_buf: Type.Payload.ElemType = undefined; - var src_buf: Type.Payload.ElemType = undefined; - const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty, &dest_buf); - const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty, &src_buf); + 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); @@ -26243,13 +27751,10 @@ fn coerceInMemoryAllowed( } // Slices - if (dest_ty.isSlice() and src_ty.isSlice()) { + if (dest_ty.isSlice(mod) and src_ty.isSlice(mod)) { return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); } - const dest_tag = dest_ty.zigTypeTag(); - const src_tag = src_ty.zigTypeTag(); - // Functions if (dest_tag == .Fn and src_tag == .Fn) { return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, target, dest_src, src_src); @@ -26257,8 +27762,8 @@ fn coerceInMemoryAllowed( // Error Unions if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) { - const dest_payload = dest_ty.errorUnionPayload(); - const src_payload = src_ty.errorUnionPayload(); + const dest_payload = dest_ty.errorUnionPayload(mod); + const src_payload = src_ty.errorUnionPayload(mod); const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src); if (child != .ok) { return InMemoryCoercionResult{ .error_union_payload = .{ @@ -26267,7 +27772,7 @@ fn coerceInMemoryAllowed( .wanted = dest_payload, } }; } - return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(), src_ty.errorUnionSet(), dest_is_mut, target, dest_src, src_src); + return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(mod), src_ty.errorUnionSet(mod), dest_is_mut, target, dest_src, src_src); } // Error Sets @@ -26277,8 +27782,8 @@ fn coerceInMemoryAllowed( // Arrays if (dest_tag == .Array and src_tag == .Array) { - const dest_info = dest_ty.arrayInfo(); - const src_info = src_ty.arrayInfo(); + const dest_info = dest_ty.arrayInfo(mod); + const src_info = src_ty.arrayInfo(mod); if (dest_info.len != src_info.len) { return InMemoryCoercionResult{ .array_len = .{ .actual = src_info.len, @@ -26296,11 +27801,15 @@ fn coerceInMemoryAllowed( } const ok_sent = dest_info.sentinel == null or (src_info.sentinel != null and - dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type, sema.mod)); + dest_info.sentinel.?.eql( + try mod.getCoerced(src_info.sentinel.?, dest_info.elem_type), + dest_info.elem_type, + mod, + )); if (!ok_sent) { return InMemoryCoercionResult{ .array_sentinel = .{ - .actual = src_info.sentinel orelse Value.initTag(.unreachable_value), - .wanted = dest_info.sentinel orelse Value.initTag(.unreachable_value), + .actual = src_info.sentinel orelse Value.@"unreachable", + .wanted = dest_info.sentinel orelse Value.@"unreachable", .ty = dest_info.elem_type, } }; } @@ -26309,8 +27818,8 @@ fn coerceInMemoryAllowed( // Vectors if (dest_tag == .Vector and src_tag == .Vector) { - const dest_len = dest_ty.vectorLen(); - const src_len = src_ty.vectorLen(); + const dest_len = dest_ty.vectorLen(mod); + const src_len = src_ty.vectorLen(mod); if (dest_len != src_len) { return InMemoryCoercionResult{ .vector_len = .{ .actual = src_len, @@ -26318,8 +27827,8 @@ fn coerceInMemoryAllowed( } }; } - const dest_elem_ty = dest_ty.scalarType(); - const src_elem_ty = src_ty.scalarType(); + const dest_elem_ty = dest_ty.scalarType(mod); + const src_elem_ty = src_ty.scalarType(mod); const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src); if (child != .ok) { return InMemoryCoercionResult{ .vector_elem = .{ @@ -26340,21 +27849,37 @@ fn coerceInMemoryAllowed( .wanted = dest_ty, } }; } - const dest_child_type = dest_ty.optionalChild(&dest_buf); - const src_child_type = src_ty.optionalChild(&src_buf); + const dest_child_type = dest_ty.optionalChild(mod); + const src_child_type = src_ty.optionalChild(mod); const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src); if (child != .ok) { return InMemoryCoercionResult{ .optional_child = .{ .child = try child.dupe(sema.arena), - .actual = try src_child_type.copy(sema.arena), - .wanted = try dest_child_type.copy(sema.arena), + .actual = src_child_type, + .wanted = dest_child_type, } }; } return .ok; } + // Tuples (with in-memory-coercible fields) + if (dest_ty.isTuple(mod) and src_ty.isTuple(mod)) tuple: { + if (dest_ty.containerLayout(mod) != src_ty.containerLayout(mod)) break :tuple; + if (dest_ty.structFieldCount(mod) != src_ty.structFieldCount(mod)) break :tuple; + const field_count = dest_ty.structFieldCount(mod); + for (0..field_count) |field_idx| { + if (dest_ty.structFieldIsComptime(field_idx, mod) != src_ty.structFieldIsComptime(field_idx, mod)) break :tuple; + if (dest_ty.structFieldAlign(field_idx, mod) != src_ty.structFieldAlign(field_idx, mod)) break :tuple; + const dest_field_ty = dest_ty.structFieldType(field_idx, mod); + const src_field_ty = src_ty.structFieldType(field_idx, mod); + const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src); + if (field != .ok) break :tuple; + } + return .ok; + } + return InMemoryCoercionResult{ .no_match = .{ .actual = dest_ty, .wanted = src_ty, @@ -26369,141 +27894,108 @@ fn coerceInMemoryAllowedErrorSets( dest_src: LazySrcLoc, src_src: LazySrcLoc, ) !InMemoryCoercionResult { + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; + // Coercion to `anyerror`. Note that this check can return false negatives // in case the error sets did not get resolved. - if (dest_ty.isAnyError()) { + if (dest_ty.isAnyError(mod)) { return .ok; } - if (dest_ty.castTag(.error_set_inferred)) |dst_payload| { - const dst_ies = dst_payload.data; + if (mod.typeToInferredErrorSetIndex(dest_ty).unwrap()) |dst_ies_index| { + const dst_ies = mod.inferredErrorSetPtr(dst_ies_index); // We will make an effort to return `ok` without resolving either error set, to // avoid unnecessary "unable to resolve error set" dependency loop errors. - switch (src_ty.tag()) { - .error_set_inferred => { - // If both are inferred error sets of functions, and - // the dest includes the source function, the coercion is OK. - // This check is important because it works without forcing a full resolution - // of inferred error sets. - const src_ies = src_ty.castTag(.error_set_inferred).?.data; - - if (dst_ies.inferred_error_sets.contains(src_ies)) { - return .ok; - } - }, - .error_set_single => { - const name = src_ty.castTag(.error_set_single).?.data; - if (dst_ies.errors.contains(name)) return .ok; - }, - .error_set_merged => { - const names = src_ty.castTag(.error_set_merged).?.data.keys(); - for (names) |name| { - if (!dst_ies.errors.contains(name)) break; - } else return .ok; - }, - .error_set => { - const names = src_ty.castTag(.error_set).?.data.names.keys(); - for (names) |name| { - if (!dst_ies.errors.contains(name)) break; - } else return .ok; - }, - .anyerror => {}, - else => unreachable, + switch (src_ty.toIntern()) { + .anyerror_type => {}, + else => switch (ip.indexToKey(src_ty.toIntern())) { + .inferred_error_set_type => |src_index| { + // If both are inferred error sets of functions, and + // the dest includes the source function, the coercion is OK. + // This check is important because it works without forcing a full resolution + // of inferred error sets. + if (dst_ies.inferred_error_sets.contains(src_index)) { + return .ok; + } + }, + .error_set_type => |error_set_type| { + for (error_set_type.names) |name| { + if (!dst_ies.errors.contains(name)) break; + } else return .ok; + }, + else => unreachable, + }, } - if (dst_ies.func == sema.owner_func) { + if (dst_ies.func == sema.owner_func_index.unwrap()) { // We are trying to coerce an error set to the current function's // inferred error set. - try dst_ies.addErrorSet(sema.gpa, src_ty); + try dst_ies.addErrorSet(src_ty, ip, gpa); return .ok; } - try sema.resolveInferredErrorSet(block, dest_src, dst_payload.data); + try sema.resolveInferredErrorSet(block, dest_src, dst_ies_index); // isAnyError might have changed from a false negative to a true positive after resolution. - if (dest_ty.isAnyError()) { + if (dest_ty.isAnyError(mod)) { return .ok; } } - var missing_error_buf = std.ArrayList([]const u8).init(sema.gpa); + var missing_error_buf = std.ArrayList(InternPool.NullTerminatedString).init(gpa); defer missing_error_buf.deinit(); - switch (src_ty.tag()) { - .error_set_inferred => { - const src_data = src_ty.castTag(.error_set_inferred).?.data; + switch (src_ty.toIntern()) { + .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { + .simple_type => unreachable, // filtered out above + .error_set_type, .inferred_error_set_type => return .from_anyerror, + else => unreachable, + }, - try sema.resolveInferredErrorSet(block, src_src, src_data); - // src anyerror status might have changed after the resolution. - if (src_ty.isAnyError()) { - // dest_ty.isAnyError() == true is already checked for at this point. - return .from_anyerror; - } + else => switch (ip.indexToKey(src_ty.toIntern())) { + .inferred_error_set_type => |src_index| { + const src_data = mod.inferredErrorSetPtr(src_index); - for (src_data.errors.keys()) |key| { - if (!dest_ty.errorSetHasField(key)) { - try missing_error_buf.append(key); + try sema.resolveInferredErrorSet(block, src_src, src_index); + // src anyerror status might have changed after the resolution. + if (src_ty.isAnyError(mod)) { + // dest_ty.isAnyError(mod) == true is already checked for at this point. + return .from_anyerror; } - } - if (missing_error_buf.items.len != 0) { - return InMemoryCoercionResult{ - .missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items), - }; - } - - return .ok; - }, - .error_set_single => { - const name = src_ty.castTag(.error_set_single).?.data; - if (dest_ty.errorSetHasField(name)) { - return .ok; - } - const list = try sema.arena.alloc([]const u8, 1); - list[0] = name; - return InMemoryCoercionResult{ .missing_error = list }; - }, - .error_set_merged => { - const names = src_ty.castTag(.error_set_merged).?.data.keys(); - for (names) |name| { - if (!dest_ty.errorSetHasField(name)) { - try missing_error_buf.append(name); + for (src_data.errors.keys()) |key| { + if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) { + try missing_error_buf.append(key); + } } - } - if (missing_error_buf.items.len != 0) { - return InMemoryCoercionResult{ - .missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items), - }; - } + if (missing_error_buf.items.len != 0) { + return InMemoryCoercionResult{ + .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), + }; + } - return .ok; - }, - .error_set => { - const names = src_ty.castTag(.error_set).?.data.names.keys(); - for (names) |name| { - if (!dest_ty.errorSetHasField(name)) { - try missing_error_buf.append(name); + return .ok; + }, + .error_set_type => |error_set_type| { + for (error_set_type.names) |name| { + if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) { + try missing_error_buf.append(name); + } } - } - if (missing_error_buf.items.len != 0) { - return InMemoryCoercionResult{ - .missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items), - }; - } + if (missing_error_buf.items.len != 0) { + return InMemoryCoercionResult{ + .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), + }; + } - return .ok; - }, - .anyerror => switch (dest_ty.tag()) { - .error_set_inferred => unreachable, // Caught by dest_ty.isAnyError() above. - .error_set_single, .error_set_merged, .error_set => return .from_anyerror, - .anyerror => unreachable, // Filtered out above. + return .ok; + }, else => unreachable, }, - else => unreachable, } - - unreachable; } fn coerceInMemoryAllowedFns( @@ -26515,69 +28007,95 @@ fn coerceInMemoryAllowedFns( dest_src: LazySrcLoc, src_src: LazySrcLoc, ) !InMemoryCoercionResult { - const dest_info = dest_ty.fnInfo(); - const src_info = src_ty.fnInfo(); + const mod = sema.mod; - if (dest_info.is_var_args != src_info.is_var_args) { - return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; - } + { + const dest_info = mod.typeToFunc(dest_ty).?; + const src_info = mod.typeToFunc(src_ty).?; - if (dest_info.is_generic != src_info.is_generic) { - return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic }; - } + if (dest_info.is_var_args != src_info.is_var_args) { + return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; + } - if (dest_info.cc != src_info.cc) { - return InMemoryCoercionResult{ .fn_cc = .{ - .actual = src_info.cc, - .wanted = dest_info.cc, - } }; - } + if (dest_info.is_generic != src_info.is_generic) { + return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic }; + } - if (!src_info.return_type.isNoReturn()) { - const rt = try sema.coerceInMemoryAllowed(block, dest_info.return_type, src_info.return_type, false, target, dest_src, src_src); - if (rt != .ok) { - return InMemoryCoercionResult{ .fn_return_type = .{ - .child = try rt.dupe(sema.arena), - .actual = src_info.return_type, - .wanted = dest_info.return_type, + if (dest_info.cc != src_info.cc) { + return InMemoryCoercionResult{ .fn_cc = .{ + .actual = src_info.cc, + .wanted = dest_info.cc, } }; } - } - if (dest_info.param_types.len != src_info.param_types.len) { - return InMemoryCoercionResult{ .fn_param_count = .{ - .actual = src_info.param_types.len, - .wanted = dest_info.param_types.len, - } }; + switch (src_info.return_type) { + .noreturn_type, .generic_poison_type => {}, + else => { + const dest_return_type = dest_info.return_type.toType(); + const src_return_type = src_info.return_type.toType(); + const rt = try sema.coerceInMemoryAllowed(block, dest_return_type, src_return_type, false, target, dest_src, src_src); + if (rt != .ok) { + return InMemoryCoercionResult{ .fn_return_type = .{ + .child = try rt.dupe(sema.arena), + .actual = src_return_type, + .wanted = dest_return_type, + } }; + } + }, + } } - if (dest_info.noalias_bits != src_info.noalias_bits) { - return InMemoryCoercionResult{ .fn_param_noalias = .{ - .actual = src_info.noalias_bits, - .wanted = dest_info.noalias_bits, - } }; - } + const params_len = params_len: { + const dest_info = mod.typeToFunc(dest_ty).?; + const src_info = mod.typeToFunc(src_ty).?; - for (dest_info.param_types, 0..) |dest_param_ty, i| { - const src_param_ty = src_info.param_types[i]; + if (dest_info.param_types.len != src_info.param_types.len) { + return InMemoryCoercionResult{ .fn_param_count = .{ + .actual = src_info.param_types.len, + .wanted = dest_info.param_types.len, + } }; + } - if (dest_info.comptime_params[i] != src_info.comptime_params[i]) { - return InMemoryCoercionResult{ .fn_param_comptime = .{ - .index = i, - .wanted = dest_info.comptime_params[i], + if (dest_info.noalias_bits != src_info.noalias_bits) { + return InMemoryCoercionResult{ .fn_param_noalias = .{ + .actual = src_info.noalias_bits, + .wanted = dest_info.noalias_bits, } }; } - // Note: Cast direction is reversed here. - const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src); - if (param != .ok) { - return InMemoryCoercionResult{ .fn_param = .{ - .child = try param.dupe(sema.arena), - .actual = src_param_ty, - .wanted = dest_param_ty, - .index = i, + break :params_len dest_info.param_types.len; + }; + + for (0..params_len) |param_i| { + const dest_info = mod.typeToFunc(dest_ty).?; + const src_info = mod.typeToFunc(src_ty).?; + + const dest_param_ty = dest_info.param_types[param_i].toType(); + const src_param_ty = src_info.param_types[param_i].toType(); + + const param_i_small = @intCast(u5, param_i); + if (dest_info.paramIsComptime(param_i_small) != src_info.paramIsComptime(param_i_small)) { + return InMemoryCoercionResult{ .fn_param_comptime = .{ + .index = param_i, + .wanted = dest_info.paramIsComptime(param_i_small), } }; } + + switch (src_param_ty.toIntern()) { + .generic_poison_type => {}, + else => { + // Note: Cast direction is reversed here. + const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src); + if (param != .ok) { + return InMemoryCoercionResult{ .fn_param = .{ + .child = try param.dupe(sema.arena), + .actual = src_param_ty, + .wanted = dest_param_ty, + .index = param_i, + } }; + } + }, + } } return .ok; @@ -26595,8 +28113,9 @@ fn coerceInMemoryAllowedPtrs( dest_src: LazySrcLoc, src_src: LazySrcLoc, ) !InMemoryCoercionResult { - const dest_info = dest_ptr_ty.ptrInfo().data; - const src_info = src_ptr_ty.ptrInfo().data; + const mod = sema.mod; + const dest_info = dest_ptr_ty.ptrInfo(mod); + const src_info = src_ptr_ty.ptrInfo(mod); const ok_ptr_size = src_info.size == dest_info.size or src_info.size == .C or dest_info.size == .C; @@ -26636,8 +28155,8 @@ fn coerceInMemoryAllowedPtrs( } }; } - const dest_allow_zero = dest_ty.ptrAllowsZero(); - const src_allow_zero = src_ty.ptrAllowsZero(); + const dest_allow_zero = dest_ty.ptrAllowsZero(mod); + const src_allow_zero = src_ty.ptrAllowsZero(mod); const ok_allows_zero = (dest_allow_zero and (src_allow_zero or !dest_is_mut)) or @@ -26661,12 +28180,15 @@ fn coerceInMemoryAllowedPtrs( } const ok_sent = dest_info.sentinel == null or src_info.size == .C or - (src_info.sentinel != null and - dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type, sema.mod)); + (src_info.sentinel != null and dest_info.sentinel.?.eql( + try mod.getCoerced(src_info.sentinel.?, dest_info.pointee_type), + dest_info.pointee_type, + sema.mod, + )); if (!ok_sent) { return InMemoryCoercionResult{ .ptr_sentinel = .{ - .actual = src_info.sentinel orelse Value.initTag(.unreachable_value), - .wanted = dest_info.sentinel orelse Value.initTag(.unreachable_value), + .actual = src_info.sentinel orelse Value.@"unreachable", + .wanted = dest_info.sentinel orelse Value.@"unreachable", .ty = dest_info.pointee_type, } }; } @@ -26685,12 +28207,12 @@ fn coerceInMemoryAllowedPtrs( const src_align = if (src_info.@"align" != 0) src_info.@"align" else - src_info.pointee_type.abiAlignment(target); + src_info.pointee_type.abiAlignment(mod); const dest_align = if (dest_info.@"align" != 0) dest_info.@"align" else - dest_info.pointee_type.abiAlignment(target); + dest_info.pointee_type.abiAlignment(mod); if (dest_align > src_align) { return InMemoryCoercionResult{ .ptr_alignment = .{ @@ -26713,8 +28235,9 @@ fn coerceVarArgParam( ) !Air.Inst.Ref { if (block.is_typeof) return inst; + const mod = sema.mod; const uncasted_ty = sema.typeOf(inst); - const coerced = switch (uncasted_ty.zigTypeTag()) { + const coerced = switch (uncasted_ty.zigTypeTag(mod)) { // TODO consider casting to c_int/f64 if they fit .ComptimeInt, .ComptimeFloat => return sema.fail( block, @@ -26724,7 +28247,7 @@ fn coerceVarArgParam( ), .Fn => blk: { const fn_val = try sema.resolveConstValue(block, .unneeded, inst, ""); - const fn_decl = fn_val.pointerDecl().?; + const fn_decl = fn_val.pointerDecl(mod).?; break :blk try sema.analyzeDeclRef(fn_decl); }, .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), @@ -26749,7 +28272,7 @@ fn coerceVarArgParam( errdefer msg.destroy(sema.gpa); const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl), coerced_ty, .param_ty); + try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl, mod), coerced_ty, .param_ty); try sema.addDeclaredHereNote(msg, coerced_ty); break :msg msg; @@ -26781,11 +28304,12 @@ fn storePtr2( operand_src: LazySrcLoc, air_tag: Air.Inst.Tag, ) CompileError!void { + const mod = sema.mod; const ptr_ty = sema.typeOf(ptr); - if (ptr_ty.isConstPtr()) + if (ptr_ty.isConstPtr(mod)) return sema.fail(block, ptr_src, "cannot assign to constant", .{}); - const elem_ty = ptr_ty.childType(); + const elem_ty = ptr_ty.childType(mod); // To generate better code for tuples, we detect a tuple operand here, and // analyze field loads and stores directly. This avoids an extra allocation + memcpy @@ -26796,8 +28320,8 @@ fn storePtr2( // this code does not handle tuple-to-struct coercion which requires dealing with missing // fields. const operand_ty = sema.typeOf(uncasted_operand); - if (operand_ty.isTuple() and elem_ty.zigTypeTag() == .Array) { - const field_count = operand_ty.structFieldCount(); + if (operand_ty.isTuple(mod) and elem_ty.zigTypeTag(mod) == .Array) { + const field_count = operand_ty.structFieldCount(mod); var i: u32 = 0; while (i < field_count) : (i += 1) { const elem_src = operand_src; // TODO better source location @@ -26821,7 +28345,7 @@ fn storePtr2( // as well as working around an LLVM bug: // https://github.com/ziglang/zig/issues/11154 if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| { - const vector_ty = sema.typeOf(vector_ptr).childType(); + const vector_ty = sema.typeOf(vector_ptr).childType(mod); const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { error.NotCoercible => unreachable, else => |e| return e, @@ -26841,7 +28365,7 @@ fn storePtr2( try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); break :rs operand_src; }; - if (ptr_val.isComptimeMutablePtr()) { + if (ptr_val.isComptimeMutablePtr(mod)) { try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); return; } else break :rs ptr_src; @@ -26862,7 +28386,7 @@ fn storePtr2( try sema.requireRuntimeBlock(block, src, runtime_src); try sema.queueFullTypeResolution(elem_ty); - if (ptr_ty.ptrInfo().data.vector_index == .runtime) { + if (ptr_ty.ptrInfo(mod).vector_index == .runtime) { const ptr_inst = Air.refToIndex(ptr).?; const air_tags = sema.air_instructions.items(.tag); if (air_tags[ptr_inst] == .ptr_elem_ptr) { @@ -26896,30 +28420,27 @@ fn storePtr2( /// pointer. Only if the final element type matches the vector element type, and the /// lengths match. fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref { - const array_ty = sema.typeOf(ptr).childType(); - if (array_ty.zigTypeTag() != .Array) return null; - var ptr_inst = Air.refToIndex(ptr) orelse return null; + const mod = sema.mod; + const array_ty = sema.typeOf(ptr).childType(mod); + if (array_ty.zigTypeTag(mod) != .Array) return null; + var ptr_ref = ptr; + var ptr_inst = Air.refToIndex(ptr_ref) orelse return null; const air_datas = sema.air_instructions.items(.data); const air_tags = sema.air_instructions.items(.tag); - const prev_ptr = while (air_tags[ptr_inst] == .bitcast) { - const prev_ptr = air_datas[ptr_inst].ty_op.operand; - const prev_ptr_ty = sema.typeOf(prev_ptr); - const prev_ptr_child_ty = switch (prev_ptr_ty.tag()) { - .single_mut_pointer => prev_ptr_ty.castTag(.single_mut_pointer).?.data, - .pointer => prev_ptr_ty.castTag(.pointer).?.data.pointee_type, - else => return null, - }; - if (prev_ptr_child_ty.zigTypeTag() == .Vector) break prev_ptr; - ptr_inst = Air.refToIndex(prev_ptr) orelse return null; + const vector_ty = while (air_tags[ptr_inst] == .bitcast) { + ptr_ref = air_datas[ptr_inst].ty_op.operand; + if (!sema.isKnownZigType(ptr_ref, .Pointer)) return null; + const child_ty = sema.typeOf(ptr_ref).childType(mod); + if (child_ty.zigTypeTag(mod) == .Vector) break child_ty; + ptr_inst = Air.refToIndex(ptr_ref) orelse return null; } else return null; // We have a pointer-to-array and a pointer-to-vector. If the elements and // lengths match, return the result. - const vector_ty = sema.typeOf(prev_ptr).childType(); - if (array_ty.childType().eql(vector_ty.childType(), sema.mod) and - array_ty.arrayLen() == vector_ty.vectorLen()) + if (array_ty.childType(mod).eql(vector_ty.childType(mod), sema.mod) and + array_ty.arrayLen(mod) == vector_ty.vectorLen(mod)) { - return prev_ptr; + return ptr_ref; } else { return null; } @@ -26935,54 +28456,55 @@ fn storePtrVal( operand_val: Value, operand_ty: Type, ) !void { + const mod = sema.mod; var mut_kit = try sema.beginComptimePtrMutation(block, src, ptr_val, operand_ty); - try sema.checkComptimeVarStore(block, src, mut_kit.decl_ref_mut); + try sema.checkComptimeVarStore(block, src, mut_kit.mut_decl); switch (mut_kit.pointee) { .direct => |val_ptr| { - if (mut_kit.decl_ref_mut.runtime_index == .comptime_field_ptr) { - if (!operand_val.eql(val_ptr.*, operand_ty, sema.mod)) { + if (mut_kit.mut_decl.runtime_index == .comptime_field_ptr) { + if (!operand_val.eql(val_ptr.*, operand_ty, mod)) { // TODO use failWithInvalidComptimeFieldStore return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{}); } return; } - const arena = mut_kit.beginArena(sema.mod); - defer mut_kit.finishArena(sema.mod); - - val_ptr.* = try operand_val.copy(arena); + val_ptr.* = (try operand_val.intern(operand_ty, mod)).toValue(); }, .reinterpret => |reinterpret| { - const target = sema.mod.getTarget(); - const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(target)); + const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(mod)); const buffer = try sema.gpa.alloc(u8, abi_size); defer sema.gpa.free(buffer); - reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, sema.mod, buffer) catch |err| switch (err) { + reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, mod, buffer) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, error.ReinterpretDeclRef => unreachable, error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already - error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(sema.mod)}), + error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), }; - operand_val.writeToMemory(operand_ty, sema.mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) { + operand_val.writeToMemory(operand_ty, mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, error.ReinterpretDeclRef => unreachable, error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already - error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(sema.mod)}), + error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), }; - const arena = mut_kit.beginArena(sema.mod); - defer mut_kit.finishArena(sema.mod); - - reinterpret.val_ptr.* = try Value.readFromMemory(mut_kit.ty, sema.mod, buffer, arena); + reinterpret.val_ptr.* = (try (try Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena)).intern(mut_kit.ty, mod)).toValue(); }, .bad_decl_ty, .bad_ptr_ty => { // TODO show the decl declaration site in a note and explain whether the decl // or the pointer is the problematic type - return sema.fail(block, src, "comptime mutation of a reinterpreted pointer requires type '{}' to have a well-defined memory layout", .{mut_kit.ty.fmt(sema.mod)}); + return sema.fail( + block, + src, + "comptime mutation of a reinterpreted pointer requires type '{}' to have a well-defined memory layout", + .{mut_kit.ty.fmt(mod)}, + ); }, } } const ComptimePtrMutationKit = struct { - decl_ref_mut: Value.Payload.DeclRefMut.Data, + mut_decl: InternPool.Key.Ptr.Addr.MutDecl, pointee: union(enum) { /// The pointer type matches the actual comptime Value so a direct /// modification is possible. @@ -27005,18 +28527,6 @@ const ComptimePtrMutationKit = struct { bad_ptr_ty, }, ty: Type, - decl_arena: std.heap.ArenaAllocator = undefined, - - fn beginArena(self: *ComptimePtrMutationKit, mod: *Module) Allocator { - const decl = mod.declPtr(self.decl_ref_mut.decl_index); - return decl.value_arena.?.acquire(mod.gpa, &self.decl_arena); - } - - fn finishArena(self: *ComptimePtrMutationKit, mod: *Module) void { - const decl = mod.declPtr(self.decl_ref_mut.decl_index); - decl.value_arena.?.release(&self.decl_arena); - self.decl_arena = undefined; - } }; fn beginComptimePtrMutation( @@ -27026,201 +28536,251 @@ fn beginComptimePtrMutation( ptr_val: Value, ptr_elem_ty: Type, ) CompileError!ComptimePtrMutationKit { - const target = sema.mod.getTarget(); - switch (ptr_val.tag()) { - .decl_ref_mut => { - const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data; - const decl = sema.mod.declPtr(decl_ref_mut.decl_index); - return sema.beginComptimePtrMutationInner(block, src, decl.ty, &decl.val, ptr_elem_ty, decl_ref_mut); - }, - .comptime_field_ptr => { - const payload = ptr_val.castTag(.comptime_field_ptr).?.data; + const mod = sema.mod; + const ptr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr; + switch (ptr.addr) { + .decl, .int => unreachable, // isComptimeMutablePtr has been checked already + .mut_decl => |mut_decl| { + const decl = mod.declPtr(mut_decl.decl); + return sema.beginComptimePtrMutationInner(block, src, decl.ty, &decl.val, ptr_elem_ty, mut_decl); + }, + .comptime_field => |comptime_field| { const duped = try sema.arena.create(Value); - duped.* = payload.field_val; - return sema.beginComptimePtrMutationInner(block, src, payload.field_ty, duped, ptr_elem_ty, .{ - .decl_index = @intToEnum(Module.Decl.Index, 0), + duped.* = comptime_field.toValue(); + return sema.beginComptimePtrMutationInner(block, src, mod.intern_pool.typeOf(comptime_field).toType(), duped, ptr_elem_ty, .{ + .decl = undefined, .runtime_index = .comptime_field_ptr, }); }, - .elem_ptr => { - const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; - var parent = try sema.beginComptimePtrMutation(block, src, elem_ptr.array_ptr, elem_ptr.elem_ty); + .eu_payload => |eu_ptr| { + const eu_ty = mod.intern_pool.typeOf(eu_ptr).toType().childType(mod); + var parent = try sema.beginComptimePtrMutation(block, src, eu_ptr.toValue(), eu_ty); + switch (parent.pointee) { + .direct => |val_ptr| { + const payload_ty = parent.ty.errorUnionPayload(mod); + if (val_ptr.ip_index == .none and val_ptr.tag() == .eu_payload) { + return ComptimePtrMutationKit{ + .mut_decl = parent.mut_decl, + .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data }, + .ty = payload_ty, + }; + } else { + // An error union has been initialized to undefined at comptime and now we + // are for the first time setting the payload. We must change the + // representation of the error union from `undef` to `opt_payload`. + + const payload = try sema.arena.create(Value.Payload.SubValue); + payload.* = .{ + .base = .{ .tag = .eu_payload }, + .data = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(), + }; + + val_ptr.* = Value.initPayload(&payload.base); + + return ComptimePtrMutationKit{ + .mut_decl = parent.mut_decl, + .pointee = .{ .direct = &payload.data }, + .ty = payload_ty, + }; + } + }, + .bad_decl_ty, .bad_ptr_ty => return parent, + // Even though the parent value type has well-defined memory layout, our + // pointer type does not. + .reinterpret => return ComptimePtrMutationKit{ + .mut_decl = parent.mut_decl, + .pointee = .bad_ptr_ty, + .ty = eu_ty, + }, + } + }, + .opt_payload => |opt_ptr| { + const opt_ty = mod.intern_pool.typeOf(opt_ptr).toType().childType(mod); + var parent = try sema.beginComptimePtrMutation(block, src, opt_ptr.toValue(), opt_ty); + switch (parent.pointee) { + .direct => |val_ptr| { + const payload_ty = parent.ty.optionalChild(mod); + switch (val_ptr.ip_index) { + .none => return ComptimePtrMutationKit{ + .mut_decl = parent.mut_decl, + .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data }, + .ty = payload_ty, + }, + else => { + const payload_val = switch (mod.intern_pool.indexToKey(val_ptr.ip_index)) { + .undef => try mod.intern(.{ .undef = payload_ty.toIntern() }), + .opt => |opt| switch (opt.val) { + .none => try mod.intern(.{ .undef = payload_ty.toIntern() }), + else => |payload| payload, + }, + else => unreachable, + }; + + // An optional has been initialized to undefined at comptime and now we + // are for the first time setting the payload. We must change the + // representation of the optional from `undef` to `opt_payload`. + + const payload = try sema.arena.create(Value.Payload.SubValue); + payload.* = .{ + .base = .{ .tag = .opt_payload }, + .data = payload_val.toValue(), + }; + + val_ptr.* = Value.initPayload(&payload.base); + + return ComptimePtrMutationKit{ + .mut_decl = parent.mut_decl, + .pointee = .{ .direct = &payload.data }, + .ty = payload_ty, + }; + }, + } + }, + .bad_decl_ty, .bad_ptr_ty => return parent, + // Even though the parent value type has well-defined memory layout, our + // pointer type does not. + .reinterpret => return ComptimePtrMutationKit{ + .mut_decl = parent.mut_decl, + .pointee = .bad_ptr_ty, + .ty = opt_ty, + }, + } + }, + .elem => |elem_ptr| { + const base_elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod); + var parent = try sema.beginComptimePtrMutation(block, src, elem_ptr.base.toValue(), base_elem_ty); switch (parent.pointee) { - .direct => |val_ptr| switch (parent.ty.zigTypeTag()) { + .direct => |val_ptr| switch (parent.ty.zigTypeTag(mod)) { .Array, .Vector => { - const check_len = parent.ty.arrayLenIncludingSentinel(); + const check_len = parent.ty.arrayLenIncludingSentinel(mod); if (elem_ptr.index >= check_len) { // TODO have the parent include the decl so we can say "declared here" return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{ elem_ptr.index, check_len, }); } - const elem_ty = parent.ty.childType(); + const elem_ty = parent.ty.childType(mod); // We might have a pointer to multiple elements of the array (e.g. a pointer // to a sub-array). In this case, we just have to reinterpret the relevant // bytes of the whole array rather than any single element. - const elem_abi_size_u64 = try sema.typeAbiSize(elem_ptr.elem_ty); + const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty); if (elem_abi_size_u64 < try sema.typeAbiSize(ptr_elem_ty)) { const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64); + const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); return .{ - .decl_ref_mut = parent.decl_ref_mut, + .mut_decl = parent.mut_decl, .pointee = .{ .reinterpret = .{ .val_ptr = val_ptr, - .byte_offset = elem_abi_size * elem_ptr.index, + .byte_offset = elem_abi_size * elem_idx, } }, .ty = parent.ty, }; } - switch (val_ptr.tag()) { - .undef => { - // An array has been initialized to undefined at comptime and now we - // are for the first time setting an element. We must change the representation - // of the array from `undef` to `array`. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); + switch (val_ptr.ip_index) { + .none => switch (val_ptr.tag()) { + .bytes => { + // An array is memory-optimized to store a slice of bytes, but we are about + // to modify an individual field and the representation has to change. + // If we wanted to avoid this, there would need to be special detection + // elsewhere to identify when writing a value to an array element that is stored + // using the `bytes` tag, and handle it without making a call to this function. + const arena = sema.arena; + + const bytes = val_ptr.castTag(.bytes).?.data; + const dest_len = parent.ty.arrayLenIncludingSentinel(mod); + // bytes.len may be one greater than dest_len because of the case when + // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. + assert(bytes.len >= dest_len); + const elems = try arena.alloc(Value, @intCast(usize, dest_len)); + for (elems, 0..) |*elem, i| { + elem.* = try mod.intValue(elem_ty, bytes[i]); + } - const array_len_including_sentinel = - try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); - const elems = try arena.alloc(Value, array_len_including_sentinel); - @memset(elems, Value.undef); + val_ptr.* = try Value.Tag.aggregate.create(arena, elems); - val_ptr.* = try Value.Tag.aggregate.create(arena, elems); + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + &elems[@intCast(usize, elem_ptr.index)], + ptr_elem_ty, + parent.mut_decl, + ); + }, + .repeated => { + // An array is memory-optimized to store only a single element value, and + // that value is understood to be the same for the entire length of the array. + // However, now we want to modify an individual field and so the + // representation has to change. If we wanted to avoid this, there would + // need to be special detection elsewhere to identify when writing a value to an + // array element that is stored using the `repeated` tag, and handle it + // without making a call to this function. + const arena = sema.arena; + + const repeated_val = try val_ptr.castTag(.repeated).?.data.copy(arena); + const array_len_including_sentinel = + try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod)); + const elems = try arena.alloc(Value, array_len_including_sentinel); + if (elems.len > 0) elems[0] = repeated_val; + for (elems[1..]) |*elem| { + elem.* = try repeated_val.copy(arena); + } - return beginComptimePtrMutationInner( - sema, - block, - src, - elem_ty, - &elems[elem_ptr.index], - ptr_elem_ty, - parent.decl_ref_mut, - ); - }, - .bytes => { - // An array is memory-optimized to store a slice of bytes, but we are about - // to modify an individual field and the representation has to change. - // If we wanted to avoid this, there would need to be special detection - // elsewhere to identify when writing a value to an array element that is stored - // using the `bytes` tag, and handle it without making a call to this function. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - - const bytes = val_ptr.castTag(.bytes).?.data; - const dest_len = parent.ty.arrayLenIncludingSentinel(); - // bytes.len may be one greater than dest_len because of the case when - // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. - assert(bytes.len >= dest_len); - const elems = try arena.alloc(Value, @intCast(usize, dest_len)); - for (elems, 0..) |*elem, i| { - elem.* = try Value.Tag.int_u64.create(arena, bytes[i]); - } + val_ptr.* = try Value.Tag.aggregate.create(arena, elems); - val_ptr.* = try Value.Tag.aggregate.create(arena, elems); + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + &elems[@intCast(usize, elem_ptr.index)], + ptr_elem_ty, + parent.mut_decl, + ); + }, - return beginComptimePtrMutationInner( + .aggregate => return beginComptimePtrMutationInner( sema, block, src, elem_ty, - &elems[elem_ptr.index], + &val_ptr.castTag(.aggregate).?.data[@intCast(usize, elem_ptr.index)], ptr_elem_ty, - parent.decl_ref_mut, - ); - }, - .str_lit => { - // An array is memory-optimized to store a slice of bytes, but we are about - // to modify an individual field and the representation has to change. - // If we wanted to avoid this, there would need to be special detection - // elsewhere to identify when writing a value to an array element that is stored - // using the `str_lit` tag, and handle it without making a call to this function. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - - const str_lit = val_ptr.castTag(.str_lit).?.data; - const dest_len = parent.ty.arrayLenIncludingSentinel(); - const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; - const elems = try arena.alloc(Value, @intCast(usize, dest_len)); - for (bytes, 0..) |byte, i| { - elems[i] = try Value.Tag.int_u64.create(arena, byte); - } - if (parent.ty.sentinel()) |sent_val| { - assert(elems.len == bytes.len + 1); - elems[bytes.len] = sent_val; - } - - val_ptr.* = try Value.Tag.aggregate.create(arena, elems); + parent.mut_decl, + ), - return beginComptimePtrMutationInner( - sema, - block, - src, - elem_ty, - &elems[elem_ptr.index], - ptr_elem_ty, - parent.decl_ref_mut, - ); + else => unreachable, }, - .repeated => { - // An array is memory-optimized to store only a single element value, and - // that value is understood to be the same for the entire length of the array. - // However, now we want to modify an individual field and so the - // representation has to change. If we wanted to avoid this, there would - // need to be special detection elsewhere to identify when writing a value to an - // array element that is stored using the `repeated` tag, and handle it - // without making a call to this function. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - - const repeated_val = try val_ptr.castTag(.repeated).?.data.copy(arena); - const array_len_including_sentinel = - try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); - const elems = try arena.alloc(Value, array_len_including_sentinel); - if (elems.len > 0) elems[0] = repeated_val; - for (elems[1..]) |*elem| { - elem.* = try repeated_val.copy(arena); - } - - val_ptr.* = try Value.Tag.aggregate.create(arena, elems); + else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) { + .undef => { + // An array has been initialized to undefined at comptime and now we + // are for the first time setting an element. We must change the representation + // of the array from `undef` to `array`. + const arena = sema.arena; - return beginComptimePtrMutationInner( - sema, - block, - src, - elem_ty, - &elems[elem_ptr.index], - ptr_elem_ty, - parent.decl_ref_mut, - ); - }, + const array_len_including_sentinel = + try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod)); + const elems = try arena.alloc(Value, array_len_including_sentinel); + @memset(elems, (try mod.intern(.{ .undef = elem_ty.toIntern() })).toValue()); - .aggregate => return beginComptimePtrMutationInner( - sema, - block, - src, - elem_ty, - &val_ptr.castTag(.aggregate).?.data[elem_ptr.index], - ptr_elem_ty, - parent.decl_ref_mut, - ), + val_ptr.* = try Value.Tag.aggregate.create(arena, elems); - .the_only_possible_value => { - const duped = try sema.arena.create(Value); - duped.* = Value.initTag(.the_only_possible_value); - return beginComptimePtrMutationInner( - sema, - block, - src, - elem_ty, - duped, - ptr_elem_ty, - parent.decl_ref_mut, - ); + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + &elems[@intCast(usize, elem_ptr.index)], + ptr_elem_ty, + parent.mut_decl, + ); + }, + else => unreachable, }, - - else => unreachable, } }, else => { @@ -27237,28 +28797,29 @@ fn beginComptimePtrMutation( parent.ty, val_ptr, ptr_elem_ty, - parent.decl_ref_mut, + parent.mut_decl, ); }, }, .reinterpret => |reinterpret| { - if (!elem_ptr.elem_ty.hasWellDefinedLayout()) { + if (!base_elem_ty.hasWellDefinedLayout(mod)) { // Even though the parent value type has well-defined memory layout, our // pointer type does not. return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, + .mut_decl = parent.mut_decl, .pointee = .bad_ptr_ty, - .ty = elem_ptr.elem_ty, + .ty = base_elem_ty, }; } - const elem_abi_size_u64 = try sema.typeAbiSize(elem_ptr.elem_ty); + const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty); const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64); + const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, + .mut_decl = parent.mut_decl, .pointee = .{ .reinterpret = .{ .val_ptr = reinterpret.val_ptr, - .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_ptr.index, + .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_idx, } }, .ty = parent.ty, }; @@ -27266,162 +28827,184 @@ fn beginComptimePtrMutation( .bad_decl_ty, .bad_ptr_ty => return parent, } }, - .field_ptr => { - const field_ptr = ptr_val.castTag(.field_ptr).?.data; - const field_index = @intCast(u32, field_ptr.field_index); + .field => |field_ptr| { + const base_child_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod); + const field_index = @intCast(u32, field_ptr.index); - var parent = try sema.beginComptimePtrMutation(block, src, field_ptr.container_ptr, field_ptr.container_ty); + var parent = try sema.beginComptimePtrMutation(block, src, field_ptr.base.toValue(), base_child_ty); switch (parent.pointee) { - .direct => |val_ptr| switch (val_ptr.tag()) { - .undef => { - // A struct or union has been initialized to undefined at comptime and now we - // are for the first time setting a field. We must change the representation - // of the struct/union from `undef` to `struct`/`union`. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - - switch (parent.ty.zigTypeTag()) { - .Struct => { - const fields = try arena.alloc(Value, parent.ty.structFieldCount()); - @memset(fields, Value.undef); - - val_ptr.* = try Value.Tag.aggregate.create(arena, fields); - - return beginComptimePtrMutationInner( - sema, - block, - src, - parent.ty.structFieldType(field_index), - &fields[field_index], - ptr_elem_ty, - parent.decl_ref_mut, - ); - }, - .Union => { - const payload = try arena.create(Value.Payload.Union); - payload.* = .{ .data = .{ - .tag = try Value.Tag.enum_field_index.create(arena, field_index), - .val = Value.undef, - } }; - - val_ptr.* = Value.initPayload(&payload.base); - - return beginComptimePtrMutationInner( - sema, - block, - src, - parent.ty.structFieldType(field_index), - &payload.data.val, - ptr_elem_ty, - parent.decl_ref_mut, - ); - }, - .Pointer => { - assert(parent.ty.isSlice()); - val_ptr.* = try Value.Tag.slice.create(arena, .{ - .ptr = Value.undef, - .len = Value.undef, - }); - - switch (field_index) { - Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner( - sema, - block, - src, - parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), - &val_ptr.castTag(.slice).?.data.ptr, - ptr_elem_ty, - parent.decl_ref_mut, - ), - Value.Payload.Slice.len_index => return beginComptimePtrMutationInner( - sema, - block, - src, - Type.usize, - &val_ptr.castTag(.slice).?.data.len, - ptr_elem_ty, - parent.decl_ref_mut, - ), - - else => unreachable, - } - }, - else => unreachable, - } - }, - .aggregate => return beginComptimePtrMutationInner( - sema, - block, - src, - parent.ty.structFieldType(field_index), - &val_ptr.castTag(.aggregate).?.data[field_index], - ptr_elem_ty, - parent.decl_ref_mut, - ), - - .@"union" => { - // We need to set the active field of the union. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - - const payload = &val_ptr.castTag(.@"union").?.data; - payload.tag = try Value.Tag.enum_field_index.create(arena, field_index); - + .direct => |val_ptr| switch (val_ptr.ip_index) { + .empty_struct => { + const duped = try sema.arena.create(Value); + duped.* = val_ptr.*; return beginComptimePtrMutationInner( sema, block, src, - parent.ty.structFieldType(field_index), - &payload.val, + parent.ty.structFieldType(field_index, mod), + duped, ptr_elem_ty, - parent.decl_ref_mut, + parent.mut_decl, ); }, - .slice => switch (field_index) { - Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner( + .none => switch (val_ptr.tag()) { + .aggregate => return beginComptimePtrMutationInner( sema, block, src, - parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), - &val_ptr.castTag(.slice).?.data.ptr, + parent.ty.structFieldType(field_index, mod), + &val_ptr.castTag(.aggregate).?.data[field_index], ptr_elem_ty, - parent.decl_ref_mut, + parent.mut_decl, ), + .repeated => { + const arena = sema.arena; - Value.Payload.Slice.len_index => return beginComptimePtrMutationInner( - sema, - block, - src, - Type.usize, - &val_ptr.castTag(.slice).?.data.len, - ptr_elem_ty, - parent.decl_ref_mut, - ), + const elems = try arena.alloc(Value, parent.ty.structFieldCount(mod)); + @memset(elems, val_ptr.castTag(.repeated).?.data); + val_ptr.* = try Value.Tag.aggregate.create(arena, elems); + + return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.structFieldType(field_index, mod), + &elems[field_index], + ptr_elem_ty, + parent.mut_decl, + ); + }, + .@"union" => { + // We need to set the active field of the union. + const union_tag_ty = base_child_ty.unionTagTypeHypothetical(mod); + + const payload = &val_ptr.castTag(.@"union").?.data; + payload.tag = try mod.enumValueFieldIndex(union_tag_ty, field_index); + return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.structFieldType(field_index, mod), + &payload.val, + ptr_elem_ty, + parent.mut_decl, + ); + }, + .slice => switch (field_index) { + Value.slice_ptr_index => return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.slicePtrFieldType(mod), + &val_ptr.castTag(.slice).?.data.ptr, + ptr_elem_ty, + parent.mut_decl, + ), + + Value.slice_len_index => return beginComptimePtrMutationInner( + sema, + block, + src, + Type.usize, + &val_ptr.castTag(.slice).?.data.len, + ptr_elem_ty, + parent.mut_decl, + ), + + else => unreachable, + }, else => unreachable, }, - - .empty_struct_value => { - const duped = try sema.arena.create(Value); - duped.* = Value.initTag(.the_only_possible_value); - return beginComptimePtrMutationInner( - sema, - block, - src, - parent.ty.structFieldType(field_index), - duped, - ptr_elem_ty, - parent.decl_ref_mut, - ); + else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) { + .undef => { + // A struct or union has been initialized to undefined at comptime and now we + // are for the first time setting a field. We must change the representation + // of the struct/union from `undef` to `struct`/`union`. + const arena = sema.arena; + + switch (parent.ty.zigTypeTag(mod)) { + .Struct => { + const fields = try arena.alloc(Value, parent.ty.structFieldCount(mod)); + for (fields, 0..) |*field, i| field.* = (try mod.intern(.{ + .undef = parent.ty.structFieldType(i, mod).toIntern(), + })).toValue(); + + val_ptr.* = try Value.Tag.aggregate.create(arena, fields); + + return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.structFieldType(field_index, mod), + &fields[field_index], + ptr_elem_ty, + parent.mut_decl, + ); + }, + .Union => { + const payload = try arena.create(Value.Payload.Union); + const tag_ty = parent.ty.unionTagTypeHypothetical(mod); + const payload_ty = parent.ty.structFieldType(field_index, mod); + payload.* = .{ .data = .{ + .tag = try mod.enumValueFieldIndex(tag_ty, field_index), + .val = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(), + } }; + + val_ptr.* = Value.initPayload(&payload.base); + + return beginComptimePtrMutationInner( + sema, + block, + src, + payload_ty, + &payload.data.val, + ptr_elem_ty, + parent.mut_decl, + ); + }, + .Pointer => { + assert(parent.ty.isSlice(mod)); + const ptr_ty = parent.ty.slicePtrFieldType(mod); + val_ptr.* = try Value.Tag.slice.create(arena, .{ + .ptr = (try mod.intern(.{ .undef = ptr_ty.toIntern() })).toValue(), + .len = (try mod.intern(.{ .undef = .usize_type })).toValue(), + }); + + switch (field_index) { + Value.slice_ptr_index => return beginComptimePtrMutationInner( + sema, + block, + src, + ptr_ty, + &val_ptr.castTag(.slice).?.data.ptr, + ptr_elem_ty, + parent.mut_decl, + ), + Value.slice_len_index => return beginComptimePtrMutationInner( + sema, + block, + src, + Type.usize, + &val_ptr.castTag(.slice).?.data.len, + ptr_elem_ty, + parent.mut_decl, + ), + + else => unreachable, + } + }, + else => unreachable, + } + }, + else => unreachable, }, - - else => unreachable, }, .reinterpret => |reinterpret| { - const field_offset_u64 = field_ptr.container_ty.structFieldOffset(field_index, target); + const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod); const field_offset = try sema.usizeCast(block, src, field_offset_u64); return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, + .mut_decl = parent.mut_decl, .pointee = .{ .reinterpret = .{ .val_ptr = reinterpret.val_ptr, .byte_offset = reinterpret.byte_offset + field_offset, @@ -27432,106 +29015,6 @@ fn beginComptimePtrMutation( .bad_decl_ty, .bad_ptr_ty => return parent, } }, - .eu_payload_ptr => { - const eu_ptr = ptr_val.castTag(.eu_payload_ptr).?.data; - var parent = try sema.beginComptimePtrMutation(block, src, eu_ptr.container_ptr, eu_ptr.container_ty); - switch (parent.pointee) { - .direct => |val_ptr| { - const payload_ty = parent.ty.errorUnionPayload(); - switch (val_ptr.tag()) { - else => { - // An error union has been initialized to undefined at comptime and now we - // are for the first time setting the payload. We must change the - // representation of the error union from `undef` to `opt_payload`. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - - const payload = try arena.create(Value.Payload.SubValue); - payload.* = .{ - .base = .{ .tag = .eu_payload }, - .data = Value.undef, - }; - - val_ptr.* = Value.initPayload(&payload.base); - - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .pointee = .{ .direct = &payload.data }, - .ty = payload_ty, - }; - }, - .eu_payload => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data }, - .ty = payload_ty, - }, - } - }, - .bad_decl_ty, .bad_ptr_ty => return parent, - // Even though the parent value type has well-defined memory layout, our - // pointer type does not. - .reinterpret => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .pointee = .bad_ptr_ty, - .ty = eu_ptr.container_ty, - }, - } - }, - .opt_payload_ptr => { - const opt_ptr = if (ptr_val.castTag(.opt_payload_ptr)) |some| some.data else { - return sema.beginComptimePtrMutation(block, src, ptr_val, try ptr_elem_ty.optionalChildAlloc(sema.arena)); - }; - var parent = try sema.beginComptimePtrMutation(block, src, opt_ptr.container_ptr, opt_ptr.container_ty); - switch (parent.pointee) { - .direct => |val_ptr| { - const payload_ty = try parent.ty.optionalChildAlloc(sema.arena); - switch (val_ptr.tag()) { - .undef, .null_value => { - // An optional has been initialized to undefined at comptime and now we - // are for the first time setting the payload. We must change the - // representation of the optional from `undef` to `opt_payload`. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - - const payload = try arena.create(Value.Payload.SubValue); - payload.* = .{ - .base = .{ .tag = .opt_payload }, - .data = Value.undef, - }; - - val_ptr.* = Value.initPayload(&payload.base); - - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .pointee = .{ .direct = &payload.data }, - .ty = payload_ty, - }; - }, - .opt_payload => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data }, - .ty = payload_ty, - }, - - else => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .pointee = .{ .direct = val_ptr }, - .ty = payload_ty, - }, - } - }, - .bad_decl_ty, .bad_ptr_ty => return parent, - // Even though the parent value type has well-defined memory layout, our - // pointer type does not. - .reinterpret => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .pointee = .bad_ptr_ty, - .ty = opt_ptr.container_ty, - }, - } - }, - .decl_ref => unreachable, // isComptimeMutablePtr() has been checked already - else => unreachable, } } @@ -27542,46 +29025,50 @@ fn beginComptimePtrMutationInner( decl_ty: Type, decl_val: *Value, ptr_elem_ty: Type, - decl_ref_mut: Value.Payload.DeclRefMut.Data, + mut_decl: InternPool.Key.Ptr.Addr.MutDecl, ) CompileError!ComptimePtrMutationKit { - const target = sema.mod.getTarget(); + const mod = sema.mod; + const target = mod.getTarget(); const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok; + + decl_val.* = try decl_val.unintern(sema.arena, mod); + if (coerce_ok) { return ComptimePtrMutationKit{ - .decl_ref_mut = decl_ref_mut, + .mut_decl = mut_decl, .pointee = .{ .direct = decl_val }, .ty = decl_ty, }; } // Handle the case that the decl is an array and we're actually trying to point to an element. - if (decl_ty.isArrayOrVector()) { - const decl_elem_ty = decl_ty.childType(); + if (decl_ty.isArrayOrVector(mod)) { + const decl_elem_ty = decl_ty.childType(mod); if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) { return ComptimePtrMutationKit{ - .decl_ref_mut = decl_ref_mut, + .mut_decl = mut_decl, .pointee = .{ .direct = decl_val }, .ty = decl_ty, }; } } - if (!decl_ty.hasWellDefinedLayout()) { + if (!decl_ty.hasWellDefinedLayout(mod)) { return ComptimePtrMutationKit{ - .decl_ref_mut = decl_ref_mut, - .pointee = .{ .bad_decl_ty = {} }, + .mut_decl = mut_decl, + .pointee = .bad_decl_ty, .ty = decl_ty, }; } - if (!ptr_elem_ty.hasWellDefinedLayout()) { + if (!ptr_elem_ty.hasWellDefinedLayout(mod)) { return ComptimePtrMutationKit{ - .decl_ref_mut = decl_ref_mut, - .pointee = .{ .bad_ptr_ty = {} }, + .mut_decl = mut_decl, + .pointee = .bad_ptr_ty, .ty = ptr_elem_ty, }; } return ComptimePtrMutationKit{ - .decl_ref_mut = decl_ref_mut, + .mut_decl = mut_decl, .pointee = .{ .reinterpret = .{ .val_ptr = decl_val, .byte_offset = 0, @@ -27623,237 +29110,227 @@ fn beginComptimePtrLoad( ptr_val: Value, maybe_array_ty: ?Type, ) ComptimePtrLoadError!ComptimePtrLoadKit { - const target = sema.mod.getTarget(); - var deref: ComptimePtrLoadKit = switch (ptr_val.tag()) { - .decl_ref, - .decl_ref_mut, - => blk: { - const decl_index = switch (ptr_val.tag()) { - .decl_ref => ptr_val.castTag(.decl_ref).?.data, - .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl_index, - else => unreachable, - }; - const is_mutable = ptr_val.tag() == .decl_ref_mut; - const decl = sema.mod.declPtr(decl_index); - const decl_tv = try decl.typedValue(); - if (decl_tv.val.tag() == .variable) return error.RuntimeLoad; - - const layout_defined = decl.ty.hasWellDefinedLayout(); - break :blk ComptimePtrLoadKit{ - .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null, - .pointee = decl_tv, - .is_mutable = is_mutable, - .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null, - }; - }, - - .elem_ptr => blk: { - const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; - const elem_ty = elem_ptr.elem_ty; - var deref = try sema.beginComptimePtrLoad(block, src, elem_ptr.array_ptr, null); + const mod = sema.mod; + const target = mod.getTarget(); - // This code assumes that elem_ptrs have been "flattened" in order for direct dereference - // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that - // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened" - if (elem_ptr.array_ptr.castTag(.elem_ptr)) |parent_elem_ptr| { - assert(!(parent_elem_ptr.data.elem_ty.eql(elem_ty, sema.mod))); - } + var deref: ComptimePtrLoadKit = switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) { + .ptr => |ptr| switch (ptr.addr) { + .decl, .mut_decl => blk: { + const decl_index = switch (ptr.addr) { + .decl => |decl| decl, + .mut_decl => |mut_decl| mut_decl.decl, + else => unreachable, + }; + const is_mutable = ptr.addr == .mut_decl; + const decl = mod.declPtr(decl_index); + const decl_tv = try decl.typedValue(); + if (decl.val.getVariable(mod) != null) return error.RuntimeLoad; + + const layout_defined = decl.ty.hasWellDefinedLayout(mod); + break :blk ComptimePtrLoadKit{ + .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null, + .pointee = decl_tv, + .is_mutable = is_mutable, + .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null, + }; + }, + .int => return error.RuntimeLoad, + .eu_payload, .opt_payload => |container_ptr| blk: { + const container_ty = mod.intern_pool.typeOf(container_ptr).toType().childType(mod); + const payload_ty = switch (ptr.addr) { + .eu_payload => container_ty.errorUnionPayload(mod), + .opt_payload => container_ty.optionalChild(mod), + else => unreachable, + }; + var deref = try sema.beginComptimePtrLoad(block, src, container_ptr.toValue(), container_ty); - if (elem_ptr.index != 0) { - if (elem_ty.hasWellDefinedLayout()) { - if (deref.parent) |*parent| { - // Update the byte offset (in-place) - const elem_size = try sema.typeAbiSize(elem_ty); - const offset = parent.byte_offset + elem_size * elem_ptr.index; - parent.byte_offset = try sema.usizeCast(block, src, offset); - } - } else { + // eu_payload and opt_payload never have a well-defined layout + if (deref.parent != null) { deref.parent = null; - deref.ty_without_well_defined_layout = elem_ty; + deref.ty_without_well_defined_layout = container_ty; + } + + if (deref.pointee) |*tv| { + const coerce_in_mem_ok = + (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or + (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok; + if (coerce_in_mem_ok) { + const payload_val = switch (tv.val.ip_index) { + .none => tv.val.cast(Value.Payload.SubValue).?.data, + .null_value => return sema.fail(block, src, "attempt to use null value", .{}), + else => switch (mod.intern_pool.indexToKey(tv.val.toIntern())) { + .error_union => |error_union| switch (error_union.val) { + .err_name => |err_name| return sema.fail( + block, + src, + "attempt to unwrap error: {}", + .{err_name.fmt(&mod.intern_pool)}, + ), + .payload => |payload| payload, + }, + .opt => |opt| switch (opt.val) { + .none => return sema.fail(block, src, "attempt to use null value", .{}), + else => |payload| payload, + }, + else => unreachable, + }.toValue(), + }; + tv.* = TypedValue{ .ty = payload_ty, .val = payload_val }; + break :blk deref; + } } - } - - // If we're loading an elem_ptr that was derived from a different type - // than the true type of the underlying decl, we cannot deref directly - const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector()) x: { - const deref_elem_ty = deref.pointee.?.ty.childType(); - break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or - (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok; - } else false; - if (!ty_matches) { deref.pointee = null; break :blk deref; - } + }, + .comptime_field => |comptime_field| blk: { + const field_ty = mod.intern_pool.typeOf(comptime_field).toType(); + break :blk ComptimePtrLoadKit{ + .parent = null, + .pointee = .{ .ty = field_ty, .val = comptime_field.toValue() }, + .is_mutable = false, + .ty_without_well_defined_layout = field_ty, + }; + }, + .elem => |elem_ptr| blk: { + const elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod); + var deref = try sema.beginComptimePtrLoad(block, src, elem_ptr.base.toValue(), null); + + // This code assumes that elem_ptrs have been "flattened" in order for direct dereference + // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that + // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened" + switch (mod.intern_pool.indexToKey(elem_ptr.base)) { + .ptr => |base_ptr| switch (base_ptr.addr) { + .elem => |base_elem| assert(!mod.intern_pool.typeOf(base_elem.base).toType().elemType2(mod).eql(elem_ty, mod)), + else => {}, + }, + else => {}, + } - var array_tv = deref.pointee.?; - const check_len = array_tv.ty.arrayLenIncludingSentinel(); - if (maybe_array_ty) |load_ty| { - // It's possible that we're loading a [N]T, in which case we'd like to slice - // the pointee array directly from our parent array. - if (load_ty.isArrayOrVector() and load_ty.childType().eql(elem_ty, sema.mod)) { - const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel()); - deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{ - .ty = try Type.array(sema.arena, N, null, elem_ty, sema.mod), - .val = try array_tv.val.sliceArray(sema.mod, sema.arena, elem_ptr.index, elem_ptr.index + N), - } else null; - break :blk deref; + if (elem_ptr.index != 0) { + if (elem_ty.hasWellDefinedLayout(mod)) { + if (deref.parent) |*parent| { + // Update the byte offset (in-place) + const elem_size = try sema.typeAbiSize(elem_ty); + const offset = parent.byte_offset + elem_size * elem_ptr.index; + parent.byte_offset = try sema.usizeCast(block, src, offset); + } + } else { + deref.parent = null; + deref.ty_without_well_defined_layout = elem_ty; + } } - } - if (elem_ptr.index >= check_len) { - deref.pointee = null; - break :blk deref; - } - if (elem_ptr.index == check_len - 1) { - if (array_tv.ty.sentinel()) |sent| { - deref.pointee = TypedValue{ - .ty = elem_ty, - .val = sent, - }; + // If we're loading an elem that was derived from a different type + // than the true type of the underlying decl, we cannot deref directly + const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector(mod)) x: { + const deref_elem_ty = deref.pointee.?.ty.childType(mod); + break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or + (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok; + } else false; + if (!ty_matches) { + deref.pointee = null; break :blk deref; } - } - deref.pointee = TypedValue{ - .ty = elem_ty, - .val = try array_tv.val.elemValue(sema.mod, sema.arena, elem_ptr.index), - }; - break :blk deref; - }, - - .slice => blk: { - const slice = ptr_val.castTag(.slice).?.data; - break :blk try sema.beginComptimePtrLoad(block, src, slice.ptr, null); - }, - .field_ptr => blk: { - const field_ptr = ptr_val.castTag(.field_ptr).?.data; - const field_index = @intCast(u32, field_ptr.field_index); - var deref = try sema.beginComptimePtrLoad(block, src, field_ptr.container_ptr, field_ptr.container_ty); - - if (field_ptr.container_ty.hasWellDefinedLayout()) { - const struct_ty = field_ptr.container_ty.castTag(.@"struct"); - if (struct_ty != null and struct_ty.?.data.layout == .Packed) { - // packed structs are not byte addressable - deref.parent = null; - } else if (deref.parent) |*parent| { - // Update the byte offset (in-place) - try sema.resolveTypeLayout(field_ptr.container_ty); - const field_offset = field_ptr.container_ty.structFieldOffset(field_index, target); - parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset); + var array_tv = deref.pointee.?; + const check_len = array_tv.ty.arrayLenIncludingSentinel(mod); + if (maybe_array_ty) |load_ty| { + // It's possible that we're loading a [N]T, in which case we'd like to slice + // the pointee array directly from our parent array. + if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) { + const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod)); + const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); + deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{ + .ty = try Type.array(sema.arena, N, null, elem_ty, mod), + .val = try array_tv.val.sliceArray(mod, sema.arena, elem_idx, elem_idx + N), + } else null; + break :blk deref; + } } - } else { - deref.parent = null; - deref.ty_without_well_defined_layout = field_ptr.container_ty; - } - - const tv = deref.pointee orelse { - deref.pointee = null; - break :blk deref; - }; - const coerce_in_mem_ok = - (try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or - (try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok; - if (!coerce_in_mem_ok) { - deref.pointee = null; - break :blk deref; - } - if (field_ptr.container_ty.isSlice()) { - const slice_val = tv.val.castTag(.slice).?.data; - deref.pointee = switch (field_index) { - Value.Payload.Slice.ptr_index => TypedValue{ - .ty = field_ptr.container_ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), - .val = slice_val.ptr, - }, - Value.Payload.Slice.len_index => TypedValue{ - .ty = Type.usize, - .val = slice_val.len, - }, - else => unreachable, - }; - } else { - const field_ty = field_ptr.container_ty.structFieldType(field_index); + if (elem_ptr.index >= check_len) { + deref.pointee = null; + break :blk deref; + } + if (elem_ptr.index == check_len - 1) { + if (array_tv.ty.sentinel(mod)) |sent| { + deref.pointee = TypedValue{ + .ty = elem_ty, + .val = sent, + }; + break :blk deref; + } + } deref.pointee = TypedValue{ - .ty = field_ty, - .val = tv.val.fieldValue(tv.ty, field_index), + .ty = elem_ty, + .val = try array_tv.val.elemValue(mod, @intCast(usize, elem_ptr.index)), }; - } - break :blk deref; - }, - - .comptime_field_ptr => blk: { - const comptime_field_ptr = ptr_val.castTag(.comptime_field_ptr).?.data; - break :blk ComptimePtrLoadKit{ - .parent = null, - .pointee = .{ .ty = comptime_field_ptr.field_ty, .val = comptime_field_ptr.field_val }, - .is_mutable = false, - .ty_without_well_defined_layout = comptime_field_ptr.field_ty, - }; - }, - - .opt_payload_ptr, - .eu_payload_ptr, - => blk: { - const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data; - const payload_ty = switch (ptr_val.tag()) { - .eu_payload_ptr => payload_ptr.container_ty.errorUnionPayload(), - .opt_payload_ptr => try payload_ptr.container_ty.optionalChildAlloc(sema.arena), - else => unreachable, - }; - var deref = try sema.beginComptimePtrLoad(block, src, payload_ptr.container_ptr, payload_ptr.container_ty); - - // eu_payload_ptr and opt_payload_ptr never have a well-defined layout - if (deref.parent != null) { - deref.parent = null; - deref.ty_without_well_defined_layout = payload_ptr.container_ty; - } + break :blk deref; + }, + .field => |field_ptr| blk: { + const field_index = @intCast(u32, field_ptr.index); + const container_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod); + var deref = try sema.beginComptimePtrLoad(block, src, field_ptr.base.toValue(), container_ty); + + if (container_ty.hasWellDefinedLayout(mod)) { + const struct_obj = mod.typeToStruct(container_ty); + if (struct_obj != null and struct_obj.?.layout == .Packed) { + // packed structs are not byte addressable + deref.parent = null; + } else if (deref.parent) |*parent| { + // Update the byte offset (in-place) + try sema.resolveTypeLayout(container_ty); + const field_offset = container_ty.structFieldOffset(field_index, mod); + parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset); + } + } else { + deref.parent = null; + deref.ty_without_well_defined_layout = container_ty; + } - if (deref.pointee) |*tv| { + const tv = deref.pointee orelse { + deref.pointee = null; + break :blk deref; + }; const coerce_in_mem_ok = - (try sema.coerceInMemoryAllowed(block, payload_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or - (try sema.coerceInMemoryAllowed(block, tv.ty, payload_ptr.container_ty, false, target, src, src)) == .ok; - if (coerce_in_mem_ok) { - const payload_val = switch (ptr_val.tag()) { - .eu_payload_ptr => if (tv.val.castTag(.eu_payload)) |some| some.data else { - return sema.fail(block, src, "attempt to unwrap error: {s}", .{tv.val.castTag(.@"error").?.data.name}); + (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or + (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok; + if (!coerce_in_mem_ok) { + deref.pointee = null; + break :blk deref; + } + + if (container_ty.isSlice(mod)) { + deref.pointee = switch (field_index) { + Value.slice_ptr_index => TypedValue{ + .ty = container_ty.slicePtrFieldType(mod), + .val = tv.val.slicePtr(mod), }, - .opt_payload_ptr => if (tv.val.castTag(.opt_payload)) |some| some.data else opt: { - if (tv.val.isNull()) return sema.fail(block, src, "attempt to use null value", .{}); - break :opt tv.val; + Value.slice_len_index => TypedValue{ + .ty = Type.usize, + .val = mod.intern_pool.indexToKey(try tv.val.intern(tv.ty, mod)).ptr.len.toValue(), }, else => unreachable, }; - tv.* = TypedValue{ .ty = payload_ty, .val = payload_val }; - break :blk deref; + } else { + const field_ty = container_ty.structFieldType(field_index, mod); + deref.pointee = TypedValue{ + .ty = field_ty, + .val = try tv.val.fieldValue(mod, field_index), + }; } - } - deref.pointee = null; - break :blk deref; - }, - .null_value => { - return sema.fail(block, src, "attempt to use null value", .{}); + break :blk deref; + }, }, - .opt_payload => blk: { - const opt_payload = ptr_val.castTag(.opt_payload).?.data; - break :blk try sema.beginComptimePtrLoad(block, src, opt_payload, null); + .opt => |opt| switch (opt.val) { + .none => return sema.fail(block, src, "attempt to use null value", .{}), + else => |payload| try sema.beginComptimePtrLoad(block, src, payload.toValue(), null), }, - - .zero, - .one, - .int_u64, - .int_i64, - .int_big_positive, - .int_big_negative, - .variable, - .extern_fn, - .function, - => return error.RuntimeLoad, - else => unreachable, }; if (deref.pointee) |tv| { - if (deref.parent == null and tv.ty.hasWellDefinedLayout()) { + if (deref.parent == null and tv.ty.hasWellDefinedLayout(mod)) { deref.parent = .{ .tv = tv, .byte_offset = 0 }; } } @@ -27868,21 +29345,21 @@ fn bitCast( inst_src: LazySrcLoc, operand_src: ?LazySrcLoc, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved); try sema.resolveTypeLayout(dest_ty); const old_ty = try sema.resolveTypeFields(sema.typeOf(inst)); try sema.resolveTypeLayout(old_ty); - const target = sema.mod.getTarget(); - const dest_bits = dest_ty.bitSize(target); - const old_bits = old_ty.bitSize(target); + const dest_bits = dest_ty.bitSize(mod); + const old_bits = old_ty.bitSize(mod); if (old_bits != dest_bits) { return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{ - dest_ty.fmt(sema.mod), + dest_ty.fmt(mod), dest_bits, - old_ty.fmt(sema.mod), + old_ty.fmt(mod), old_bits, }); } @@ -27905,20 +29382,21 @@ fn bitCastVal( new_ty: Type, buffer_offset: usize, ) !?Value { - const target = sema.mod.getTarget(); - if (old_ty.eql(new_ty, sema.mod)) return val; + const mod = sema.mod; + if (old_ty.eql(new_ty, mod)) return val; // For types with well-defined memory layouts, we serialize them a byte buffer, // then deserialize to the new type. - const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target)); + const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(mod)); const buffer = try sema.gpa.alloc(u8, abi_size); defer sema.gpa.free(buffer); - val.writeToMemory(old_ty, sema.mod, buffer) catch |err| switch (err) { + val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, error.ReinterpretDeclRef => return null, error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already - error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(sema.mod)}), + error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}), }; - return try Value.readFromMemory(new_ty, sema.mod, buffer[buffer_offset..], sema.arena); + return try Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena); } fn coerceArrayPtrToSlice( @@ -27928,25 +29406,32 @@ fn coerceArrayPtrToSlice( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; if (try sema.resolveMaybeUndefVal(inst)) |val| { const ptr_array_ty = sema.typeOf(inst); - const array_ty = ptr_array_ty.childType(); - const slice_val = try Value.Tag.slice.create(sema.arena, .{ - .ptr = val, - .len = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()), - }); - return sema.addConstant(dest_ty, slice_val); + const array_ty = ptr_array_ty.childType(mod); + const slice_val = try mod.intern(.{ .ptr = .{ + .ty = dest_ty.toIntern(), + .addr = switch (mod.intern_pool.indexToKey(val.toIntern())) { + .undef => .{ .int = try mod.intern(.{ .undef = .usize_type }) }, + .ptr => |ptr| ptr.addr, + else => unreachable, + }, + .len = (try mod.intValue(Type.usize, array_ty.arrayLen(mod))).toIntern(), + } }); + return sema.addConstant(dest_ty, slice_val.toValue()); } try sema.requireRuntimeBlock(block, inst_src, null); return block.addTyOp(.array_to_slice, dest_ty, inst); } fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool { - const dest_info = dest_ty.ptrInfo().data; - const inst_info = inst_ty.ptrInfo().data; - const len0 = (inst_info.pointee_type.zigTypeTag() == .Array and (inst_info.pointee_type.arrayLenIncludingSentinel() == 0 or - (inst_info.pointee_type.arrayLen() == 0 and dest_info.sentinel == null and dest_info.size != .C and dest_info.size != .Many))) or - (inst_info.pointee_type.isTuple() and inst_info.pointee_type.structFieldCount() == 0); + const mod = sema.mod; + const dest_info = dest_ty.ptrInfo(mod); + const inst_info = inst_ty.ptrInfo(mod); + const len0 = (inst_info.pointee_type.zigTypeTag(mod) == .Array and (inst_info.pointee_type.arrayLenIncludingSentinel(mod) == 0 or + (inst_info.pointee_type.arrayLen(mod) == 0 and dest_info.sentinel == null and dest_info.size != .C and dest_info.size != .Many))) or + (inst_info.pointee_type.isTuple(mod) and inst_info.pointee_type.structFieldCount(mod) == 0); const ok_cv_qualifiers = ((inst_info.mutable or !dest_info.mutable) or len0) and @@ -27970,17 +29455,16 @@ fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_resul } if (inst_info.@"align" == 0 and dest_info.@"align" == 0) return true; if (len0) return true; - const target = sema.mod.getTarget(); const inst_align = if (inst_info.@"align" != 0) inst_info.@"align" else - inst_info.pointee_type.abiAlignment(target); + inst_info.pointee_type.abiAlignment(mod); const dest_align = if (dest_info.@"align" != 0) dest_info.@"align" else - dest_info.pointee_type.abiAlignment(target); + dest_info.pointee_type.abiAlignment(mod); if (dest_align > inst_align) { in_memory_result.* = .{ .ptr_alignment = .{ @@ -27999,26 +29483,30 @@ fn coerceCompatiblePtrs( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { + const mod = sema.mod; const inst_ty = sema.typeOf(inst); if (try sema.resolveMaybeUndefVal(inst)) |val| { - if (!val.isUndef() and val.isNull() and !dest_ty.isAllowzeroPtr()) { + if (!val.isUndef(mod) and val.isNull(mod) and !dest_ty.isAllowzeroPtr(mod)) { return sema.fail(block, inst_src, "null pointer casted to type '{}'", .{dest_ty.fmt(sema.mod)}); } // The comptime Value representation is compatible with both types. - return sema.addConstant(dest_ty, val); + return sema.addConstant( + dest_ty, + try mod.getCoerced((try val.intern(inst_ty, mod)).toValue(), dest_ty), + ); } try sema.requireRuntimeBlock(block, inst_src, null); - const inst_allows_zero = inst_ty.zigTypeTag() != .Pointer or inst_ty.ptrAllowsZero(); - if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero() and - (try sema.typeHasRuntimeBits(dest_ty.elemType2()) or dest_ty.elemType2().zigTypeTag() == .Fn)) + const inst_allows_zero = inst_ty.zigTypeTag(mod) != .Pointer or inst_ty.ptrAllowsZero(mod); + if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(mod) and + (try sema.typeHasRuntimeBits(dest_ty.elemType2(mod)) or dest_ty.elemType2(mod).zigTypeTag(mod) == .Fn)) { - const actual_ptr = if (inst_ty.isSlice()) + const actual_ptr = if (inst_ty.isSlice(mod)) try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty) else inst; const ptr_int = try block.addUnOp(.ptrtoint, actual_ptr); const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); - const ok = if (inst_ty.isSlice()) ok: { + const ok = if (inst_ty.isSlice(mod)) ok: { const len = try sema.analyzeSliceLen(block, inst_src, inst); const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero); @@ -28036,9 +29524,11 @@ fn coerceEnumToUnion( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { + const mod = sema.mod; + const ip = &mod.intern_pool; const inst_ty = sema.typeOf(inst); - const tag_ty = union_ty.unionTagType() orelse { + const tag_ty = union_ty.unionTagType(mod) orelse { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), @@ -28065,16 +29555,18 @@ fn coerceEnumToUnion( return sema.failWithOwnedErrorMsg(msg); }; - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const field = union_obj.fields.values()[field_index]; const field_ty = try sema.resolveTypeFields(field.ty); - if (field_ty.zigTypeTag() == .NoReturn) { + if (field_ty.zigTypeTag(mod) == .NoReturn) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{}); errdefer msg.destroy(sema.gpa); const field_name = union_obj.fields.keys()[field_index]; - try sema.addFieldErrNote(union_ty, field_index, msg, "field '{s}' declared here", .{field_name}); + try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ + field_name.fmt(ip), + }); try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; }; @@ -28083,27 +29575,27 @@ fn coerceEnumToUnion( const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse { const msg = msg: { const field_name = union_obj.fields.keys()[field_index]; - const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{s}'", .{ - inst_ty.fmt(sema.mod), union_ty.fmt(sema.mod), field_ty.fmt(sema.mod), field_name, + const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{}'", .{ + inst_ty.fmt(sema.mod), union_ty.fmt(sema.mod), + field_ty.fmt(sema.mod), field_name.fmt(ip), }); errdefer msg.destroy(sema.gpa); - try sema.addFieldErrNote(union_ty, field_index, msg, "field '{s}' declared here", .{field_name}); + try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ + field_name.fmt(ip), + }); try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); }; - return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{ - .tag = val, - .val = opv, - })); + return sema.addConstant(union_ty, try mod.unionValue(union_ty, val, opv)); } try sema.requireRuntimeBlock(block, inst_src, null); - if (tag_ty.isNonexhaustiveEnum()) { + if (tag_ty.isNonexhaustiveEnum(mod)) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "runtime coercion to union '{}' from non-exhaustive enum", .{ union_ty.fmt(sema.mod), @@ -28115,13 +29607,13 @@ fn coerceEnumToUnion( return sema.failWithOwnedErrorMsg(msg); } - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; { var msg: ?*Module.ErrorMsg = null; errdefer if (msg) |some| some.destroy(sema.gpa); for (union_obj.fields.values(), 0..) |field, i| { - if (field.ty.zigTypeTag() == .NoReturn) { + if (field.ty.zigTypeTag(mod) == .NoReturn) { const err_msg = msg orelse try sema.errMsg( block, inst_src, @@ -28141,7 +29633,7 @@ fn coerceEnumToUnion( } // If the union has all fields 0 bits, the union value is just the enum value. - if (union_ty.unionHasAllZeroBitFieldTypes()) { + if (union_ty.unionHasAllZeroBitFieldTypes(mod)) { return block.addBitCast(union_ty, enum_tag); } @@ -28159,8 +29651,11 @@ fn coerceEnumToUnion( while (it.next()) |field| : (field_index += 1) { const field_name = field.key_ptr.*; const field_ty = field.value_ptr.ty; - if (!field_ty.hasRuntimeBits()) continue; - try sema.addFieldErrNote(union_ty, field_index, msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(sema.mod) }); + if (!(try sema.typeHasRuntimeBits(field_ty))) continue; + try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' has type '{}'", .{ + field_name.fmt(ip), + field_ty.fmt(sema.mod), + }); } try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; @@ -28176,36 +29671,55 @@ fn coerceAnonStructToUnion( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { + const mod = sema.mod; const inst_ty = sema.typeOf(inst); - const field_count = inst_ty.structFieldCount(); - if (field_count != 1) { - const msg = msg: { - const msg = if (field_count > 1) try sema.errMsg( - block, - inst_src, - "cannot initialize multiple union fields at once; unions can only have one active field", - .{}, - ) else try sema.errMsg( - block, - inst_src, - "union initializer must initialize one field", - .{}, - ); - errdefer msg.destroy(sema.gpa); + const field_info: union(enum) { + name: InternPool.NullTerminatedString, + count: usize, + } = switch (mod.intern_pool.indexToKey(inst_ty.toIntern())) { + .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 1) + .{ .name = anon_struct_type.names[0] } + else + .{ .count = anon_struct_type.names.len }, + .struct_type => |struct_type| name: { + const field_names = mod.structPtrUnwrap(struct_type.index).?.fields.keys(); + break :name if (field_names.len == 1) + .{ .name = field_names[0] } + else + .{ .count = field_names.len }; + }, + else => unreachable, + }; + switch (field_info) { + .name => |field_name| { + const init = try sema.structFieldVal(block, inst_src, inst, field_name, inst_src, inst_ty); + return sema.unionInit(block, init, inst_src, union_ty, union_ty_src, field_name, inst_src); + }, + .count => |field_count| { + assert(field_count != 1); + const msg = msg: { + const msg = if (field_count > 1) try sema.errMsg( + block, + inst_src, + "cannot initialize multiple union fields at once; unions can only have one active field", + .{}, + ) else try sema.errMsg( + block, + inst_src, + "union initializer must initialize one field", + .{}, + ); + errdefer msg.destroy(sema.gpa); - // TODO add notes for where the anon struct was created to point out - // the extra fields. + // TODO add notes for where the anon struct was created to point out + // the extra fields. - try sema.addDeclaredHereNote(msg, union_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(msg); + try sema.addDeclaredHereNote(msg, union_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + }, } - - const anon_struct = inst_ty.castTag(.anon_struct).?.data; - const field_name = anon_struct.names[0]; - const init = try sema.structFieldVal(block, inst_src, inst, field_name, inst_src, inst_ty); - return sema.unionInit(block, init, inst_src, union_ty, union_ty_src, field_name, inst_src); } fn coerceAnonStructToUnionPtrs( @@ -28216,7 +29730,8 @@ fn coerceAnonStructToUnionPtrs( ptr_anon_struct: Air.Inst.Ref, anon_struct_src: LazySrcLoc, ) !Air.Inst.Ref { - const union_ty = ptr_union_ty.childType(); + const mod = sema.mod; + const union_ty = ptr_union_ty.childType(mod); const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src); const union_inst = try sema.coerceAnonStructToUnion(block, union_ty, union_ty_src, anon_struct, anon_struct_src); return sema.analyzeRef(block, union_ty_src, union_inst); @@ -28230,7 +29745,8 @@ fn coerceAnonStructToStructPtrs( ptr_anon_struct: Air.Inst.Ref, anon_struct_src: LazySrcLoc, ) !Air.Inst.Ref { - const struct_ty = ptr_struct_ty.childType(); + const mod = sema.mod; + const struct_ty = ptr_struct_ty.childType(mod); const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src); const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, anon_struct, anon_struct_src); return sema.analyzeRef(block, struct_ty_src, struct_inst); @@ -28245,15 +29761,16 @@ fn coerceArrayLike( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { + const mod = sema.mod; const inst_ty = sema.typeOf(inst); - const inst_len = inst_ty.arrayLen(); - const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen()); - const target = sema.mod.getTarget(); + const inst_len = inst_ty.arrayLen(mod); + const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod)); + const target = mod.getTarget(); if (dest_len != inst_len) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ - dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), + dest_ty.fmt(mod), inst_ty.fmt(mod), }); errdefer msg.destroy(sema.gpa); try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); @@ -28263,35 +29780,32 @@ fn coerceArrayLike( return sema.failWithOwnedErrorMsg(msg); } - const dest_elem_ty = dest_ty.childType(); - const inst_elem_ty = inst_ty.childType(); + const dest_elem_ty = dest_ty.childType(mod); + const inst_elem_ty = inst_ty.childType(mod); const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src); if (in_memory_result == .ok) { if (try sema.resolveMaybeUndefVal(inst)) |inst_val| { // These types share the same comptime value representation. - return sema.addConstant(dest_ty, inst_val); + return sema.coerceInMemory(inst_val, dest_ty); } try sema.requireRuntimeBlock(block, inst_src, null); return block.addBitCast(dest_ty, inst); } - const element_vals = try sema.arena.alloc(Value, dest_len); + const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); var runtime_src: ?LazySrcLoc = null; - for (element_vals, 0..) |*elem, i| { - const index_ref = try sema.addConstant( - Type.usize, - try Value.Tag.int_u64.create(sema.arena, i), - ); + for (element_vals, element_refs, 0..) |*val, *ref, i| { + const index_ref = try sema.addConstant(Type.usize, try mod.intValue(Type.usize, i)); const src = inst_src; // TODO better source location const elem_src = inst_src; // TODO better source location const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true); const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); - element_refs[i] = coerced; + ref.* = coerced; if (runtime_src == null) { if (try sema.resolveMaybeUndefVal(coerced)) |elem_val| { - elem.* = elem_val; + val.* = try elem_val.intern(dest_elem_ty, mod); } else { runtime_src = elem_src; } @@ -28303,10 +29817,10 @@ fn coerceArrayLike( return block.addAggregateInit(dest_ty, element_refs); } - return sema.addConstant( - dest_ty, - try Value.Tag.aggregate.create(sema.arena, element_vals), - ); + return sema.addConstant(dest_ty, (try mod.intern(.{ .aggregate = .{ + .ty = dest_ty.toIntern(), + .storage = .{ .elems = element_vals }, + } })).toValue()); } /// If the lengths match, coerces element-wise. @@ -28318,9 +29832,10 @@ fn coerceTupleToArray( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { + const mod = sema.mod; const inst_ty = sema.typeOf(inst); - const inst_len = inst_ty.arrayLen(); - const dest_len = dest_ty.arrayLen(); + const inst_len = inst_ty.arrayLen(mod); + const dest_len = dest_ty.arrayLen(mod); if (dest_len != inst_len) { const msg = msg: { @@ -28335,26 +29850,27 @@ fn coerceTupleToArray( return sema.failWithOwnedErrorMsg(msg); } - const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLenIncludingSentinel()); - const element_vals = try sema.arena.alloc(Value, dest_elems); + const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len); + const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems); const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems); - const dest_elem_ty = dest_ty.childType(); + const dest_elem_ty = dest_ty.childType(mod); var runtime_src: ?LazySrcLoc = null; - for (element_vals, 0..) |*elem, i_usize| { + for (element_vals, element_refs, 0..) |*val, *ref, i_usize| { const i = @intCast(u32, i_usize); if (i_usize == inst_len) { - elem.* = dest_ty.sentinel().?; - element_refs[i] = try sema.addConstant(dest_elem_ty, elem.*); + const sentinel_val = dest_ty.sentinel(mod).?; + val.* = sentinel_val.toIntern(); + ref.* = try sema.addConstant(dest_elem_ty, sentinel_val); break; } const elem_src = inst_src; // TODO better source location const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i); const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); - element_refs[i] = coerced; + ref.* = coerced; if (runtime_src == null) { if (try sema.resolveMaybeUndefVal(coerced)) |elem_val| { - elem.* = elem_val; + val.* = try elem_val.intern(dest_elem_ty, mod); } else { runtime_src = elem_src; } @@ -28366,10 +29882,10 @@ fn coerceTupleToArray( return block.addAggregateInit(dest_ty, element_refs); } - return sema.addConstant( - dest_ty, - try Value.Tag.aggregate.create(sema.arena, element_vals), - ); + return sema.addConstant(dest_ty, (try mod.intern(.{ .aggregate = .{ + .ty = dest_ty.toIntern(), + .storage = .{ .elems = element_vals }, + } })).toValue()); } /// If the lengths match, coerces element-wise. @@ -28381,10 +29897,11 @@ fn coerceTupleToSlicePtrs( ptr_tuple: Air.Inst.Ref, tuple_src: LazySrcLoc, ) !Air.Inst.Ref { - const tuple_ty = sema.typeOf(ptr_tuple).childType(); + const mod = sema.mod; + const tuple_ty = sema.typeOf(ptr_tuple).childType(mod); const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); - const slice_info = slice_ty.ptrInfo().data; - const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(), slice_info.sentinel, slice_info.pointee_type, sema.mod); + const slice_info = slice_ty.ptrInfo(mod); + const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(mod), slice_info.sentinel, slice_info.pointee_type, sema.mod); const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src); if (slice_info.@"align" != 0) { return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{}); @@ -28402,8 +29919,9 @@ fn coerceTupleToArrayPtrs( ptr_tuple: Air.Inst.Ref, tuple_src: LazySrcLoc, ) !Air.Inst.Ref { + const mod = sema.mod; const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); - const ptr_info = ptr_array_ty.ptrInfo().data; + const ptr_info = ptr_array_ty.ptrInfo(mod); const array_ty = ptr_info.pointee_type; const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src); if (ptr_info.@"align" != 0) { @@ -28422,27 +29940,41 @@ fn coerceTupleToStruct( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { + const mod = sema.mod; + const ip = &mod.intern_pool; const struct_ty = try sema.resolveTypeFields(dest_ty); - if (struct_ty.isTupleOrAnonStruct()) { + if (struct_ty.isTupleOrAnonStruct(mod)) { return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src); } - const fields = struct_ty.structFields(); - const field_vals = try sema.arena.alloc(Value, fields.count()); + const fields = struct_ty.structFields(mod); + const field_vals = try sema.arena.alloc(InternPool.Index, fields.count()); const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); @memset(field_refs, .none); const inst_ty = sema.typeOf(inst); var runtime_src: ?LazySrcLoc = null; - const field_count = inst_ty.structFieldCount(); - var field_i: u32 = 0; - while (field_i < field_count) : (field_i += 1) { - const field_src = inst_src; // TODO better source location - const field_name = if (inst_ty.castTag(.anon_struct)) |payload| - payload.data.names[field_i] + const field_count = switch (ip.indexToKey(inst_ty.toIntern())) { + .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, + .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| + struct_obj.fields.count() else - try std.fmt.allocPrint(sema.arena, "{d}", .{field_i}); + 0, + else => unreachable, + }; + for (0..field_count) |field_index_usize| { + const field_i = @intCast(u32, field_index_usize); + const field_src = inst_src; // TODO better source location + // https://github.com/ziglang/zig/issues/15709 + const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) { + .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0) + anon_struct_type.names[field_i] + else + try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), + .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i], + else => unreachable, + }; const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src); const field = fields.values()[field_index]; const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); @@ -28453,13 +29985,13 @@ fn coerceTupleToStruct( return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known"); }; - if (!init_val.eql(field.default_val, field.ty, sema.mod)) { + if (!init_val.eql(field.default_val.toValue(), field.ty, sema.mod)) { return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); } } if (runtime_src == null) { if (try sema.resolveMaybeUndefVal(coerced)) |field_val| { - field_vals[field_index] = field_val; + field_vals[field_index] = field_val.toIntern(); } else { runtime_src = field_src; } @@ -28476,9 +30008,9 @@ fn coerceTupleToStruct( const field_name = fields.keys()[i]; const field = fields.values()[i]; const field_src = inst_src; // TODO better source location - if (field.default_val.tag() == .unreachable_value) { - const template = "missing struct field: {s}"; - const args = .{field_name}; + if (field.default_val == .none) { + const template = "missing struct field: {}"; + const args = .{field_name.fmt(ip)}; if (root_msg) |msg| { try sema.errNote(block, field_src, msg, template, args); } else { @@ -28489,7 +30021,7 @@ fn coerceTupleToStruct( if (runtime_src == null) { field_vals[i] = field.default_val; } else { - field_ref.* = try sema.addConstant(field.ty, field.default_val); + field_ref.* = try sema.addConstant(field.ty, field.default_val.toValue()); } } @@ -28504,10 +30036,14 @@ fn coerceTupleToStruct( return block.addAggregateInit(struct_ty, field_refs); } - return sema.addConstant( - struct_ty, - try Value.Tag.aggregate.create(sema.arena, field_vals), - ); + const struct_val = try mod.intern(.{ .aggregate = .{ + .ty = struct_ty.toIntern(), + .storage = .{ .elems = field_vals }, + } }); + // TODO: figure out InternPool removals for incremental compilation + //errdefer ip.remove(struct_val); + + return sema.addConstant(struct_ty, struct_val.toValue()); } fn coerceTupleToTuple( @@ -28517,47 +30053,76 @@ fn coerceTupleToTuple( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { - const dest_field_count = tuple_ty.structFieldCount(); - const field_vals = try sema.arena.alloc(Value, dest_field_count); + const mod = sema.mod; + const ip = &mod.intern_pool; + const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) { + .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, + .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| + struct_obj.fields.count() + else + 0, + else => unreachable, + }; + const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count); const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); @memset(field_refs, .none); const inst_ty = sema.typeOf(inst); - const inst_field_count = inst_ty.structFieldCount(); - if (inst_field_count > dest_field_count) return error.NotCoercible; + const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) { + .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, + .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| + struct_obj.fields.count() + else + 0, + else => unreachable, + }; + if (src_field_count > dest_field_count) return error.NotCoercible; var runtime_src: ?LazySrcLoc = null; - var field_i: u32 = 0; - while (field_i < inst_field_count) : (field_i += 1) { + for (0..dest_field_count) |field_index_usize| { + const field_i = @intCast(u32, field_index_usize); const field_src = inst_src; // TODO better source location - const field_name = if (inst_ty.castTag(.anon_struct)) |payload| - payload.data.names[field_i] - else - try std.fmt.allocPrint(sema.arena, "{d}", .{field_i}); + // https://github.com/ziglang/zig/issues/15709 + const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) { + .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0) + anon_struct_type.names[field_i] + else + try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), + .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i], + else => unreachable, + }; - if (mem.eql(u8, field_name, "len")) { + if (ip.stringEqlSlice(field_name, "len")) return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{}); - } + + const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) { + .anon_struct_type => |anon_struct_type| anon_struct_type.types[field_index_usize].toType(), + .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].ty, + else => unreachable, + }; + const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { + .anon_struct_type => |anon_struct_type| anon_struct_type.values[field_index_usize], + .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].default_val, + else => unreachable, + }; const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src); - const field_ty = tuple_ty.structFieldType(field_i); - const default_val = tuple_ty.structFieldDefaultValue(field_i); const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); const coerced = try sema.coerce(block, field_ty, elem_ref, field_src); field_refs[field_index] = coerced; - if (default_val.tag() != .unreachable_value) { + if (default_val != .none) { const init_val = (try sema.resolveMaybeUndefVal(coerced)) orelse { return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known"); }; - if (!init_val.eql(default_val, field_ty, sema.mod)) { + if (!init_val.eql(default_val.toValue(), field_ty, sema.mod)) { return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); } } if (runtime_src == null) { if (try sema.resolveMaybeUndefVal(coerced)) |field_val| { - field_vals[field_index] = field_val; + field_vals[field_index] = field_val.toIntern(); } else { runtime_src = field_src; } @@ -28571,12 +30136,15 @@ fn coerceTupleToTuple( for (field_refs, 0..) |*field_ref, i| { if (field_ref.* != .none) continue; - const default_val = tuple_ty.structFieldDefaultValue(i); - const field_ty = tuple_ty.structFieldType(i); + const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { + .anon_struct_type => |anon_struct_type| anon_struct_type.values[i], + .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[i].default_val, + else => unreachable, + }; const field_src = inst_src; // TODO better source location - if (default_val.tag() == .unreachable_value) { - if (tuple_ty.isTuple()) { + if (default_val == .none) { + if (tuple_ty.isTuple(mod)) { const template = "missing tuple field: {d}"; if (root_msg) |msg| { try sema.errNote(block, field_src, msg, template, .{i}); @@ -28585,8 +30153,8 @@ fn coerceTupleToTuple( } continue; } - const template = "missing struct field: {s}"; - const args = .{tuple_ty.structFieldName(i)}; + const template = "missing struct field: {}"; + const args = .{tuple_ty.structFieldName(i, mod).fmt(ip)}; if (root_msg) |msg| { try sema.errNote(block, field_src, msg, template, args); } else { @@ -28597,7 +30165,12 @@ fn coerceTupleToTuple( if (runtime_src == null) { field_vals[i] = default_val; } else { - field_ref.* = try sema.addConstant(field_ty, default_val); + const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) { + .anon_struct_type => |anon_struct_type| anon_struct_type.types[i].toType(), + .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[i].ty, + else => unreachable, + }; + field_ref.* = try sema.addConstant(field_ty, default_val.toValue()); } } @@ -28614,7 +30187,10 @@ fn coerceTupleToTuple( return sema.addConstant( tuple_ty, - try Value.Tag.aggregate.create(sema.arena, field_vals), + (try mod.intern(.{ .aggregate = .{ + .ty = tuple_ty.toIntern(), + .storage = .{ .elems = field_vals }, + } })).toValue(), ); } @@ -28628,10 +30204,10 @@ fn analyzeDeclVal( if (sema.decl_val_table.get(decl_index)) |result| { return result; } - const decl_ref = try sema.analyzeDeclRef(decl_index); + const decl_ref = try sema.analyzeDeclRefInner(decl_index, false); const result = try sema.analyzeLoad(block, src, decl_ref, src); if (Air.refToIndex(result)) |index| { - if (sema.air_instructions.items(.tag)[index] == .constant and !block.is_typeof) { + if (sema.air_instructions.items(.tag)[index] == .interned and !block.is_typeof) { try sema.decl_val_table.put(sema.gpa, decl_index, result); } } @@ -28652,13 +30228,14 @@ fn addReferencedBy( } fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void { - const decl = sema.mod.declPtr(decl_index); + const mod = sema.mod; + const decl = mod.declPtr(decl_index); if (decl.analysis == .in_progress) { - const msg = try Module.ErrorMsg.create(sema.gpa, decl.srcLoc(), "dependency loop detected", .{}); + const msg = try Module.ErrorMsg.create(sema.gpa, decl.srcLoc(mod), "dependency loop detected", .{}); return sema.failWithOwnedErrorMsg(msg); } - sema.mod.ensureDeclAnalyzed(decl_index) catch |err| { + mod.ensureDeclAnalyzed(decl_index) catch |err| { if (sema.owner_func) |owner_func| { owner_func.state = .dependency_failure; } else { @@ -28668,7 +30245,7 @@ fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void { }; } -fn ensureFuncBodyAnalyzed(sema: *Sema, func: *Module.Fn) CompileError!void { +fn ensureFuncBodyAnalyzed(sema: *Sema, func: Module.Fn.Index) CompileError!void { sema.mod.ensureFuncBodyAnalyzed(func) catch |err| { if (sema.owner_func) |owner_func| { owner_func.state = .dependency_failure; @@ -28680,49 +30257,75 @@ fn ensureFuncBodyAnalyzed(sema: *Sema, func: *Module.Fn) CompileError!void { } fn refValue(sema: *Sema, block: *Block, ty: Type, val: Value) !Value { + const mod = sema.mod; var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); const decl = try anon_decl.finish( - try ty.copy(anon_decl.arena()), - try val.copy(anon_decl.arena()), + ty, + val, 0, // default alignment ); - try sema.mod.declareDeclDependency(sema.owner_decl_index, decl); - return try Value.Tag.decl_ref.create(sema.arena, decl); + try sema.maybeQueueFuncBodyAnalysis(decl); + try mod.declareDeclDependency(sema.owner_decl_index, decl); + const result = try mod.intern(.{ .ptr = .{ + .ty = (try mod.singleConstPtrType(ty)).toIntern(), + .addr = .{ .decl = decl }, + } }); + return result.toValue(); } fn optRefValue(sema: *Sema, block: *Block, ty: Type, opt_val: ?Value) !Value { - const val = opt_val orelse return Value.null; - const ptr_val = try sema.refValue(block, ty, val); - const result = try Value.Tag.opt_payload.create(sema.arena, ptr_val); - return result; + const mod = sema.mod; + const ptr_anyopaque_ty = try mod.singleConstPtrType(Type.anyopaque); + return (try mod.intern(.{ .opt = .{ + .ty = (try mod.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(), + .val = if (opt_val) |val| (try mod.getCoerced( + try sema.refValue(block, ty, val), + ptr_anyopaque_ty, + )).toIntern() else .none, + } })).toValue(); } fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref { - try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index); + return sema.analyzeDeclRefInner(decl_index, true); +} + +/// Analyze a reference to the decl at the given index. Ensures the underlying decl is analyzed, but +/// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a +/// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeDeclRef` wraps +/// this function with `analyze_fn_body` set to true. +fn analyzeDeclRefInner(sema: *Sema, decl_index: Decl.Index, analyze_fn_body: bool) CompileError!Air.Inst.Ref { + const mod = sema.mod; + try mod.declareDeclDependency(sema.owner_decl_index, decl_index); try sema.ensureDeclAnalyzed(decl_index); - const decl = sema.mod.declPtr(decl_index); + const decl = mod.declPtr(decl_index); const decl_tv = try decl.typedValue(); - if (decl_tv.val.castTag(.variable)) |payload| { - const variable = payload.data; - const ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = decl_tv.ty, - .mutable = variable.is_mutable, - .@"addrspace" = decl.@"addrspace", - .@"align" = decl.@"align", - }); - return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl_index)); + const ptr_ty = try mod.ptrType(.{ + .child = decl_tv.ty.toIntern(), + .flags = .{ + .alignment = InternPool.Alignment.fromByteUnits(decl.@"align"), + .is_const = if (decl.val.getVariable(mod)) |variable| variable.is_const else true, + .address_space = decl.@"addrspace", + }, + }); + if (analyze_fn_body) { + try sema.maybeQueueFuncBodyAnalysis(decl_index); } - return sema.addConstant( - try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = decl_tv.ty, - .mutable = false, - .@"addrspace" = decl.@"addrspace", - .@"align" = decl.@"align", - }), - try Value.Tag.decl_ref.create(sema.arena, decl_index), - ); + return sema.addConstant(ptr_ty, (try mod.intern(.{ .ptr = .{ + .ty = ptr_ty.toIntern(), + .addr = .{ .decl = decl_index }, + } })).toValue()); +} + +fn maybeQueueFuncBodyAnalysis(sema: *Sema, decl_index: Decl.Index) !void { + const mod = sema.mod; + const decl = mod.declPtr(decl_index); + const tv = try decl.typedValue(); + if (tv.ty.zigTypeTag(mod) != .Fn) return; + if (!try sema.fnHasRuntimeBits(tv.ty)) return; + const func_index = mod.intern_pool.indexToFunc(tv.val.toIntern()).unwrap() orelse return; // undef or extern_fn + try mod.ensureFuncBodyAnalysisQueued(func_index); } fn analyzeRef( @@ -28734,18 +30337,16 @@ fn analyzeRef( const operand_ty = sema.typeOf(operand); if (try sema.resolveMaybeUndefVal(operand)) |val| { - switch (val.tag()) { - .extern_fn, .function => { - const decl_index = val.pointerDecl().?; - return sema.analyzeDeclRef(decl_index); - }, + switch (sema.mod.intern_pool.indexToKey(val.toIntern())) { + .extern_func => |extern_func| return sema.analyzeDeclRef(extern_func.decl), + .func => |func| return sema.analyzeDeclRef(sema.mod.funcPtr(func.index).owner_decl), else => {}, } var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); return sema.analyzeDeclRef(try anon_decl.finish( - try operand_ty.copy(anon_decl.arena()), - try val.copy(anon_decl.arena()), + operand_ty, + val, 0, // default alignment )); } @@ -28775,9 +30376,10 @@ fn analyzeLoad( ptr: Air.Inst.Ref, ptr_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const ptr_ty = sema.typeOf(ptr); - const elem_ty = switch (ptr_ty.zigTypeTag()) { - .Pointer => ptr_ty.childType(), + const elem_ty = switch (ptr_ty.zigTypeTag(mod)) { + .Pointer => ptr_ty.childType(mod), else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}), }; @@ -28787,11 +30389,11 @@ fn analyzeLoad( if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| { - return sema.addConstant(elem_ty, elem_val); + return sema.addConstant(elem_ty, try mod.getCoerced(elem_val, elem_ty)); } } - if (ptr_ty.ptrInfo().data.vector_index == .runtime) { + if (ptr_ty.ptrInfo(mod).vector_index == .runtime) { const ptr_inst = Air.refToIndex(ptr).?; const air_tags = sema.air_instructions.items(.tag); if (air_tags[ptr_inst] == .ptr_elem_ptr) { @@ -28814,11 +30416,11 @@ fn analyzeSlicePtr( slice: Air.Inst.Ref, slice_ty: Type, ) CompileError!Air.Inst.Ref { - const buf = try sema.arena.create(Type.SlicePtrFieldTypeBuffer); - const result_ty = slice_ty.slicePtrFieldType(buf); + const mod = sema.mod; + const result_ty = slice_ty.slicePtrFieldType(mod); if (try sema.resolveMaybeUndefVal(slice)) |val| { - if (val.isUndef()) return sema.addConstUndef(result_ty); - return sema.addConstant(result_ty, val.slicePtr()); + if (val.isUndef(mod)) return sema.addConstUndef(result_ty); + return sema.addConstant(result_ty, val.slicePtr(mod)); } try sema.requireRuntimeBlock(block, slice_src, null); return block.addTyOp(.slice_ptr, result_ty, slice); @@ -28830,8 +30432,9 @@ fn analyzeSliceLen( src: LazySrcLoc, slice_inst: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; if (try sema.resolveMaybeUndefVal(slice_inst)) |slice_val| { - if (slice_val.isUndef()) { + if (slice_val.isUndef(mod)) { return sema.addConstUndef(Type.usize); } return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)); @@ -28847,12 +30450,13 @@ fn analyzeIsNull( operand: Air.Inst.Ref, invert_logic: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const result_ty = Type.bool; if (try sema.resolveMaybeUndefVal(operand)) |opt_val| { - if (opt_val.isUndef()) { + if (opt_val.isUndef(mod)) { return sema.addConstUndef(result_ty); } - const is_null = opt_val.isNull(); + const is_null = opt_val.isNull(mod); const bool_value = if (invert_logic) !is_null else is_null; if (bool_value) { return Air.Inst.Ref.bool_true; @@ -28863,11 +30467,10 @@ fn analyzeIsNull( const inverted_non_null_res = if (invert_logic) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; const operand_ty = sema.typeOf(operand); - var buf: Type.Payload.ElemType = undefined; - if (operand_ty.zigTypeTag() == .Optional and operand_ty.optionalChild(&buf).zigTypeTag() == .NoReturn) { + if (operand_ty.zigTypeTag(mod) == .Optional and operand_ty.optionalChild(mod).zigTypeTag(mod) == .NoReturn) { return inverted_non_null_res; } - if (operand_ty.zigTypeTag() != .Optional and !operand_ty.isPtrLikeOptional()) { + if (operand_ty.zigTypeTag(mod) != .Optional and !operand_ty.isPtrLikeOptional(mod)) { return inverted_non_null_res; } try sema.requireRuntimeBlock(block, src, null); @@ -28881,11 +30484,12 @@ fn analyzePtrIsNonErrComptimeOnly( src: LazySrcLoc, operand: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const ptr_ty = sema.typeOf(operand); - assert(ptr_ty.zigTypeTag() == .Pointer); - const child_ty = ptr_ty.childType(); + assert(ptr_ty.zigTypeTag(mod) == .Pointer); + const child_ty = ptr_ty.childType(mod); - const child_tag = child_ty.zigTypeTag(); + const child_tag = child_ty.zigTypeTag(mod); if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return Air.Inst.Ref.bool_true; if (child_tag == .ErrorSet) return Air.Inst.Ref.bool_false; assert(child_tag == .ErrorUnion); @@ -28902,14 +30506,15 @@ fn analyzeIsNonErrComptimeOnly( src: LazySrcLoc, operand: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const operand_ty = sema.typeOf(operand); - const ot = operand_ty.zigTypeTag(); + const ot = operand_ty.zigTypeTag(mod); if (ot != .ErrorSet and ot != .ErrorUnion) return Air.Inst.Ref.bool_true; if (ot == .ErrorSet) return Air.Inst.Ref.bool_false; assert(ot == .ErrorUnion); - const payload_ty = operand_ty.errorUnionPayload(); - if (payload_ty.zigTypeTag() == .NoReturn) { + const payload_ty = operand_ty.errorUnionPayload(mod); + if (payload_ty.zigTypeTag(mod) == .NoReturn) { return Air.Inst.Ref.bool_false; } @@ -28930,50 +30535,56 @@ fn analyzeIsNonErrComptimeOnly( // exception if the error union error set is known to be empty, // we allow the comparison but always make it comptime-known. - const set_ty = operand_ty.errorUnionSet(); - switch (set_ty.tag()) { - .anyerror => {}, - .error_set_inferred => blk: { - // If the error set is empty, we must return a comptime true or false. - // However we want to avoid unnecessarily resolving an inferred error set - // in case it is already non-empty. - const ies = set_ty.castTag(.error_set_inferred).?.data; - if (ies.is_anyerror) break :blk; - if (ies.errors.count() != 0) break :blk; - if (maybe_operand_val == null) { - // Try to avoid resolving inferred error set if possible. - if (ies.errors.count() != 0) break :blk; + const set_ty = operand_ty.errorUnionSet(mod); + switch (set_ty.toIntern()) { + .anyerror_type => {}, + else => switch (mod.intern_pool.indexToKey(set_ty.toIntern())) { + .error_set_type => |error_set_type| { + if (error_set_type.names.len == 0) return Air.Inst.Ref.bool_true; + }, + .inferred_error_set_type => |ies_index| blk: { + // If the error set is empty, we must return a comptime true or false. + // However we want to avoid unnecessarily resolving an inferred error set + // in case it is already non-empty. + const ies = mod.inferredErrorSetPtr(ies_index); if (ies.is_anyerror) break :blk; - for (ies.inferred_error_sets.keys()) |other_ies| { - if (ies == other_ies) continue; - try sema.resolveInferredErrorSet(block, src, other_ies); - if (other_ies.is_anyerror) { - ies.is_anyerror = true; - ies.is_resolved = true; - break :blk; - } + if (ies.errors.count() != 0) break :blk; + if (maybe_operand_val == null) { + // Try to avoid resolving inferred error set if possible. + if (ies.errors.count() != 0) break :blk; + if (ies.is_anyerror) break :blk; + for (ies.inferred_error_sets.keys()) |other_ies_index| { + if (ies_index == other_ies_index) continue; + try sema.resolveInferredErrorSet(block, src, other_ies_index); + const other_ies = mod.inferredErrorSetPtr(other_ies_index); + if (other_ies.is_anyerror) { + ies.is_anyerror = true; + ies.is_resolved = true; + break :blk; + } - if (other_ies.errors.count() != 0) break :blk; - } - if (ies.func == sema.owner_func) { - // We're checking the inferred errorset of the current function and none of - // its child inferred error sets contained any errors meaning that any value - // so far with this type can't contain errors either. - return Air.Inst.Ref.bool_true; + if (other_ies.errors.count() != 0) break :blk; + } + if (ies.func == sema.owner_func_index.unwrap()) { + // We're checking the inferred errorset of the current function and none of + // its child inferred error sets contained any errors meaning that any value + // so far with this type can't contain errors either. + return Air.Inst.Ref.bool_true; + } + try sema.resolveInferredErrorSet(block, src, ies_index); + if (ies.is_anyerror) break :blk; + if (ies.errors.count() == 0) return Air.Inst.Ref.bool_true; } - try sema.resolveInferredErrorSet(block, src, ies); - if (ies.is_anyerror) break :blk; - if (ies.errors.count() == 0) return Air.Inst.Ref.bool_true; - } + }, + else => unreachable, }, - else => if (set_ty.errorSetNames().len == 0) return Air.Inst.Ref.bool_true, } if (maybe_operand_val) |err_union| { - if (err_union.isUndef()) { + if (err_union.isUndef(mod)) { return sema.addConstUndef(Type.bool); } - if (err_union.getError() == null) { + if (err_union.getErrorName(mod) == .none) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; @@ -29024,73 +30635,80 @@ fn analyzeSlice( ptr_src: LazySrcLoc, start_src: LazySrcLoc, end_src: LazySrcLoc, + by_length: bool, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; // Slice expressions can operate on a variable whose type is an array. This requires // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer. const ptr_ptr_ty = sema.typeOf(ptr_ptr); - const target = sema.mod.getTarget(); - const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag()) { - .Pointer => ptr_ptr_ty.elemType(), - else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(sema.mod)}), + const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(mod)) { + .Pointer => ptr_ptr_ty.childType(mod), + else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(mod)}), }; - const mod = sema.mod; var array_ty = ptr_ptr_child_ty; var slice_ty = ptr_ptr_ty; var ptr_or_slice = ptr_ptr; var elem_ty: Type = undefined; var ptr_sentinel: ?Value = null; - switch (ptr_ptr_child_ty.zigTypeTag()) { + switch (ptr_ptr_child_ty.zigTypeTag(mod)) { .Array => { - ptr_sentinel = ptr_ptr_child_ty.sentinel(); - elem_ty = ptr_ptr_child_ty.childType(); + ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); + elem_ty = ptr_ptr_child_ty.childType(mod); }, - .Pointer => switch (ptr_ptr_child_ty.ptrSize()) { + .Pointer => switch (ptr_ptr_child_ty.ptrSize(mod)) { .One => { - const double_child_ty = ptr_ptr_child_ty.childType(); - if (double_child_ty.zigTypeTag() == .Array) { - ptr_sentinel = double_child_ty.sentinel(); + const double_child_ty = ptr_ptr_child_ty.childType(mod); + if (double_child_ty.zigTypeTag(mod) == .Array) { + ptr_sentinel = double_child_ty.sentinel(mod); ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); slice_ty = ptr_ptr_child_ty; array_ty = double_child_ty; - elem_ty = double_child_ty.childType(); + elem_ty = double_child_ty.childType(mod); } else { return sema.fail(block, src, "slice of single-item pointer", .{}); } }, .Many, .C => { - ptr_sentinel = ptr_ptr_child_ty.sentinel(); + ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); slice_ty = ptr_ptr_child_ty; array_ty = ptr_ptr_child_ty; - elem_ty = ptr_ptr_child_ty.childType(); + elem_ty = ptr_ptr_child_ty.childType(mod); - if (ptr_ptr_child_ty.ptrSize() == .C) { + if (ptr_ptr_child_ty.ptrSize(mod) == .C) { if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| { - if (ptr_val.isNull()) { + if (ptr_val.isNull(mod)) { return sema.fail(block, src, "slice of null pointer", .{}); } } } }, .Slice => { - ptr_sentinel = ptr_ptr_child_ty.sentinel(); + ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); slice_ty = ptr_ptr_child_ty; array_ty = ptr_ptr_child_ty; - elem_ty = ptr_ptr_child_ty.childType(); + elem_ty = ptr_ptr_child_ty.childType(mod); }, }, else => return sema.fail(block, src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(mod)}), } - const ptr = if (slice_ty.isSlice()) + const ptr = if (slice_ty.isSlice(mod)) try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty) - else - ptr_or_slice; + else if (array_ty.zigTypeTag(mod) == .Array) ptr: { + var manyptr_ty_key = mod.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type; + assert(manyptr_ty_key.child == array_ty.toIntern()); + 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 mod.ptrType(manyptr_ty_key), ptr_or_slice, ptr_src); + } else ptr_or_slice; const start = try sema.coerce(block, Type.usize, uncasted_start, start_src); const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); + const new_ptr_ty = sema.typeOf(new_ptr); // true if and only if the end index of the slice, implicitly or explicitly, equals // the length of the underlying object being sliced. we might learn the length of the @@ -29098,18 +30716,22 @@ fn analyzeSlice( // we might learn of the length because it is a comptime-known slice value. var end_is_len = uncasted_end_opt == .none; const end = e: { - if (array_ty.zigTypeTag() == .Array) { - const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()); + if (array_ty.zigTypeTag(mod) == .Array) { + const len_val = try mod.intValue(Type.usize, array_ty.arrayLen(mod)); if (!end_is_len) { - const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + const end = if (by_length) end: { + const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); + break :end try sema.coerce(block, Type.usize, uncasted_end, end_src); + } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); if (try sema.resolveMaybeUndefVal(end)) |end_val| { - const len_s_val = try Value.Tag.int_u64.create( - sema.arena, - array_ty.arrayLenIncludingSentinel(), + const len_s_val = try mod.intValue( + Type.usize, + array_ty.arrayLenIncludingSentinel(mod), ); if (!(try sema.compareAll(end_val, .lte, len_s_val, Type.usize))) { - const sentinel_label: []const u8 = if (array_ty.sentinel() != null) + const sentinel_label: []const u8 = if (array_ty.sentinel(mod) != null) " +1 (sentinel)" else ""; @@ -29137,21 +30759,23 @@ fn analyzeSlice( } break :e try sema.addConstant(Type.usize, len_val); - } else if (slice_ty.isSlice()) { + } else if (slice_ty.isSlice(mod)) { if (!end_is_len) { - const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + const end = if (by_length) end: { + const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); + break :end try sema.coerce(block, Type.usize, uncasted_end, end_src); + } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| { - if (slice_val.isUndef()) { + if (slice_val.isUndef(mod)) { return sema.fail(block, src, "slice of undefined", .{}); } - const has_sentinel = slice_ty.sentinel() != null; - var int_payload: Value.Payload.U64 = .{ - .base = .{ .tag = .int_u64 }, - .data = slice_val.sliceLen(mod) + @boolToInt(has_sentinel), - }; - const slice_len_val = Value.initPayload(&int_payload.base); - if (!(try sema.compareAll(end_val, .lte, slice_len_val, Type.usize))) { + const has_sentinel = slice_ty.sentinel(mod) != null; + const slice_len = slice_val.sliceLen(mod); + const len_plus_sent = slice_len + @boolToInt(has_sentinel); + const slice_len_val_with_sentinel = try mod.intValue(Type.usize, len_plus_sent); + if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, Type.usize))) { const sentinel_label: []const u8 = if (has_sentinel) " +1 (sentinel)" else @@ -29169,13 +30793,10 @@ fn analyzeSlice( ); } - // If the slice has a sentinel, we subtract one so that - // end_is_len is only true if it equals the length WITHOUT - // the sentinel, so we don't add a sentinel type. - if (has_sentinel) { - int_payload.data -= 1; - } - + // If the slice has a sentinel, we consider end_is_len + // is only true if it equals the length WITHOUT the + // sentinel, so we don't add a sentinel type. + const slice_len_val = try mod.intValue(Type.usize, slice_len); if (end_val.eql(slice_len_val, Type.usize, mod)) { end_is_len = true; } @@ -29186,7 +30807,11 @@ fn analyzeSlice( break :e try sema.analyzeSliceLen(block, src, ptr_or_slice); } if (!end_is_len) { - break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + if (by_length) { + const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); + break :e try sema.coerce(block, Type.usize, uncasted_end, end_src); + } else break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); } return sema.fail(block, src, "slice of pointer must include end value", .{}); }; @@ -29207,10 +30832,13 @@ fn analyzeSlice( }; const slice_sentinel = if (sentinel_opt != .none) sentinel else null; + var checked_start_lte_end = by_length; + var runtime_src: ?LazySrcLoc = null; + // requirement: start <= end if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| { - if (!(try sema.compareAll(start_val, .lte, end_val, Type.usize))) { + if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) { return sema.fail( block, start_src, @@ -29221,14 +30849,18 @@ fn analyzeSlice( }, ); } + checked_start_lte_end = true; if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: { const expected_sentinel = sentinel orelse break :sentinel_check; - const start_int = start_val.getUnsignedInt(sema.mod.getTarget()).?; - const end_int = end_val.getUnsignedInt(sema.mod.getTarget()).?; + const start_int = start_val.getUnsignedInt(mod).?; + const end_int = end_val.getUnsignedInt(mod).?; const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int); - const elem_ptr = try ptr_val.elemPtr(sema.typeOf(new_ptr), sema.arena, sentinel_index, sema.mod); - const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty, false); + const many_ptr_ty = try mod.manyConstPtrType(elem_ty); + const many_ptr_val = try mod.getCoerced(ptr_val, many_ptr_ty); + const elem_ptr_ty = try mod.singleConstPtrType(elem_ty); + const elem_ptr = try many_ptr_val.elemPtr(elem_ptr_ty, sentinel_index, mod); + const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty); const actual_sentinel = switch (res) { .runtime_load => break :sentinel_check, .val => |v| v, @@ -29236,45 +30868,61 @@ fn analyzeSlice( block, src, "comptime dereference requires '{}' to have a well-defined layout, but it does not.", - .{ty.fmt(sema.mod)}, + .{ty.fmt(mod)}, ), .out_of_bounds => |ty| return sema.fail( block, end_src, "slice end index {d} exceeds bounds of containing decl of type '{}'", - .{ end_int, ty.fmt(sema.mod) }, + .{ end_int, ty.fmt(mod) }, ), }; - if (!actual_sentinel.eql(expected_sentinel, elem_ty, sema.mod)) { + if (!actual_sentinel.eql(expected_sentinel, elem_ty, mod)) { const msg = msg: { const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{}); errdefer msg.destroy(sema.gpa); try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{ - expected_sentinel.fmtValue(elem_ty, sema.mod), - actual_sentinel.fmtValue(elem_ty, sema.mod), + expected_sentinel.fmtValue(elem_ty, mod), + actual_sentinel.fmtValue(elem_ty, mod), }); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } + } else { + runtime_src = ptr_src; } + } else { + runtime_src = start_src; } + } else { + runtime_src = end_src; } - if (block.wantSafety() and !block.is_comptime) { + if (!checked_start_lte_end and block.wantSafety() and !block.is_comptime) { // requirement: start <= end - try sema.panicStartLargerThanEnd(block, start, end); + assert(!block.is_comptime); + try sema.requireRuntimeBlock(block, src, runtime_src.?); + const ok = try block.addBinOp(.cmp_lte, start, end); + if (!sema.mod.comp.formatted_panics) { + try sema.addSafetyCheck(block, ok, .start_index_greater_than_end); + } else { + try sema.safetyCheckFormatted(block, ok, "panicStartGreaterThanEnd", &.{ start, end }); + } } - const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); + const new_len = if (by_length) + try sema.coerce(block, Type.usize, uncasted_end_opt, end_src) + else + try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); - const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data; - const new_allowzero = new_ptr_ty_info.@"allowzero" and sema.typeOf(ptr).ptrSize() != .C; + const new_ptr_ty_info = new_ptr_ty.ptrInfo(mod); + const new_allowzero = new_ptr_ty_info.@"allowzero" and sema.typeOf(ptr).ptrSize(mod) != .C; if (opt_new_len_val) |new_len_val| { - const new_len_int = new_len_val.toUnsignedInt(target); + const new_len_int = new_len_val.toUnsignedInt(mod); const return_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty, mod), @@ -29292,14 +30940,14 @@ fn analyzeSlice( const result = try block.addBitCast(return_ty, new_ptr); if (block.wantSafety()) { // requirement: slicing C ptr is non-null - if (ptr_ptr_child_ty.isCPtr()) { + if (ptr_ptr_child_ty.isCPtr(mod)) { const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true); try sema.addSafetyCheck(block, is_non_null, .unwrap_null); } - if (slice_ty.isSlice()) { + if (slice_ty.isSlice(mod)) { const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); - const actual_len = if (slice_ty.sentinel() == null) + const actual_len = if (slice_ty.sentinel(mod) == null) slice_len_inst else try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); @@ -29318,8 +30966,11 @@ fn analyzeSlice( return result; }; - if (!new_ptr_val.isUndef()) { - return sema.addConstant(return_ty, new_ptr_val); + if (!new_ptr_val.isUndef(mod)) { + return sema.addConstant(return_ty, try mod.getCoerced( + (try new_ptr_val.intern(new_ptr_ty, mod)).toValue(), + return_ty, + )); } // Special case: @as([]i32, undefined)[x..x] @@ -29341,25 +30992,18 @@ fn analyzeSlice( .size = .Slice, }); - const runtime_src = if ((try sema.resolveMaybeUndefVal(ptr_or_slice)) == null) - ptr_src - else if ((try sema.resolveMaybeUndefVal(start)) == null) - start_src - else - end_src; - - try sema.requireRuntimeBlock(block, src, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src.?); if (block.wantSafety()) { // requirement: slicing C ptr is non-null - if (ptr_ptr_child_ty.isCPtr()) { + if (ptr_ptr_child_ty.isCPtr(mod)) { const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true); try sema.addSafetyCheck(block, is_non_null, .unwrap_null); } // requirement: end <= len - const opt_len_inst = if (array_ty.zigTypeTag() == .Array) - try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel()) - else if (slice_ty.isSlice()) blk: { + const opt_len_inst = if (array_ty.zigTypeTag(mod) == .Array) + try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel(mod)) + else if (slice_ty.isSlice(mod)) blk: { 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 @@ -29367,7 +31011,7 @@ fn analyzeSlice( } const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); - if (slice_ty.sentinel() == null) break :blk slice_len_inst; + if (slice_ty.sentinel(mod) == null) break :blk slice_len_inst; // we have to add one because slice lengths don't include the sentinel break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); @@ -29411,15 +31055,16 @@ fn cmpNumeric( lhs_src: LazySrcLoc, rhs_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const lhs_ty = sema.typeOf(uncasted_lhs); const rhs_ty = sema.typeOf(uncasted_rhs); - assert(lhs_ty.isNumeric()); - assert(rhs_ty.isNumeric()); + assert(lhs_ty.isNumeric(mod)); + assert(rhs_ty.isNumeric(mod)); - const lhs_ty_tag = lhs_ty.zigTypeTag(); - const rhs_ty_tag = rhs_ty.zigTypeTag(); - const target = sema.mod.getTarget(); + const lhs_ty_tag = lhs_ty.zigTypeTag(mod); + const rhs_ty_tag = rhs_ty.zigTypeTag(mod); + const target = mod.getTarget(); // One exception to heterogeneous comparison: comptime_float needs to // coerce to fixed-width float. @@ -29438,49 +31083,45 @@ fn cmpNumeric( if (try sema.resolveMaybeUndefVal(lhs)) |lhs_val| { if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| { // Compare ints: const vs. undefined (or vice versa) - if (!lhs_val.isUndef() and (lhs_ty.isInt() or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt() and rhs_val.isUndef()) { - try sema.resolveLazyValue(lhs_val); - if (sema.compareIntsOnlyPossibleResult(target, lhs_val, op, rhs_ty)) |res| { + if (!lhs_val.isUndef(mod) and (lhs_ty.isInt(mod) or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt(mod) and rhs_val.isUndef(mod)) { + if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| { return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; } - } else if (!rhs_val.isUndef() and (rhs_ty.isInt() or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt() and lhs_val.isUndef()) { - try sema.resolveLazyValue(rhs_val); - if (sema.compareIntsOnlyPossibleResult(target, rhs_val, op.reverse(), lhs_ty)) |res| { + } else if (!rhs_val.isUndef(mod) and (rhs_ty.isInt(mod) or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt(mod) and lhs_val.isUndef(mod)) { + if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| { return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; } } - if (lhs_val.isUndef() or rhs_val.isUndef()) { + if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { return sema.addConstUndef(Type.bool); } - if (lhs_val.isNan() or rhs_val.isNan()) { + if (lhs_val.isNan(mod) or rhs_val.isNan(mod)) { if (op == std.math.CompareOperator.neq) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; } } - if (try Value.compareHeteroAdvanced(lhs_val, op, rhs_val, target, sema)) { + if (try Value.compareHeteroAdvanced(lhs_val, op, rhs_val, mod, sema)) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; } } else { - if (!lhs_val.isUndef() and (lhs_ty.isInt() or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt()) { + if (!lhs_val.isUndef(mod) and (lhs_ty.isInt(mod) or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt(mod)) { // Compare ints: const vs. var - try sema.resolveLazyValue(lhs_val); - if (sema.compareIntsOnlyPossibleResult(target, lhs_val, op, rhs_ty)) |res| { + if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| { return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; } } break :src rhs_src; } } else { - if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| { - if (!rhs_val.isUndef() and (rhs_ty.isInt() or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt()) { + if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| { + if (!rhs_val.isUndef(mod) and (rhs_ty.isInt(mod) or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt(mod)) { // Compare ints: var vs. const - try sema.resolveLazyValue(rhs_val); - if (sema.compareIntsOnlyPossibleResult(target, rhs_val, op.reverse(), lhs_ty)) |res| { + if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| { return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; } } @@ -29534,32 +31175,31 @@ fn cmpNumeric( const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema)) else - (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt()); + (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(mod)); const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| !(try rhs_val.compareAllWithZeroAdvanced(.gte, sema)) else - (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt()); + (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(mod)); const dest_int_is_signed = lhs_is_signed or rhs_is_signed; var dest_float_type: ?Type = null; var lhs_bits: usize = undefined; - if (try sema.resolveMaybeUndefVal(lhs)) |lhs_val| { - try sema.resolveLazyValue(lhs_val); - if (lhs_val.isUndef()) + if (try sema.resolveMaybeUndefLazyVal(lhs)) |lhs_val| { + if (lhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool); - if (lhs_val.isNan()) switch (op) { + if (lhs_val.isNan(mod)) switch (op) { .neq => return Air.Inst.Ref.bool_true, else => return Air.Inst.Ref.bool_false, }; - if (lhs_val.isInf()) switch (op) { + if (lhs_val.isInf(mod)) switch (op) { .neq => return Air.Inst.Ref.bool_true, .eq => return Air.Inst.Ref.bool_false, - .gt, .gte => return if (lhs_val.isNegativeInf()) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true, - .lt, .lte => return if (lhs_val.isNegativeInf()) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false, + .gt, .gte => return if (lhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true, + .lt, .lte => return if (lhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false, }; if (!rhs_is_signed) { - switch (lhs_val.orderAgainstZero()) { + switch (lhs_val.orderAgainstZero(mod)) { .gt => {}, .eq => switch (op) { // LHS = 0, RHS is unsigned .lte => return Air.Inst.Ref.bool_true, @@ -29573,7 +31213,7 @@ fn cmpNumeric( } } if (lhs_is_float) { - if (lhs_val.floatHasFraction()) { + if (lhs_val.floatHasFraction(mod)) { switch (op) { .eq => return Air.Inst.Ref.bool_false, .neq => return Air.Inst.Ref.bool_true, @@ -29581,9 +31221,9 @@ fn cmpNumeric( } } - var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128)); + var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128, mod)); defer bigint.deinit(); - if (lhs_val.floatHasFraction()) { + if (lhs_val.floatHasFraction(mod)) { if (lhs_is_signed) { try bigint.addScalar(&bigint, -1); } else { @@ -29592,33 +31232,32 @@ fn cmpNumeric( } lhs_bits = bigint.toConst().bitCountTwosComp(); } else { - lhs_bits = lhs_val.intBitCountTwosComp(target); + lhs_bits = lhs_val.intBitCountTwosComp(mod); } lhs_bits += @boolToInt(!lhs_is_signed and dest_int_is_signed); } else if (lhs_is_float) { dest_float_type = lhs_ty; } else { - const int_info = lhs_ty.intInfo(target); + const int_info = lhs_ty.intInfo(mod); lhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed); } var rhs_bits: usize = undefined; - if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| { - try sema.resolveLazyValue(rhs_val); - if (rhs_val.isUndef()) + if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| { + if (rhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool); - if (rhs_val.isNan()) switch (op) { + if (rhs_val.isNan(mod)) switch (op) { .neq => return Air.Inst.Ref.bool_true, else => return Air.Inst.Ref.bool_false, }; - if (rhs_val.isInf()) switch (op) { + if (rhs_val.isInf(mod)) switch (op) { .neq => return Air.Inst.Ref.bool_true, .eq => return Air.Inst.Ref.bool_false, - .gt, .gte => return if (rhs_val.isNegativeInf()) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false, - .lt, .lte => return if (rhs_val.isNegativeInf()) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true, + .gt, .gte => return if (rhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false, + .lt, .lte => return if (rhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true, }; if (!lhs_is_signed) { - switch (rhs_val.orderAgainstZero()) { + switch (rhs_val.orderAgainstZero(mod)) { .gt => {}, .eq => switch (op) { // RHS = 0, LHS is unsigned .gte => return Air.Inst.Ref.bool_true, @@ -29632,7 +31271,7 @@ fn cmpNumeric( } } if (rhs_is_float) { - if (rhs_val.floatHasFraction()) { + if (rhs_val.floatHasFraction(mod)) { switch (op) { .eq => return Air.Inst.Ref.bool_false, .neq => return Air.Inst.Ref.bool_true, @@ -29640,9 +31279,9 @@ fn cmpNumeric( } } - var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128)); + var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128, mod)); defer bigint.deinit(); - if (rhs_val.floatHasFraction()) { + if (rhs_val.floatHasFraction(mod)) { if (rhs_is_signed) { try bigint.addScalar(&bigint, -1); } else { @@ -29651,21 +31290,21 @@ fn cmpNumeric( } rhs_bits = bigint.toConst().bitCountTwosComp(); } else { - rhs_bits = rhs_val.intBitCountTwosComp(target); + rhs_bits = rhs_val.intBitCountTwosComp(mod); } rhs_bits += @boolToInt(!rhs_is_signed and dest_int_is_signed); } else if (rhs_is_float) { dest_float_type = rhs_ty; } else { - const int_info = rhs_ty.intInfo(target); + const int_info = rhs_ty.intInfo(mod); rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed); } const dest_ty = if (dest_float_type) |ft| ft else blk: { - const max_bits = std.math.max(lhs_bits, rhs_bits); + const max_bits = @max(lhs_bits, rhs_bits); const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}); const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned; - break :blk try Module.makeIntType(sema.arena, signedness, casted_bits); + break :blk try mod.intType(signedness, casted_bits); }; const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); @@ -29673,13 +31312,20 @@ fn cmpNumeric( return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs); } -/// Asserts that LHS value is an int or comptime int and not undefined, and that RHS type is an int. -/// Given a const LHS and an unknown RHS, attempt to determine whether `op` has a guaranteed result. +/// Asserts that LHS value is an int or comptime int and not undefined, and +/// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to +/// determine whether `op` has a guaranteed result. /// If it cannot be determined, returns null. /// Otherwise returns a bool for the guaranteed comparison operation. -fn compareIntsOnlyPossibleResult(sema: *Sema, target: std.Target, lhs_val: Value, op: std.math.CompareOperator, rhs_ty: Type) ?bool { - const rhs_info = rhs_ty.intInfo(target); - const vs_zero = lhs_val.orderAgainstZeroAdvanced(sema) catch unreachable; +fn compareIntsOnlyPossibleResult( + sema: *Sema, + lhs_val: Value, + op: std.math.CompareOperator, + rhs_ty: Type, +) Allocator.Error!?bool { + const mod = sema.mod; + const rhs_info = rhs_ty.intInfo(mod); + const vs_zero = lhs_val.orderAgainstZeroAdvanced(mod, sema) catch unreachable; const is_zero = vs_zero == .eq; const is_negative = vs_zero == .lt; const is_positive = vs_zero == .gt; @@ -29711,7 +31357,7 @@ fn compareIntsOnlyPossibleResult(sema: *Sema, target: std.Target, lhs_val: Value }; const sign_adj = @boolToInt(!is_negative and rhs_info.signedness == .signed); - const req_bits = lhs_val.intBitCountTwosComp(target) + sign_adj; + const req_bits = lhs_val.intBitCountTwosComp(mod) + sign_adj; // No sized type can have more than 65535 bits. // The RHS type operand is either a runtime value or sized (but undefined) constant. @@ -29744,12 +31390,11 @@ fn compareIntsOnlyPossibleResult(sema: *Sema, target: std.Target, lhs_val: Value .max = false, }; - var ty_buffer: Type.Payload.Bits = .{ - .base = .{ .tag = if (is_negative) .int_signed else .int_unsigned }, - .data = @intCast(u16, req_bits), - }; - const ty = Type.initPayload(&ty_buffer.base); - const pop_count = lhs_val.popCount(ty, target); + const ty = try mod.intType( + if (is_negative) .signed else .unsigned, + @intCast(u16, req_bits), + ); + const pop_count = lhs_val.popCount(ty, mod); if (is_negative) { break :edge .{ @@ -29785,21 +31430,29 @@ fn cmpVector( lhs_src: LazySrcLoc, rhs_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); - assert(lhs_ty.zigTypeTag() == .Vector); - assert(rhs_ty.zigTypeTag() == .Vector); + assert(lhs_ty.zigTypeTag(mod) == .Vector); + assert(rhs_ty.zigTypeTag(mod) == .Vector); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); - const result_ty = try Type.vector(sema.arena, lhs_ty.vectorLen(), Type.bool); + const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } }); + const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src); + const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src); + + const result_ty = try mod.vectorType(.{ + .len = lhs_ty.vectorLen(mod), + .child = .bool_type, + }); const runtime_src: LazySrcLoc = src: { - if (try sema.resolveMaybeUndefVal(lhs)) |lhs_val| { - if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| { - if (lhs_val.isUndef() or rhs_val.isUndef()) { + if (try sema.resolveMaybeUndefVal(casted_lhs)) |lhs_val| { + if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| { + if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { return sema.addConstUndef(result_ty); } - const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, lhs_ty); + const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty); return sema.addConstant(result_ty, cmp_val); } else { break :src rhs_src; @@ -29810,7 +31463,7 @@ fn cmpVector( }; try sema.requireRuntimeBlock(block, src, runtime_src); - return block.addCmpVector(lhs, rhs, op); + return block.addCmpVector(casted_lhs, casted_rhs, op); } fn wrapOptional( @@ -29821,7 +31474,10 @@ fn wrapOptional( inst_src: LazySrcLoc, ) !Air.Inst.Ref { if (try sema.resolveMaybeUndefVal(inst)) |val| { - return sema.addConstant(dest_ty, try Value.Tag.opt_payload.create(sema.arena, val)); + return sema.addConstant(dest_ty, (try sema.mod.intern(.{ .opt = .{ + .ty = dest_ty.toIntern(), + .val = val.toIntern(), + } })).toValue()); } try sema.requireRuntimeBlock(block, inst_src, null); @@ -29835,10 +31491,14 @@ fn wrapErrorUnionPayload( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { - const dest_payload_ty = dest_ty.errorUnionPayload(); + const mod = sema.mod; + const dest_payload_ty = dest_ty.errorUnionPayload(mod); const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false }); if (try sema.resolveMaybeUndefVal(coerced)) |val| { - return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val)); + return sema.addConstant(dest_ty, (try mod.intern(.{ .error_union = .{ + .ty = dest_ty.toIntern(), + .val = .{ .payload = try val.intern(dest_payload_ty, mod) }, + } })).toValue()); } try sema.requireRuntimeBlock(block, inst_src, null); try sema.queueFullTypeResolution(dest_payload_ty); @@ -29852,48 +31512,41 @@ fn wrapErrorUnionSet( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { + const mod = sema.mod; + const ip = &mod.intern_pool; const inst_ty = sema.typeOf(inst); - const dest_err_set_ty = dest_ty.errorUnionSet(); + const dest_err_set_ty = dest_ty.errorUnionSet(mod); if (try sema.resolveMaybeUndefVal(inst)) |val| { - switch (dest_err_set_ty.tag()) { - .anyerror => {}, - .error_set_single => ok: { - const expected_name = val.castTag(.@"error").?.data.name; - const n = dest_err_set_ty.castTag(.error_set_single).?.data; - if (mem.eql(u8, expected_name, n)) break :ok; - return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); - }, - .error_set => { - const expected_name = val.castTag(.@"error").?.data.name; - const error_set = dest_err_set_ty.castTag(.error_set).?.data; - if (!error_set.names.contains(expected_name)) { + switch (dest_err_set_ty.toIntern()) { + .anyerror_type => {}, + else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) { + .error_set_type => |error_set_type| ok: { + const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; + if (error_set_type.nameIndex(ip, expected_name) != null) break :ok; return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); - } - }, - .error_set_inferred => ok: { - const expected_name = val.castTag(.@"error").?.data.name; - const ies = dest_err_set_ty.castTag(.error_set_inferred).?.data; + }, + .inferred_error_set_type => |ies_index| ok: { + const ies = mod.inferredErrorSetPtr(ies_index); + const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; - // We carefully do this in an order that avoids unnecessarily - // resolving the destination error set type. - if (ies.is_anyerror) break :ok; - if (ies.errors.contains(expected_name)) break :ok; - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) { - break :ok; - } + // We carefully do this in an order that avoids unnecessarily + // resolving the destination error set type. + if (ies.is_anyerror) break :ok; + + if (ies.errors.contains(expected_name)) break :ok; + if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) break :ok; - return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); - }, - .error_set_merged => { - const expected_name = val.castTag(.@"error").?.data.name; - const error_set = dest_err_set_ty.castTag(.error_set_merged).?.data; - if (!error_set.contains(expected_name)) { return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); - } + }, + else => unreachable, }, - else => unreachable, } - return sema.addConstant(dest_ty, val); + return sema.addConstant(dest_ty, (try mod.intern(.{ .error_union = .{ + .ty = dest_ty.toIntern(), + .val = .{ + .err_name = mod.intern_pool.indexToKey(try val.intern(dest_err_set_ty, mod)).err.name, + }, + } })).toValue()); } try sema.requireRuntimeBlock(block, inst_src, null); @@ -29908,16 +31561,301 @@ fn unionToTag( un: Air.Inst.Ref, un_src: LazySrcLoc, ) !Air.Inst.Ref { + const mod = sema.mod; if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| { return sema.addConstant(enum_ty, opv); } if (try sema.resolveMaybeUndefVal(un)) |un_val| { - return sema.addConstant(enum_ty, un_val.unionTag()); + return sema.addConstant(enum_ty, un_val.unionTag(mod)); } try sema.requireRuntimeBlock(block, un_src, null); return block.addTyOp(.get_union_tag, enum_ty, un); } +const PeerResolveStrategy = enum { + /// The type is not known. + /// If refined no further, this is equivalent to `exact`. + unknown, + /// The type may be an error set or error union. + /// If refined no further, it is an error set. + error_set, + /// The type must be some error union. + error_union, + /// The type may be @TypeOf(null), an optional or a C pointer. + /// If refined no further, it is @TypeOf(null). + nullable, + /// The type must be some optional or a C pointer. + /// If refined no further, it is an optional. + optional, + /// The type must be either an array or a vector. + /// If refined no further, it is an array. + array, + /// The type must be a vector. + vector, + /// The type must be a C pointer. + c_ptr, + /// The type must be a pointer (C or not). + /// If refined no further, it is a non-C pointer. + ptr, + /// The type must be a function or a pointer to a function. + /// If refined no further, it is a function. + func, + /// The type must be an enum literal, or some specific enum or union. Which one is decided + /// afterwards based on the types in question. + enum_or_union, + /// The type must be some integer or float type. + /// If refined no further, it is `comptime_int`. + comptime_int, + /// The type must be some float type. + /// If refined no further, it is `comptime_float`. + comptime_float, + /// The type must be some float or fixed-width integer type. + /// If refined no further, it is some fixed-width integer type. + fixed_int, + /// The type must be some fixed-width float type. + fixed_float, + /// The type must be a struct literal or tuple type. + coercible_struct, + /// The peers must all be of the same type. + exact, + + /// Given two strategies, find a strategy that satisfies both, if one exists. If no such + /// strategy exists, any strategy may be returned; an error will be emitted when the caller + /// attempts to use the strategy to resolve the type. + /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at + /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy. + fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy { + // Our merging should be order-independent. Thus, even though the union order is arbitrary, + // by sorting the tags and switching first on the smaller, we have half as many cases to + // worry about (since we avoid the duplicates). + const s0_is_a = @enumToInt(a) <= @enumToInt(b); + const s0 = if (s0_is_a) a else b; + const s1 = if (s0_is_a) b else a; + + const ReasonMethod = enum { + all_s0, + all_s1, + either, + }; + + const res: struct { ReasonMethod, PeerResolveStrategy } = switch (s0) { + .unknown => .{ .all_s1, s1 }, + .error_set => switch (s1) { + .error_set => .{ .either, .error_set }, + else => .{ .all_s0, .error_union }, + }, + .error_union => switch (s1) { + .error_union => .{ .either, .error_union }, + else => .{ .all_s0, .error_union }, + }, + .nullable => switch (s1) { + .nullable => .{ .either, .nullable }, + .c_ptr => .{ .all_s1, .c_ptr }, + else => .{ .all_s0, .optional }, + }, + .optional => switch (s1) { + .optional => .{ .either, .optional }, + .c_ptr => .{ .all_s1, .c_ptr }, + else => .{ .all_s0, .optional }, + }, + .array => switch (s1) { + .array => .{ .either, .array }, + .vector => .{ .all_s1, .vector }, + else => .{ .all_s0, .array }, + }, + .vector => switch (s1) { + .vector => .{ .either, .vector }, + else => .{ .all_s0, .vector }, + }, + .c_ptr => switch (s1) { + .c_ptr => .{ .either, .c_ptr }, + else => .{ .all_s0, .c_ptr }, + }, + .ptr => switch (s1) { + .ptr => .{ .either, .ptr }, + else => .{ .all_s0, .ptr }, + }, + .func => switch (s1) { + .func => .{ .either, .func }, + else => .{ .all_s1, s1 }, // doesn't override anything later + }, + .enum_or_union => switch (s1) { + .enum_or_union => .{ .either, .enum_or_union }, + else => .{ .all_s0, .enum_or_union }, + }, + .comptime_int => switch (s1) { + .comptime_int => .{ .either, .comptime_int }, + else => .{ .all_s1, s1 }, // doesn't override anything later + }, + .comptime_float => switch (s1) { + .comptime_float => .{ .either, .comptime_float }, + else => .{ .all_s1, s1 }, // doesn't override anything later + }, + .fixed_int => switch (s1) { + .fixed_int => .{ .either, .fixed_int }, + else => .{ .all_s1, s1 }, // doesn't override anything later + }, + .fixed_float => switch (s1) { + .fixed_float => .{ .either, .fixed_float }, + else => .{ .all_s1, s1 }, // doesn't override anything later + }, + .coercible_struct => switch (s1) { + .exact => .{ .all_s1, .exact }, + else => .{ .all_s0, .coercible_struct }, + }, + .exact => .{ .all_s0, .exact }, + }; + + switch (res[0]) { + .all_s0 => { + if (!s0_is_a) { + reason_peer.* = b_peer_idx; + } + }, + .all_s1 => { + if (s0_is_a) { + reason_peer.* = b_peer_idx; + } + }, + .either => { + // Prefer the earliest peer + reason_peer.* = @min(reason_peer.*, b_peer_idx); + }, + } + + return res[1]; + } + + fn select(ty: Type, mod: *Module) PeerResolveStrategy { + return switch (ty.zigTypeTag(mod)) { + .Type, .Void, .Bool, .Opaque, .Frame, .AnyFrame => .exact, + .NoReturn, .Undefined => .unknown, + .Null => .nullable, + .ComptimeInt => .comptime_int, + .Int => .fixed_int, + .ComptimeFloat => .comptime_float, + .Float => .fixed_float, + .Pointer => if (ty.ptrInfo(mod).size == .C) .c_ptr else .ptr, + .Array => .array, + .Vector => .vector, + .Optional => .optional, + .ErrorSet => .error_set, + .ErrorUnion => .error_union, + .EnumLiteral, .Enum, .Union => .enum_or_union, + .Struct => if (ty.isTupleOrAnonStruct(mod)) .coercible_struct else .exact, + .Fn => .func, + }; + } +}; + +const PeerResolveResult = union(enum) { + /// The peer type resolution was successful, and resulted in the given type. + success: Type, + /// There was some generic conflict between two peers. + conflict: struct { + peer_idx_a: usize, + peer_idx_b: usize, + }, + /// There was an error when resolving the type of a struct or tuple field. + field_error: struct { + /// The name of the field which caused the failure. + field_name: []const u8, + /// The type of this field in each peer. + field_types: []Type, + /// The error from resolving the field type. Guaranteed not to be `success`. + sub_result: *PeerResolveResult, + }, + + fn report( + result: PeerResolveResult, + sema: *Sema, + block: *Block, + src: LazySrcLoc, + instructions: []const Air.Inst.Ref, + candidate_srcs: Module.PeerTypeCandidateSrc, + ) !*Module.ErrorMsg { + const mod = sema.mod; + const decl_ptr = mod.declPtr(block.src_decl); + + var opt_msg: ?*Module.ErrorMsg = null; + errdefer if (opt_msg) |msg| msg.destroy(sema.gpa); + + // If we mention fields we'll want to include field types, so put peer types in a buffer + var peer_tys = try sema.arena.alloc(Type, instructions.len); + for (peer_tys, instructions) |*ty, inst| { + ty.* = sema.typeOf(inst); + } + + var cur = result; + while (true) { + var conflict_idx: [2]usize = undefined; + + switch (cur) { + .success => unreachable, + .conflict => |conflict| { + // Fall through to two-peer conflict handling below + conflict_idx = .{ + conflict.peer_idx_a, + conflict.peer_idx_b, + }; + }, + .field_error => |field_error| { + const fmt = "struct field '{s}' has conflicting types"; + const args = .{field_error.field_name}; + if (opt_msg) |msg| { + try sema.errNote(block, src, msg, fmt, args); + } else { + opt_msg = try sema.errMsg(block, src, fmt, args); + } + + // Continue on to child error + cur = field_error.sub_result.*; + peer_tys = field_error.field_types; + continue; + }, + } + + // This is the path for reporting a generic conflict between two peers. + + if (conflict_idx[1] < conflict_idx[0]) { + // b comes first in source, so it's better if it comes first in the error + std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]); + } + + const conflict_tys: [2]Type = .{ + peer_tys[conflict_idx[0]], + peer_tys[conflict_idx[1]], + }; + const conflict_srcs: [2]?LazySrcLoc = .{ + candidate_srcs.resolve(mod, decl_ptr, conflict_idx[0]), + candidate_srcs.resolve(mod, decl_ptr, conflict_idx[1]), + }; + + const fmt = "incompatible types: '{}' and '{}'"; + const args = .{ + conflict_tys[0].fmt(mod), + conflict_tys[1].fmt(mod), + }; + const msg = if (opt_msg) |msg| msg: { + try sema.errNote(block, src, msg, fmt, args); + break :msg msg; + } else msg: { + const msg = try sema.errMsg(block, src, fmt, args); + opt_msg = msg; + break :msg msg; + }; + + if (conflict_srcs[0]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[0].fmt(mod)}); + if (conflict_srcs[1]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[1].fmt(mod)}); + + // No child error + break; + } + + return opt_msg.?; + } +}; + fn resolvePeerTypes( sema: *Sema, block: *Block, @@ -29926,663 +31864,1303 @@ fn resolvePeerTypes( candidate_srcs: Module.PeerTypeCandidateSrc, ) !Type { switch (instructions.len) { - 0 => return Type.initTag(.noreturn), + 0 => return Type.noreturn, 1 => return sema.typeOf(instructions[0]), else => {}, } - const target = sema.mod.getTarget(); + var peer_tys = try sema.arena.alloc(?Type, instructions.len); + var peer_vals = try sema.arena.alloc(?Value, instructions.len); - var chosen = instructions[0]; - // If this is non-null then it does the following thing, depending on the chosen zigTypeTag(). - // * ErrorSet: this is an override - // * ErrorUnion: this is an override of the error set only - // * other: at the end we make an ErrorUnion with the other thing and this - var err_set_ty: ?Type = null; - var any_are_null = false; - var seen_const = false; - var convert_to_slice = false; - var chosen_i: usize = 0; - for (instructions[1..], 0..) |candidate, candidate_i| { - const candidate_ty = sema.typeOf(candidate); - const chosen_ty = sema.typeOf(chosen); - - const candidate_ty_tag = try candidate_ty.zigTypeTagOrPoison(); - const chosen_ty_tag = try chosen_ty.zigTypeTagOrPoison(); - - // If the candidate can coerce into our chosen type, we're done. - // If the chosen type can coerce into the candidate, use that. - if ((try sema.coerceInMemoryAllowed(block, chosen_ty, candidate_ty, false, target, src, src)) == .ok) { - continue; - } - if ((try sema.coerceInMemoryAllowed(block, candidate_ty, chosen_ty, false, target, src, src)) == .ok) { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - } + for (instructions, peer_tys, peer_vals) |inst, *ty, *val| { + ty.* = sema.typeOf(inst); + val.* = try sema.resolveMaybeUndefVal(inst); + } - switch (candidate_ty_tag) { - .NoReturn, .Undefined => continue, + switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) { + .success => |ty| return ty, + else => |result| { + const msg = try result.report(sema, block, src, instructions, candidate_srcs); + return sema.failWithOwnedErrorMsg(msg); + }, + } +} - .Null => { - any_are_null = true; - continue; - }, +fn resolvePeerTypesInner( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + peer_tys: []?Type, + peer_vals: []?Value, +) !PeerResolveResult { + const mod = sema.mod; - .Int => switch (chosen_ty_tag) { - .ComptimeInt => { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - }, - .Int => { - const chosen_info = chosen_ty.intInfo(target); - const candidate_info = candidate_ty.intInfo(target); + var strat_reason: usize = 0; + var s: PeerResolveStrategy = .unknown; + for (peer_tys, 0..) |opt_ty, i| { + const ty = opt_ty orelse continue; + s = s.merge(PeerResolveStrategy.select(ty, mod), &strat_reason, i); + } - if (chosen_info.bits < candidate_info.bits) { - chosen = candidate; - chosen_i = candidate_i + 1; - } - continue; - }, - .Pointer => if (chosen_ty.ptrSize() == .C) continue, - else => {}, - }, - .ComptimeInt => switch (chosen_ty_tag) { - .Int, .Float, .ComptimeFloat => continue, - .Pointer => if (chosen_ty.ptrSize() == .C) continue, - else => {}, - }, - .Float => switch (chosen_ty_tag) { - .Float => { - if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) { - chosen = candidate; - chosen_i = candidate_i + 1; - } - continue; - }, - .ComptimeFloat, .ComptimeInt => { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - }, - else => {}, - }, - .ComptimeFloat => switch (chosen_ty_tag) { - .Float => continue, - .ComptimeInt => { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - }, - else => {}, - }, - .Enum => switch (chosen_ty_tag) { - .EnumLiteral => { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - }, - .Union => continue, - else => {}, - }, - .EnumLiteral => switch (chosen_ty_tag) { - .Enum, .Union => continue, - else => {}, - }, - .Union => switch (chosen_ty_tag) { - .Enum, .EnumLiteral => { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - }, + if (s == .unknown) { + // The whole thing was noreturn or undefined - try to do an exact match + s = .exact; + } else { + // There was something other than noreturn and undefined, so we can ignore those peers + for (peer_tys) |*ty_ptr| { + const ty = ty_ptr.* orelse continue; + switch (ty.zigTypeTag(mod)) { + .NoReturn, .Undefined => ty_ptr.* = null, else => {}, - }, - .ErrorSet => switch (chosen_ty_tag) { - .ErrorSet => { - // If chosen is superset of candidate, keep it. - // If candidate is superset of chosen, switch it. - // If neither is a superset, merge errors. - const chosen_set_ty = err_set_ty orelse chosen_ty; + } + } + } - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) { - continue; - } - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { - err_set_ty = null; - chosen = candidate; - chosen_i = candidate_i + 1; - continue; + const target = mod.getTarget(); + + switch (s) { + .unknown => unreachable, + + .error_set => { + var final_set: ?Type = null; + for (peer_tys, 0..) |opt_ty, i| { + const ty = opt_ty orelse continue; + if (ty.zigTypeTag(mod) != .ErrorSet) return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }; + if (final_set) |cur_set| { + final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty); + } else { + final_set = ty; + } + } + return .{ .success = final_set.? }; + }, + + .error_union => { + var final_set: ?Type = null; + for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { + const ty = ty_ptr.* orelse continue; + const set_ty = switch (ty.zigTypeTag(mod)) { + .ErrorSet => blk: { + ty_ptr.* = null; // no payload to decide on + val_ptr.* = null; + break :blk ty; + }, + .ErrorUnion => blk: { + const set_ty = ty.errorUnionSet(mod); + ty_ptr.* = ty.errorUnionPayload(mod); + if (val_ptr.*) |eu_val| switch (mod.intern_pool.indexToKey(eu_val.toIntern())) { + .error_union => |eu| switch (eu.val) { + .payload => |payload_ip| val_ptr.* = payload_ip.toValue(), + .err_name => val_ptr.* = null, + }, + .undef => val_ptr.* = (try sema.mod.intern(.{ .undef = ty_ptr.*.?.toIntern() })).toValue(), + else => unreachable, + }; + break :blk set_ty; + }, + else => continue, // whole type is the payload + }; + if (final_set) |cur_set| { + final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty); + } else { + final_set = set_ty; + } + } + assert(final_set != null); + const final_payload = switch (try sema.resolvePeerTypesInner( + block, + src, + peer_tys, + peer_vals, + )) { + .success => |ty| ty, + else => |result| return result, + }; + return .{ .success = try mod.errorUnionType(final_set.?, final_payload) }; + }, + + .nullable => { + for (peer_tys, 0..) |opt_ty, i| { + const ty = opt_ty orelse continue; + if (!ty.eql(Type.null, mod)) return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }; + } + return .{ .success = Type.null }; + }, + + .optional => { + for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { + const ty = ty_ptr.* orelse continue; + switch (ty.zigTypeTag(mod)) { + .Null => { + ty_ptr.* = null; + val_ptr.* = null; + }, + .Optional => { + ty_ptr.* = ty.optionalChild(mod); + if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(mod)) opt_val.optionalValue(mod) else null; + }, + else => {}, + } + } + const child_ty = switch (try sema.resolvePeerTypesInner( + block, + src, + peer_tys, + peer_vals, + )) { + .success => |ty| ty, + else => |result| return result, + }; + return .{ .success = try mod.optionalType(child_ty.toIntern()) }; + }, + + .array => { + // Index of the first non-null peer + var opt_first_idx: ?usize = null; + // Index of the first array or vector peer (i.e. not a tuple) + var opt_first_arr_idx: ?usize = null; + // Set to non-null once we see any peer, even a tuple + var len: u64 = undefined; + var sentinel: ?Value = undefined; + // Only set once we see a non-tuple peer + var elem_ty: Type = undefined; + + for (peer_tys, 0..) |*ty_ptr, i| { + const ty = ty_ptr.* orelse continue; + + if (!ty.isArrayOrVector(mod)) { + // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced. + const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }; + + if (opt_first_idx) |first_idx| { + if (arr_like.len != len) return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; + } else { + opt_first_idx = i; + len = arr_like.len; } - err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_ty); + sentinel = null; + continue; - }, - .ErrorUnion => { - const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(); + } - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) { - continue; + const first_arr_idx = opt_first_arr_idx orelse { + if (opt_first_idx == null) { + opt_first_idx = i; + len = ty.arrayLen(mod); + sentinel = ty.sentinel(mod); } - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { - err_set_ty = candidate_ty; - continue; - } - - err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_ty); + opt_first_arr_idx = i; + elem_ty = ty.childType(mod); continue; - }, - else => { - if (err_set_ty) |chosen_set_ty| { - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) { - continue; - } - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { - err_set_ty = candidate_ty; - continue; - } + }; - err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_ty); - continue; + if (ty.arrayLen(mod) != len) return .{ .conflict = .{ + .peer_idx_a = first_arr_idx, + .peer_idx_b = i, + } }; + + if (!ty.childType(mod).eql(elem_ty, mod)) { + return .{ .conflict = .{ + .peer_idx_a = first_arr_idx, + .peer_idx_b = i, + } }; + } + + if (sentinel) |cur_sent| { + if (ty.sentinel(mod)) |peer_sent| { + if (!peer_sent.eql(cur_sent, elem_ty, mod)) sentinel = null; } else { - err_set_ty = candidate_ty; - continue; + sentinel = null; } - }, - }, - .ErrorUnion => switch (chosen_ty_tag) { - .ErrorSet => { - const chosen_set_ty = err_set_ty orelse chosen_ty; - const candidate_set_ty = candidate_ty.errorUnionSet(); + } + } - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) { - err_set_ty = chosen_set_ty; - } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { - err_set_ty = null; + // There should always be at least one array or vector peer + assert(opt_first_arr_idx != null); + + return .{ .success = try mod.arrayType(.{ + .len = len, + .child = elem_ty.toIntern(), + .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none, + }) }; + }, + + .vector => { + var len: ?u64 = null; + var first_idx: usize = undefined; + for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| { + const ty = ty_ptr.* orelse continue; + + if (!ty.isArrayOrVector(mod)) { + // Allow tuples of the correct length + const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }; + + if (len) |expect_len| { + if (arr_like.len != expect_len) return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; } else { - err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); + len = arr_like.len; + first_idx = i; } - chosen = candidate; - chosen_i = candidate_i + 1; + + // Tuples won't participate in the child type resolution. We'll resolve without + // them, and if the tuples have a bad type, we'll get a coercion error later. + ty_ptr.* = null; + val_ptr.* = null; + continue; - }, + } - .ErrorUnion => { - const chosen_payload_ty = chosen_ty.errorUnionPayload(); - const candidate_payload_ty = candidate_ty.errorUnionPayload(); + if (len) |expect_len| { + if (ty.arrayLen(mod) != expect_len) return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; + } else { + len = ty.arrayLen(mod); + first_idx = i; + } - const coerce_chosen = (try sema.coerceInMemoryAllowed(block, chosen_payload_ty, candidate_payload_ty, false, target, src, src)) == .ok; - const coerce_candidate = (try sema.coerceInMemoryAllowed(block, candidate_payload_ty, chosen_payload_ty, false, target, src, src)) == .ok; + ty_ptr.* = ty.childType(mod); + val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR + } - if (coerce_chosen or coerce_candidate) { - // If we can coerce to the candidate, we switch to that - // type. This is the same logic as the bare (non-union) - // coercion check we do at the top of this func. - if (coerce_candidate) { - chosen = candidate; - chosen_i = candidate_i + 1; - } + const child_ty = switch (try sema.resolvePeerTypesInner( + block, + src, + peer_tys, + peer_vals, + )) { + .success => |ty| ty, + else => |result| return result, + }; - const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(); - const candidate_set_ty = candidate_ty.errorUnionSet(); + return .{ .success = try mod.vectorType(.{ + .len = @intCast(u32, len.?), + .child = child_ty.toIntern(), + }) }; + }, - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) { - err_set_ty = chosen_set_ty; - } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { - err_set_ty = candidate_set_ty; + .c_ptr => { + var opt_ptr_info: ?Type.Payload.Pointer.Data = null; + var first_idx: usize = undefined; + for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { + const ty = opt_ty orelse continue; + switch (ty.zigTypeTag(mod)) { + .ComptimeInt => continue, // comptime-known integers can always coerce to C pointers + .Int => { + if (opt_val != null) { + // Always allow the coercion for comptime-known ints + continue; } else { - err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); + // Runtime-known, so check if the type is no bigger than a usize + const ptr_bits = target.ptrBitWidth(); + const bits = ty.intInfo(mod).bits; + if (bits <= ptr_bits) continue; } - continue; - } - }, + }, + .Null => continue, + else => {}, + } - else => { - if (err_set_ty) |chosen_set_ty| { - const candidate_set_ty = candidate_ty.errorUnionSet(); - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) { - err_set_ty = chosen_set_ty; - } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { - err_set_ty = null; - } else { - err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); - } + if (!ty.isPtrAtRuntime(mod)) return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }; + + // Goes through optionals + const peer_info = ty.ptrInfo(mod); + + var ptr_info = opt_ptr_info orelse { + opt_ptr_info = peer_info; + opt_ptr_info.?.size = .C; + first_idx = i; + continue; + }; + + // Try peer -> cur, then cur -> peer + ptr_info.pointee_type = (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) orelse { + return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; + }; + + if (ptr_info.sentinel != null and peer_info.sentinel != null) { + const peer_sent = try mod.getCoerced(ptr_info.sentinel.?, ptr_info.pointee_type); + const ptr_sent = try mod.getCoerced(peer_info.sentinel.?, ptr_info.pointee_type); + if (ptr_sent.eql(peer_sent, ptr_info.pointee_type, mod)) { + ptr_info.sentinel = ptr_sent; + } else { + ptr_info.sentinel = null; } - seen_const = seen_const or chosen_ty.isConstPtr(); - chosen = candidate; - chosen_i = candidate_i + 1; + } else { + ptr_info.sentinel = null; + } + + // Note that the align can be always non-zero; Type.ptr will canonicalize it + ptr_info.@"align" = @min(ptr_info.alignment(mod), peer_info.alignment(mod)); + if (ptr_info.@"addrspace" != peer_info.@"addrspace") { + return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; + } + + if (ptr_info.bit_offset != peer_info.bit_offset or + ptr_info.host_size != peer_info.host_size) + { + return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; + } + + ptr_info.mutable = ptr_info.mutable and peer_info.mutable; + ptr_info.@"volatile" = ptr_info.@"volatile" or peer_info.@"volatile"; + + opt_ptr_info = ptr_info; + } + return .{ .success = try Type.ptr(sema.arena, mod, opt_ptr_info.?) }; + }, + + .ptr => { + // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only + // if there were no actual slices. Else, we want the slice index to report a conflict. + var opt_slice_idx: ?usize = null; + + var opt_ptr_info: ?Type.Payload.Pointer.Data = null; + var first_idx: usize = undefined; + var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error + + for (peer_tys, 0..) |opt_ty, i| { + const ty = opt_ty orelse continue; + const peer_info: Type.Payload.Pointer.Data = switch (ty.zigTypeTag(mod)) { + .Pointer => ty.ptrInfo(mod), + .Fn => .{ + .pointee_type = ty, + .@"addrspace" = target_util.defaultAddressSpace(target, .global_constant), + }, + else => return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }, + }; + + switch (peer_info.size) { + .One, .Many => {}, + .Slice => opt_slice_idx = i, + .C => return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }, + } + + var ptr_info = opt_ptr_info orelse { + opt_ptr_info = peer_info; + first_idx = i; continue; - }, - }, - .Pointer => { - const cand_info = candidate_ty.ptrInfo().data; - switch (chosen_ty_tag) { - .Pointer => { - const chosen_info = chosen_ty.ptrInfo().data; + }; - seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable; + other_idx = i; - // *[N]T to [*]T - // *[N]T to []T - if ((cand_info.size == .Many or cand_info.size == .Slice) and - chosen_info.size == .One and - chosen_info.pointee_type.zigTypeTag() == .Array) - { - // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T` - convert_to_slice = false; - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - } - if (cand_info.size == .One and - cand_info.pointee_type.zigTypeTag() == .Array and - (chosen_info.size == .Many or chosen_info.size == .Slice)) - { - // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T` - convert_to_slice = false; - continue; - } + // We want to return this in a lot of cases, so alias it here for convenience + const generic_err: PeerResolveResult = .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; - // *[N]T and *[M]T - // Verify both are single-pointers to arrays. - // Keep the one whose element type can be coerced into. - if (chosen_info.size == .One and - cand_info.size == .One and - chosen_info.pointee_type.zigTypeTag() == .Array and - cand_info.pointee_type.zigTypeTag() == .Array) - { - const chosen_elem_ty = chosen_info.pointee_type.childType(); - const cand_elem_ty = cand_info.pointee_type.childType(); + // Note that the align can be always non-zero; Type.ptr will canonicalize it + ptr_info.@"align" = @min(ptr_info.alignment(mod), peer_info.alignment(mod)); - const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_elem_ty, cand_elem_ty, chosen_info.mutable, target, src, src); - if (chosen_ok) { - convert_to_slice = true; - continue; - } + if (ptr_info.@"addrspace" != peer_info.@"addrspace") { + return generic_err; + } - const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_elem_ty, chosen_elem_ty, cand_info.mutable, target, src, src); - if (cand_ok) { - convert_to_slice = true; - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - } + if (ptr_info.bit_offset != peer_info.bit_offset or + ptr_info.host_size != peer_info.host_size) + { + return generic_err; + } - // They're both bad. Report error. - // In the future we probably want to use the - // coerceInMemoryAllowed error reporting mechanism, - // however, for now we just fall through for the - // "incompatible types" error below. - } + ptr_info.mutable = ptr_info.mutable and peer_info.mutable; + ptr_info.@"volatile" = ptr_info.@"volatile" or peer_info.@"volatile"; + + const peer_sentinel: ?Value = switch (peer_info.size) { + .One => switch (peer_info.pointee_type.zigTypeTag(mod)) { + .Array => peer_info.pointee_type.sentinel(mod), + else => null, + }, + .Many, .Slice => peer_info.sentinel, + .C => unreachable, + }; + + const cur_sentinel: ?Value = switch (ptr_info.size) { + .One => switch (ptr_info.pointee_type.zigTypeTag(mod)) { + .Array => ptr_info.pointee_type.sentinel(mod), + else => null, + }, + .Many, .Slice => ptr_info.sentinel, + .C => unreachable, + }; - // [*c]T and any other pointer size - // Whichever element type can coerce to the other one, is - // the one we will keep. If they're both OK then we keep the - // C pointer since it matches both single and many pointers. - if (cand_info.size == .C or chosen_info.size == .C) { - const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_info.pointee_type, chosen_info.pointee_type, cand_info.mutable, target, src, src); - const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_info.pointee_type, cand_info.pointee_type, chosen_info.mutable, target, src, src); - - if (cand_ok) { - if (chosen_ok) { - if (chosen_info.size == .C) { - continue; - } else { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; + // We abstract array handling slightly so that tuple pointers can work like array pointers + const peer_pointee_array = sema.typeIsArrayLike(peer_info.pointee_type); + const cur_pointee_array = sema.typeIsArrayLike(ptr_info.pointee_type); + + // This switch is just responsible for deciding the size and pointee (not including + // single-pointer array sentinel). + good: { + switch (peer_info.size) { + .One => switch (ptr_info.size) { + .One => { + if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| { + ptr_info.pointee_type = pointee; + break :good; + } + + const cur_arr = cur_pointee_array orelse return generic_err; + const peer_arr = peer_pointee_array orelse return generic_err; + + if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| { + // *[n:x]T + *[n:y]T = *[n]T + if (cur_arr.len == peer_arr.len) { + ptr_info.pointee_type = try mod.arrayType(.{ + .len = cur_arr.len, + .child = elem_ty.toIntern(), + }); + break :good; } - } else { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; + // *[a]T + *[b]T = []T + ptr_info.size = .Slice; + ptr_info.pointee_type = elem_ty; + break :good; } - } else { - if (chosen_ok) { - continue; - } else { - // They're both bad. Report error. - // In the future we probably want to use the - // coerceInMemoryAllowed error reporting mechanism, - // however, for now we just fall through for the - // "incompatible types" error below. + + if (peer_arr.elem_ty.toIntern() == .noreturn_type) { + // *struct{} + *[a]T = []T + ptr_info.size = .Slice; + ptr_info.pointee_type = cur_arr.elem_ty; + break :good; } - } - } - }, - .Int, .ComptimeInt => { - if (cand_info.size == .C) { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; + + if (cur_arr.elem_ty.toIntern() == .noreturn_type) { + // *[a]T + *struct{} = []T + ptr_info.size = .Slice; + ptr_info.pointee_type = peer_arr.elem_ty; + break :good; + } + + return generic_err; + }, + .Many => { + // Only works for *[n]T + [*]T -> [*]T + const arr = peer_pointee_array orelse return generic_err; + if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, arr.elem_ty)) |pointee| { + ptr_info.pointee_type = pointee; + break :good; + } + if (arr.elem_ty.toIntern() == .noreturn_type) { + // *struct{} + [*]T -> [*]T + break :good; + } + return generic_err; + }, + .Slice => { + // Only works for *[n]T + []T -> []T + const arr = peer_pointee_array orelse return generic_err; + if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, arr.elem_ty)) |pointee| { + ptr_info.pointee_type = pointee; + break :good; + } + if (arr.elem_ty.toIntern() == .noreturn_type) { + // *struct{} + []T -> []T + break :good; + } + return generic_err; + }, + .C => unreachable, + }, + .Many => switch (ptr_info.size) { + .One => { + // Only works for [*]T + *[n]T -> [*]T + const arr = cur_pointee_array orelse return generic_err; + if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.pointee_type)) |pointee| { + ptr_info.size = .Many; + ptr_info.pointee_type = pointee; + break :good; + } + if (arr.elem_ty.toIntern() == .noreturn_type) { + // [*]T + *struct{} -> [*]T + ptr_info.size = .Many; + ptr_info.pointee_type = peer_info.pointee_type; + break :good; + } + return generic_err; + }, + .Many => { + if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| { + ptr_info.pointee_type = pointee; + break :good; + } + return generic_err; + }, + .Slice => { + // Only works if no peers are actually slices + if (opt_slice_idx) |slice_idx| { + return .{ .conflict = .{ + .peer_idx_a = slice_idx, + .peer_idx_b = i, + } }; + } + // Okay, then works for [*]T + "[]T" -> [*]T + if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| { + ptr_info.size = .Many; + ptr_info.pointee_type = pointee; + break :good; + } + return generic_err; + }, + .C => unreachable, + }, + .Slice => switch (ptr_info.size) { + .One => { + // Only works for []T + *[n]T -> []T + const arr = cur_pointee_array orelse return generic_err; + if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.pointee_type)) |pointee| { + ptr_info.size = .Slice; + ptr_info.pointee_type = pointee; + break :good; + } + if (arr.elem_ty.toIntern() == .noreturn_type) { + // []T + *struct{} -> []T + ptr_info.size = .Slice; + ptr_info.pointee_type = peer_info.pointee_type; + break :good; + } + return generic_err; + }, + .Many => { + // Impossible! (current peer is an actual slice) + return generic_err; + }, + .Slice => { + if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| { + ptr_info.pointee_type = pointee; + break :good; + } + return generic_err; + }, + .C => unreachable, + }, + .C => unreachable, + } + } + + const sentinel_ty = if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag(mod) == .Array) blk: { + break :blk ptr_info.pointee_type.childType(mod); + } else ptr_info.pointee_type; + + // TODO: once InternPool is in, we need to cast the sentinels to sentinel_ty + + sentinel: { + no_sentinel: { + if (peer_sentinel == null) break :no_sentinel; + if (cur_sentinel == null) break :no_sentinel; + const peer_sent_coerced = try mod.getCoerced(peer_sentinel.?, sentinel_ty); + const cur_sent_coerced = try mod.getCoerced(cur_sentinel.?, sentinel_ty); + if (!peer_sent_coerced.eql(cur_sent_coerced, sentinel_ty, mod)) break :no_sentinel; + // Sentinels match + if (ptr_info.size == .One) { + assert(ptr_info.pointee_type.zigTypeTag(mod) == .Array); + ptr_info.pointee_type = try mod.arrayType(.{ + .len = ptr_info.pointee_type.arrayLen(mod), + .child = ptr_info.pointee_type.childType(mod).toIntern(), + .sentinel = cur_sent_coerced.toIntern(), + }); + } else { + ptr_info.sentinel = cur_sent_coerced; } + break :sentinel; + } + // Clear existing sentinel + ptr_info.sentinel = null; + if (ptr_info.pointee_type.zigTypeTag(mod) == .Array) { + ptr_info.pointee_type = try mod.arrayType(.{ + .len = ptr_info.pointee_type.arrayLen(mod), + .child = ptr_info.pointee_type.childType(mod).toIntern(), + .sentinel = .none, + }); + } + } + + opt_ptr_info = ptr_info; + } + + // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance) + // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to + // coerce the empty struct to a specific type, but no peer provided one. We need to + // detect this case and emit an error. + const pointee = opt_ptr_info.?.pointee_type; + if (pointee.toIntern() == .noreturn_type or + (pointee.zigTypeTag(mod) == .Array and pointee.childType(mod).toIntern() == .noreturn_type)) + { + return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = other_idx, + } }; + } + + return .{ .success = try Type.ptr(sema.arena, mod, opt_ptr_info.?) }; + }, + + .func => { + var opt_cur_ty: ?Type = null; + var first_idx: usize = undefined; + for (peer_tys, 0..) |opt_ty, i| { + const ty = opt_ty orelse continue; + const cur_ty = opt_cur_ty orelse { + opt_cur_ty = ty; + first_idx = i; + continue; + }; + if (ty.zigTypeTag(mod) != .Fn) return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }; + // ty -> cur_ty + if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, target, src, src)) { + continue; + } + // cur_ty -> ty + if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, target, src, src)) { + opt_cur_ty = ty; + continue; + } + return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; + } + return .{ .success = opt_cur_ty.? }; + }, + + .enum_or_union => { + var opt_cur_ty: ?Type = null; + // The peer index which gave the current type + var cur_ty_idx: usize = undefined; + + for (peer_tys, 0..) |opt_ty, i| { + const ty = opt_ty orelse continue; + switch (ty.zigTypeTag(mod)) { + .EnumLiteral, .Enum, .Union => {}, + else => return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }, + } + const cur_ty = opt_cur_ty orelse { + opt_cur_ty = ty; + cur_ty_idx = i; + continue; + }; + + // We want to return this in a lot of cases, so alias it here for convenience + const generic_err: PeerResolveResult = .{ .conflict = .{ + .peer_idx_a = cur_ty_idx, + .peer_idx_b = i, + } }; + + switch (cur_ty.zigTypeTag(mod)) { + .EnumLiteral => { + opt_cur_ty = ty; + cur_ty_idx = i; }, - .Optional => { - var opt_child_buf: Type.Payload.ElemType = undefined; - const chosen_ptr_ty = chosen_ty.optionalChild(&opt_child_buf); - if (chosen_ptr_ty.zigTypeTag() == .Pointer) { - const chosen_info = chosen_ptr_ty.ptrInfo().data; - - seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable; - - // *[N]T to ?![*]T - // *[N]T to ?![]T - if (cand_info.size == .One and - cand_info.pointee_type.zigTypeTag() == .Array and - (chosen_info.size == .Many or chosen_info.size == .Slice)) - { - continue; - } - } + .Enum => switch (ty.zigTypeTag(mod)) { + .EnumLiteral => {}, + .Enum => { + if (!ty.eql(cur_ty, mod)) return generic_err; + }, + .Union => { + const tag_ty = ty.unionTagTypeHypothetical(mod); + if (!tag_ty.eql(cur_ty, mod)) return generic_err; + opt_cur_ty = ty; + cur_ty_idx = i; + }, + else => unreachable, }, - .ErrorUnion => { - const chosen_ptr_ty = chosen_ty.errorUnionPayload(); - if (chosen_ptr_ty.zigTypeTag() == .Pointer) { - const chosen_info = chosen_ptr_ty.ptrInfo().data; - - seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable; - - // *[N]T to E![*]T - // *[N]T to E![]T - if (cand_info.size == .One and - cand_info.pointee_type.zigTypeTag() == .Array and - (chosen_info.size == .Many or chosen_info.size == .Slice)) - { - continue; - } - } + .Union => switch (ty.zigTypeTag(mod)) { + .EnumLiteral => {}, + .Enum => { + const cur_tag_ty = cur_ty.unionTagTypeHypothetical(mod); + if (!ty.eql(cur_tag_ty, mod)) return generic_err; + }, + .Union => { + if (!ty.eql(cur_ty, mod)) return generic_err; + }, + else => unreachable, }, - .Fn => { - if (!cand_info.mutable and cand_info.pointee_type.zigTypeTag() == .Fn and .ok == try sema.coerceInMemoryAllowedFns(block, chosen_ty, cand_info.pointee_type, target, src, src)) { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - } + else => unreachable, + } + } + return .{ .success = opt_cur_ty.? }; + }, + + .comptime_int => { + for (peer_tys, 0..) |opt_ty, i| { + const ty = opt_ty orelse continue; + switch (ty.zigTypeTag(mod)) { + .ComptimeInt => {}, + else => return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }, + } + } + return .{ .success = Type.comptime_int }; + }, + + .comptime_float => { + for (peer_tys, 0..) |opt_ty, i| { + const ty = opt_ty orelse continue; + switch (ty.zigTypeTag(mod)) { + .ComptimeInt, .ComptimeFloat => {}, + else => return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }, + } + } + return .{ .success = Type.comptime_float }; + }, + + .fixed_int => { + var idx_unsigned: ?usize = null; + var idx_signed: ?usize = null; + + // TODO: this is for compatibility with legacy behavior. See beneath the loop. + var any_comptime_known = false; + + for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| { + const ty = opt_ty orelse continue; + const opt_val = ptr_opt_val.*; + + const peer_tag = ty.zigTypeTag(mod); + switch (peer_tag) { + .ComptimeInt => { + // If the value is undefined, we can't refine to a fixed-width int + if (opt_val == null or opt_val.?.isUndef(mod)) return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }; + any_comptime_known = true; + ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?); + continue; }, - else => {}, + .Int => {}, + else => return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }, } - }, - .Optional => { - var opt_child_buf: Type.Payload.ElemType = undefined; - const opt_child_ty = candidate_ty.optionalChild(&opt_child_buf); - if ((try sema.coerceInMemoryAllowed(block, chosen_ty, opt_child_ty, false, target, src, src)) == .ok) { - seen_const = seen_const or opt_child_ty.isConstPtr(); - any_are_null = true; + + if (opt_val != null) any_comptime_known = true; + + const info = ty.intInfo(mod); + + const idx_ptr = switch (info.signedness) { + .unsigned => &idx_unsigned, + .signed => &idx_signed, + }; + + const largest_idx = idx_ptr.* orelse { + idx_ptr.* = i; continue; + }; + + const cur_info = peer_tys[largest_idx].?.intInfo(mod); + if (info.bits > cur_info.bits) { + idx_ptr.* = i; } + } - seen_const = seen_const or chosen_ty.isConstPtr(); - any_are_null = false; - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - }, - .Vector => switch (chosen_ty_tag) { - .Vector => { - const chosen_len = chosen_ty.vectorLen(); - const candidate_len = candidate_ty.vectorLen(); - if (chosen_len != candidate_len) - continue; + if (idx_signed == null) { + return .{ .success = peer_tys[idx_unsigned.?].? }; + } - const chosen_child_ty = chosen_ty.childType(); - const candidate_child_ty = candidate_ty.childType(); - if (chosen_child_ty.zigTypeTag() == .Int and candidate_child_ty.zigTypeTag() == .Int) { - const chosen_info = chosen_child_ty.intInfo(target); - const candidate_info = candidate_child_ty.intInfo(target); - if (chosen_info.bits < candidate_info.bits) { - chosen = candidate; - chosen_i = candidate_i + 1; - } - continue; - } - if (chosen_child_ty.zigTypeTag() == .Float and candidate_child_ty.zigTypeTag() == .Float) { - if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) { - chosen = candidate; - chosen_i = candidate_i + 1; + if (idx_unsigned == null) { + return .{ .success = peer_tys[idx_signed.?].? }; + } + + const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(mod); + const signed_info = peer_tys[idx_signed.?].?.intInfo(mod); + if (signed_info.bits > unsigned_info.bits) { + return .{ .success = peer_tys[idx_signed.?].? }; + } + + // TODO: this is for compatibility with legacy behavior. Before this version of PTR was + // implemented, the algorithm very often returned false positives, with the expectation + // that you'd just hit a coercion error later. One of these was that for integers, the + // largest type would always be returned, even if it couldn't fit everything. This had + // an unintentional consequence to semantics, which is that if values were known at + // comptime, they would be coerced down to the smallest type where possible. This + // behavior is unintuitive and order-dependent, so in my opinion should be eliminated, + // but for now we'll retain compatibility. + if (any_comptime_known) { + if (unsigned_info.bits > signed_info.bits) { + return .{ .success = peer_tys[idx_unsigned.?].? }; + } + const idx = @min(idx_unsigned.?, idx_signed.?); + return .{ .success = peer_tys[idx].? }; + } + + return .{ .conflict = .{ + .peer_idx_a = idx_unsigned.?, + .peer_idx_b = idx_signed.?, + } }; + }, + + .fixed_float => { + var opt_cur_ty: ?Type = null; + + for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { + const ty = opt_ty orelse continue; + switch (ty.zigTypeTag(mod)) { + .ComptimeFloat, .ComptimeInt => {}, + .Int => { + if (opt_val == null) return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }; + }, + .Float => { + if (opt_cur_ty) |cur_ty| { + if (cur_ty.eql(ty, mod)) continue; + // Recreate the type so we eliminate any c_longdouble + const bits = @max(cur_ty.floatBits(target), ty.floatBits(target)); + opt_cur_ty = switch (bits) { + 16 => Type.f16, + 32 => Type.f32, + 64 => Type.f64, + 80 => Type.f80, + 128 => Type.f128, + else => unreachable, + }; + } else { + opt_cur_ty = ty; } - continue; - } - }, - .Array => { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - }, - else => {}, - }, - .Array => switch (chosen_ty_tag) { - .Vector => continue, - else => {}, - }, - .Fn => if (chosen_ty.isSinglePointer() and chosen_ty.isConstPtr() and chosen_ty.childType().zigTypeTag() == .Fn) { - if (.ok == try sema.coerceInMemoryAllowedFns(block, chosen_ty.childType(), candidate_ty, target, src, src)) { - continue; + }, + else => return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }, } - }, - else => {}, - } + } - switch (chosen_ty_tag) { - .NoReturn, .Undefined => { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - }, - .Null => { - any_are_null = true; - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - }, - .Optional => { - var opt_child_buf: Type.Payload.ElemType = undefined; - const opt_child_ty = chosen_ty.optionalChild(&opt_child_buf); - if ((try sema.coerceInMemoryAllowed(block, opt_child_ty, candidate_ty, false, target, src, src)) == .ok) { - continue; + // Note that fixed_float is only chosen if there is at least one fixed-width float peer, + // so opt_cur_ty must be non-null. + return .{ .success = opt_cur_ty.? }; + }, + + .coercible_struct => { + // First, check that every peer has the same approximate structure (field count and names) + + var opt_first_idx: ?usize = null; + var is_tuple: bool = undefined; + var field_count: usize = undefined; + // Only defined for non-tuples. + var field_names: []InternPool.NullTerminatedString = undefined; + + for (peer_tys, 0..) |opt_ty, i| { + const ty = opt_ty orelse continue; + + if (!ty.isTupleOrAnonStruct(mod)) { + return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }; } - if ((try sema.coerceInMemoryAllowed(block, candidate_ty, opt_child_ty, false, target, src, src)) == .ok) { - any_are_null = true; - chosen = candidate; - chosen_i = candidate_i + 1; + + const first_idx = opt_first_idx orelse { + opt_first_idx = i; + is_tuple = ty.isTuple(mod); + field_count = ty.structFieldCount(mod); + if (!is_tuple) { + const names = mod.intern_pool.indexToKey(ty.toIntern()).anon_struct_type.names; + field_names = try sema.arena.dupe(InternPool.NullTerminatedString, names); + } continue; + }; + + if (ty.isTuple(mod) != is_tuple or ty.structFieldCount(mod) != field_count) { + return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; } - }, - .ErrorUnion => { - const payload_ty = chosen_ty.errorUnionPayload(); - if ((try sema.coerceInMemoryAllowed(block, payload_ty, candidate_ty, false, target, src, src)) == .ok) { - continue; + + if (!is_tuple) { + for (field_names, 0..) |expected, field_idx| { + const actual = ty.structFieldName(field_idx, mod); + if (actual == expected) continue; + return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; + } } - }, - .ErrorSet => { - chosen = candidate; - chosen_i = candidate_i + 1; - if (err_set_ty) |chosen_set_ty| { - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, chosen_ty, src, src)) { + } + + assert(opt_first_idx != null); + + // Now, we'll recursively resolve the field types + const field_types = try sema.arena.alloc(InternPool.Index, field_count); + // Values for `comptime` fields - `.none` used for non-comptime fields + const field_vals = try sema.arena.alloc(InternPool.Index, field_count); + const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len); + const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len); + + for (field_types, field_vals, 0..) |*field_ty, *field_val, field_idx| { + // Fill buffers with types and values of the field + for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| { + const ty = opt_ty orelse { + peer_field_ty.* = null; + peer_field_val.* = null; continue; - } - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_ty, chosen_set_ty, src, src)) { - err_set_ty = chosen_ty; + }; + peer_field_ty.* = ty.structFieldType(field_idx, mod); + peer_field_val.* = if (opt_val) |val| try val.fieldValue(mod, field_idx) else null; + } + + // Resolve field type recursively + field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) { + .success => |ty| ty.toIntern(), + else => |result| { + const result_buf = try sema.arena.create(PeerResolveResult); + result_buf.* = result; + const field_name = if (is_tuple) name: { + break :name try std.fmt.allocPrint(sema.arena, "{d}", .{field_idx}); + } else try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(field_names[field_idx])); + + // The error info needs the field types, but we can't reuse sub_peer_tys + // since the recursive call may have clobbered it. + const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len); + for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| { + // Already-resolved types won't be referenced by the error so it's fine + // to leave them undefined. + const ty = opt_ty orelse continue; + peer_field_ty.* = ty.structFieldType(field_idx, mod); + } + + return .{ .field_error = .{ + .field_name = field_name, + .field_types = peer_field_tys, + .sub_result = result_buf, + } }; + }, + }; + + // Decide if this is a comptime field. If it is comptime in all peers, and the + // coerced comptime values are all the same, we say it is comptime, else not. + + var comptime_val: ?Value = null; + for (peer_tys) |opt_ty| { + const struct_ty = opt_ty orelse continue; + const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse { + comptime_val = null; + break; + }; + const uncoerced_field_ty = struct_ty.structFieldType(field_idx, mod); + const uncoerced_field = try sema.addConstant(uncoerced_field_ty, uncoerced_field_val); + const coerced_inst = sema.coerceExtra(block, field_ty.toType(), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) { + // It's possible for PTR to give false positives. Just give up on making this a comptime field, we'll get an error later anyway + error.NotCoercible => { + comptime_val = null; + break; + }, + else => |e| return e, + }; + const coerced_val = (try sema.resolveMaybeUndefVal(coerced_inst)) orelse continue; + const existing = comptime_val orelse { + comptime_val = coerced_val; continue; + }; + if (!coerced_val.eql(existing, field_ty.toType(), mod)) { + comptime_val = null; + break; } - - err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, chosen_ty); - continue; - } else { - err_set_ty = chosen_ty; - continue; } - }, - else => {}, - } - // At this point, we hit a compile error. We need to recover - // the source locations. - const chosen_src = candidate_srcs.resolve( - sema.gpa, - sema.mod.declPtr(block.src_decl), - chosen_i, - ); - const candidate_src = candidate_srcs.resolve( - sema.gpa, - sema.mod.declPtr(block.src_decl), - candidate_i + 1, - ); + field_val.* = if (comptime_val) |v| v.toIntern() else .none; + } - const msg = msg: { - const msg = try sema.errMsg(block, src, "incompatible types: '{}' and '{}'", .{ - chosen_ty.fmt(sema.mod), - candidate_ty.fmt(sema.mod), - }); - errdefer msg.destroy(sema.gpa); + const final_ty = try mod.intern(.{ .anon_struct_type = .{ + .types = field_types, + .names = if (is_tuple) &.{} else field_names, + .values = field_vals, + } }); - if (chosen_src) |src_loc| - try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty.fmt(sema.mod)}); + return .{ .success = final_ty.toType() }; + }, - if (candidate_src) |src_loc| - try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty.fmt(sema.mod)}); + .exact => { + var expect_ty: ?Type = null; + var first_idx: usize = undefined; + for (peer_tys, 0..) |opt_ty, i| { + const ty = opt_ty orelse continue; + if (expect_ty) |expect| { + if (!ty.eql(expect, mod)) return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; + } else { + expect_ty = ty; + first_idx = i; + } + } + return .{ .success = expect_ty.? }; + }, + } +} - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(msg); +fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type { + // e0 -> e1 + if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) { + return e1; } - const chosen_ty = sema.typeOf(chosen); + // e1 -> e0 + if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) { + return e0; + } - if (convert_to_slice) { - // turn *[N]T => []T - const chosen_child_ty = chosen_ty.childType(); - var info = chosen_ty.ptrInfo(); - info.data.sentinel = chosen_child_ty.sentinel(); - info.data.size = .Slice; - info.data.mutable = !(seen_const or chosen_child_ty.isConstPtr()); - info.data.pointee_type = chosen_child_ty.elemType2(); + return sema.errorSetMerge(e0, e1); +} - const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data); - const opt_ptr_ty = if (any_are_null) - try Type.optional(sema.arena, new_ptr_ty) - else - new_ptr_ty; - const set_ty = err_set_ty orelse return opt_ptr_ty; - return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod); +fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type { + // ty_b -> ty_a + if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, sema.mod.getTarget(), src, src)) { + return ty_a; } - if (seen_const) { - // turn []T => []const T - switch (chosen_ty.zigTypeTag()) { - .ErrorUnion => { - const ptr_ty = chosen_ty.errorUnionPayload(); - var info = ptr_ty.ptrInfo(); - info.data.mutable = false; - const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data); - const opt_ptr_ty = if (any_are_null) - try Type.optional(sema.arena, new_ptr_ty) - else - new_ptr_ty; - const set_ty = err_set_ty orelse chosen_ty.errorUnionSet(); - return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod); - }, - .Pointer => { - var info = chosen_ty.ptrInfo(); - info.data.mutable = false; - const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data); - const opt_ptr_ty = if (any_are_null) - try Type.optional(sema.arena, new_ptr_ty) - else - new_ptr_ty; - const set_ty = err_set_ty orelse return opt_ptr_ty; - return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod); - }, - else => return chosen_ty, - } + // ty_a -> ty_b + if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, sema.mod.getTarget(), src, src)) { + return ty_b; } - if (any_are_null) { - const opt_ty = switch (chosen_ty.zigTypeTag()) { - .Null, .Optional => chosen_ty, - else => try Type.optional(sema.arena, chosen_ty), - }; - const set_ty = err_set_ty orelse return opt_ty; - return try Type.errorUnion(sema.arena, set_ty, opt_ty, sema.mod); - } + return null; +} - if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag()) { - .ErrorSet => return ty, - .ErrorUnion => { - const payload_ty = chosen_ty.errorUnionPayload(); - return try Type.errorUnion(sema.arena, ty, payload_ty, sema.mod); +const ArrayLike = struct { + len: u64, + /// `noreturn` indicates that this type is `struct{}` so can coerce to anything + elem_ty: Type, +}; +fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike { + const mod = sema.mod; + return switch (ty.zigTypeTag(mod)) { + .Array => .{ + .len = ty.arrayLen(mod), + .elem_ty = ty.childType(mod), }, - else => return try Type.errorUnion(sema.arena, ty, chosen_ty, sema.mod), + .Struct => { + const field_count = ty.structFieldCount(mod); + if (field_count == 0) return .{ + .len = 0, + .elem_ty = Type.noreturn, + }; + if (!ty.isTuple(mod)) return null; + const elem_ty = ty.structFieldType(0, mod); + for (1..field_count) |i| { + if (!ty.structFieldType(i, mod).eql(elem_ty, mod)) { + return null; + } + } + return .{ + .len = field_count, + .elem_ty = elem_ty, + }; + }, + else => null, }; - - return chosen_ty; } -pub fn resolveFnTypes(sema: *Sema, fn_info: Type.Payload.Function.Data) CompileError!void { - try sema.resolveTypeFully(fn_info.return_type); +pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { + const mod = sema.mod; + try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.return_type.toType()); - if (sema.mod.comp.bin_file.options.error_return_tracing and fn_info.return_type.isError()) { + if (mod.comp.bin_file.options.error_return_tracing and mod.typeToFunc(fn_ty).?.return_type.toType().isError(mod)) { // Ensure the type exists so that backends can assume that. _ = try sema.getBuiltinType("StackTrace"); } - for (fn_info.param_types) |param_ty| { - try sema.resolveTypeFully(param_ty); + for (0..mod.typeToFunc(fn_ty).?.param_types.len) |i| { + try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.param_types[i].toType()); } } /// Make it so that calling hash() and eql() on `val` will not assert due /// to a type not having its layout resolved. -fn resolveLazyValue(sema: *Sema, val: Value) CompileError!void { - switch (val.tag()) { - .lazy_align => { - const ty = val.castTag(.lazy_align).?.data; - return sema.resolveTypeLayout(ty); - }, - .lazy_size => { - const ty = val.castTag(.lazy_size).?.data; - return sema.resolveTypeLayout(ty); - }, - .comptime_field_ptr => { - const field_ptr = val.castTag(.comptime_field_ptr).?.data; - return sema.resolveLazyValue(field_ptr.field_val); - }, - .eu_payload, - .opt_payload, - => { - const sub_val = val.cast(Value.Payload.SubValue).?.data; - return sema.resolveLazyValue(sub_val); - }, - .@"union" => { - const union_val = val.castTag(.@"union").?.data; - return sema.resolveLazyValue(union_val.val); - }, - .aggregate => { - const aggregate = val.castTag(.aggregate).?.data; - for (aggregate) |elem_val| { - try sema.resolveLazyValue(elem_val); +fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value { + const mod = sema.mod; + switch (mod.intern_pool.indexToKey(val.toIntern())) { + .int => |int| switch (int.storage) { + .u64, .i64, .big_int => return val, + .lazy_align, .lazy_size => return (try mod.intern(.{ .int = .{ + .ty = int.ty, + .storage = .{ .u64 = (try val.getUnsignedIntAdvanced(mod, sema)).? }, + } })).toValue(), + }, + .ptr => |ptr| { + const resolved_len = switch (ptr.len) { + .none => .none, + else => (try sema.resolveLazyValue(ptr.len.toValue())).toIntern(), + }; + switch (ptr.addr) { + .decl, .mut_decl => return if (resolved_len == ptr.len) + val + else + (try mod.intern(.{ .ptr = .{ + .ty = ptr.ty, + .addr = switch (ptr.addr) { + .decl => |decl| .{ .decl = decl }, + .mut_decl => |mut_decl| .{ .mut_decl = mut_decl }, + else => unreachable, + }, + .len = resolved_len, + } })).toValue(), + .comptime_field => |field_val| { + const resolved_field_val = + (try sema.resolveLazyValue(field_val.toValue())).toIntern(); + return if (resolved_field_val == field_val and resolved_len == ptr.len) + val + else + (try mod.intern(.{ .ptr = .{ + .ty = ptr.ty, + .addr = .{ .comptime_field = resolved_field_val }, + .len = resolved_len, + } })).toValue(); + }, + .int => |int| { + const resolved_int = (try sema.resolveLazyValue(int.toValue())).toIntern(); + return if (resolved_int == int and resolved_len == ptr.len) + val + else + (try mod.intern(.{ .ptr = .{ + .ty = ptr.ty, + .addr = .{ .int = resolved_int }, + .len = resolved_len, + } })).toValue(); + }, + .eu_payload, .opt_payload => |base| { + const resolved_base = (try sema.resolveLazyValue(base.toValue())).toIntern(); + return if (resolved_base == base and resolved_len == ptr.len) + val + else + (try mod.intern(.{ .ptr = .{ + .ty = ptr.ty, + .addr = switch (ptr.addr) { + .eu_payload => .{ .eu_payload = resolved_base }, + .opt_payload => .{ .opt_payload = resolved_base }, + else => unreachable, + }, + .len = ptr.len, + } })).toValue(); + }, + .elem, .field => |base_index| { + const resolved_base = (try sema.resolveLazyValue(base_index.base.toValue())).toIntern(); + return if (resolved_base == base_index.base and resolved_len == ptr.len) + val + else + (try mod.intern(.{ .ptr = .{ + .ty = ptr.ty, + .addr = switch (ptr.addr) { + .elem => .{ .elem = .{ + .base = resolved_base, + .index = base_index.index, + } }, + .field => .{ .field = .{ + .base = resolved_base, + .index = base_index.index, + } }, + else => unreachable, + }, + .len = ptr.len, + } })).toValue(); + }, } }, - .slice => { - const slice = val.castTag(.slice).?.data; - try sema.resolveLazyValue(slice.ptr); - return sema.resolveLazyValue(slice.len); + .aggregate => |aggregate| switch (aggregate.storage) { + .bytes => return val, + .elems => |elems| { + var resolved_elems: []InternPool.Index = &.{}; + for (elems, 0..) |elem, i| { + const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern(); + if (resolved_elems.len == 0 and resolved_elem != elem) { + resolved_elems = try sema.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 (try mod.intern(.{ .aggregate = .{ + .ty = aggregate.ty, + .storage = .{ .elems = resolved_elems }, + } })).toValue(); + }, + .repeated_elem => |elem| { + const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern(); + return if (resolved_elem == elem) val else (try mod.intern(.{ .aggregate = .{ + .ty = aggregate.ty, + .storage = .{ .repeated_elem = resolved_elem }, + } })).toValue(); + }, + }, + .un => |un| { + const resolved_tag = (try sema.resolveLazyValue(un.tag.toValue())).toIntern(); + const resolved_val = (try sema.resolveLazyValue(un.val.toValue())).toIntern(); + return if (resolved_tag == un.tag and resolved_val == un.val) + val + else + (try mod.intern(.{ .un = .{ + .ty = un.ty, + .tag = resolved_tag, + .val = resolved_val, + } })).toValue(); }, - else => return, + else => return val, } } pub fn resolveTypeLayout(sema: *Sema, ty: Type) CompileError!void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .Struct => return sema.resolveStructLayout(ty), .Union => return sema.resolveUnionLayout(ty), .Array => { - if (ty.arrayLenIncludingSentinel() == 0) return; - const elem_ty = ty.childType(); + if (ty.arrayLenIncludingSentinel(mod) == 0) return; + const elem_ty = ty.childType(mod); return sema.resolveTypeLayout(elem_ty); }, .Optional => { - var buf: Type.Payload.ElemType = undefined; - const payload_ty = ty.optionalChild(&buf); + const payload_ty = ty.optionalChild(mod); // In case of querying the ABI alignment of this optional, we will ask // for hasRuntimeBits() of the payload type, so we need "requires comptime" // to be known already before this function returns. @@ -30590,37 +33168,37 @@ pub fn resolveTypeLayout(sema: *Sema, ty: Type) CompileError!void { return sema.resolveTypeLayout(payload_ty); }, .ErrorUnion => { - const payload_ty = ty.errorUnionPayload(); + const payload_ty = ty.errorUnionPayload(mod); return sema.resolveTypeLayout(payload_ty); }, .Fn => { - const info = ty.fnInfo(); + const info = mod.typeToFunc(ty).?; if (info.is_generic) { // Resolving of generic function types is deferred to when // the function is instantiated. return; } for (info.param_types) |param_ty| { - try sema.resolveTypeLayout(param_ty); + try sema.resolveTypeLayout(param_ty.toType()); } - try sema.resolveTypeLayout(info.return_type); + try sema.resolveTypeLayout(info.return_type.toType()); }, else => {}, } } fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { + const mod = sema.mod; const resolved_ty = try sema.resolveTypeFields(ty); - if (resolved_ty.castTag(.@"struct")) |payload| { - const struct_obj = payload.data; + if (mod.typeToStruct(resolved_ty)) |struct_obj| { switch (struct_obj.status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { const msg = try Module.ErrorMsg.create( sema.gpa, - struct_obj.srcLoc(sema.mod), + struct_obj.srcLoc(mod), "struct '{}' depends on itself", - .{ty.fmt(sema.mod)}, + .{ty.fmt(mod)}, ); return sema.failWithOwnedErrorMsg(msg); }, @@ -30644,35 +33222,27 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { } if (struct_obj.layout == .Packed) { - try semaBackingIntType(sema.mod, struct_obj); + try semaBackingIntType(mod, struct_obj); } struct_obj.status = .have_layout; _ = try sema.resolveTypeRequiresComptime(resolved_ty); - if (struct_obj.assumed_runtime_bits and !resolved_ty.hasRuntimeBits()) { + if (struct_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) { const msg = try Module.ErrorMsg.create( sema.gpa, - struct_obj.srcLoc(sema.mod), + struct_obj.srcLoc(mod), "struct layout depends on it having runtime bits", .{}, ); return sema.failWithOwnedErrorMsg(msg); } - if (struct_obj.layout == .Auto and sema.mod.backendSupportsFeature(.field_reordering)) { - const optimized_order = if (struct_obj.owner_decl == sema.owner_decl_index) - try sema.perm_arena.alloc(u32, struct_obj.fields.count()) - else blk: { - const decl = sema.mod.declPtr(struct_obj.owner_decl); - var decl_arena: std.heap.ArenaAllocator = undefined; - const decl_arena_allocator = decl.value_arena.?.acquire(sema.mod.gpa, &decl_arena); - defer decl.value_arena.?.release(&decl_arena); - break :blk try decl_arena_allocator.alloc(u32, struct_obj.fields.count()); - }; + if (struct_obj.layout == .Auto and mod.backendSupportsFeature(.field_reordering)) { + const optimized_order = try mod.tmp_hack_arena.allocator().alloc(u32, struct_obj.fields.count()); for (struct_obj.fields.values(), 0..) |field, i| { - optimized_order[i] = if (field.ty.hasRuntimeBits()) + optimized_order[i] = if (try sema.typeHasRuntimeBits(field.ty)) @intCast(u32, i) else Module.Struct.omitted_field; @@ -30683,14 +33253,14 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { sema: *Sema, fn lessThan(ctx: @This(), a: u32, b: u32) bool { + const m = ctx.sema.mod; if (a == Module.Struct.omitted_field) return false; if (b == Module.Struct.omitted_field) return true; - const target = ctx.sema.mod.getTarget(); - return ctx.struct_obj.fields.values()[a].ty.abiAlignment(target) > - ctx.struct_obj.fields.values()[b].ty.abiAlignment(target); + return ctx.struct_obj.fields.values()[a].ty.abiAlignment(m) > + ctx.struct_obj.fields.values()[b].ty.abiAlignment(m); } }; - std.sort.sort(u32, optimized_order, AlignSortContext{ + mem.sort(u32, optimized_order, AlignSortContext{ .struct_obj = struct_obj, .sema = sema, }, AlignSortContext.lessThan); @@ -30702,20 +33272,16 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!void { const gpa = mod.gpa; - const target = mod.getTarget(); var fields_bit_sum: u64 = 0; for (struct_obj.fields.values()) |field| { - fields_bit_sum += field.ty.bitSize(target); + fields_bit_sum += field.ty.bitSize(mod); } const decl_index = struct_obj.owner_decl; const decl = mod.declPtr(decl_index); - var decl_arena: std.heap.ArenaAllocator = undefined; - const decl_arena_allocator = decl.value_arena.?.acquire(gpa, &decl_arena); - defer decl.value_arena.?.release(&decl_arena); - const zir = struct_obj.namespace.file_scope.zir; + const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir; const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; assert(extended.opcode == .struct_decl); const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); @@ -30732,28 +33298,33 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi var analysis_arena = std.heap.ArenaAllocator.init(gpa); defer analysis_arena.deinit(); + var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa); + defer comptime_mutable_decls.deinit(); + var sema: Sema = .{ .mod = mod, .gpa = gpa, .arena = analysis_arena.allocator(), - .perm_arena = decl_arena_allocator, .code = zir, .owner_decl = decl, .owner_decl_index = decl_index, .func = null, + .func_index = .none, .fn_ret_ty = Type.void, .owner_func = null, + .owner_func_index = .none, + .comptime_mutable_decls = &comptime_mutable_decls, }; defer sema.deinit(); - var wip_captures = try WipCaptureScope.init(gpa, decl_arena_allocator, decl.src_scope); + var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope); defer wip_captures.deinit(); var block: Block = .{ .parent = null, .sema = &sema, .src_decl = decl_index, - .namespace = &struct_obj.namespace, + .namespace = struct_obj.namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, .inlining = null, @@ -30777,21 +33348,27 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi }; try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum); - struct_obj.backing_int_ty = try backing_int_ty.copy(decl_arena_allocator); + struct_obj.backing_int_ty = backing_int_ty; try wip_captures.finalize(); + for (comptime_mutable_decls.items) |ct_decl_index| { + const ct_decl = mod.declPtr(ct_decl_index); + try ct_decl.intern(mod); + } } else { if (fields_bit_sum > std.math.maxInt(u16)) { var sema: Sema = .{ .mod = mod, .gpa = gpa, .arena = undefined, - .perm_arena = decl_arena_allocator, .code = zir, .owner_decl = decl, .owner_decl_index = decl_index, .func = null, + .func_index = .none, .fn_ret_ty = Type.void, .owner_func = null, + .owner_func_index = .none, + .comptime_mutable_decls = undefined, }; defer sema.deinit(); @@ -30799,7 +33376,7 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi .parent = null, .sema = &sema, .src_decl = decl_index, - .namespace = &struct_obj.namespace, + .namespace = struct_obj.namespace, .wip_capture_scope = undefined, .instructions = .{}, .inlining = null, @@ -30807,56 +33384,65 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi }; return sema.fail(&block, LazySrcLoc.nodeOffset(0), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum}); } - var buf: Type.Payload.Bits = .{ - .base = .{ .tag = .int_unsigned }, - .data = @intCast(u16, fields_bit_sum), - }; - struct_obj.backing_int_ty = try Type.initPayload(&buf.base).copy(decl_arena_allocator); + struct_obj.backing_int_ty = try mod.intType(.unsigned, @intCast(u16, fields_bit_sum)); } } fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void { - const target = sema.mod.getTarget(); + const mod = sema.mod; - if (!backing_int_ty.isInt()) { + if (!backing_int_ty.isInt(mod)) { return sema.fail(block, src, "expected backing integer type, found '{}'", .{backing_int_ty.fmt(sema.mod)}); } - if (backing_int_ty.bitSize(target) != fields_bit_sum) { + if (backing_int_ty.bitSize(mod) != fields_bit_sum) { return sema.fail( block, src, "backing integer type '{}' has bit size {} but the struct fields have a total bit size of {}", - .{ backing_int_ty.fmt(sema.mod), backing_int_ty.bitSize(target), fields_bit_sum }, + .{ backing_int_ty.fmt(sema.mod), backing_int_ty.bitSize(mod), fields_bit_sum }, ); } } -fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, array_ty: Type) !void { - if (!array_ty.isIndexable()) { +fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { + const mod = sema.mod; + if (!ty.isIndexable(mod)) { const msg = msg: { - const msg = try sema.errMsg( - block, - src, - "type '{}' does not support indexing", - .{array_ty.fmt(sema.mod)}, - ); + const msg = try sema.errMsg(block, src, "type '{}' does not support indexing", .{ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); - try sema.errNote( - block, - src, - msg, - "for loop operand must be an array, slice, tuple, or vector", - .{}, - ); + try sema.errNote(block, src, msg, "operand must be an array, slice, tuple, or vector", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } } +fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { + const mod = sema.mod; + if (ty.zigTypeTag(mod) == .Pointer) { + switch (ty.ptrSize(mod)) { + .Slice, .Many, .C => return, + .One => { + const elem_ty = ty.childType(mod); + if (elem_ty.zigTypeTag(mod) == .Array) return; + // TODO https://github.com/ziglang/zig/issues/15479 + // if (elem_ty.isTuple()) return; + }, + } + } + const msg = msg: { + const msg = try sema.errMsg(block, src, "type '{}' is not an indexable pointer", .{ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); +} + fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { + const mod = sema.mod; const resolved_ty = try sema.resolveTypeFields(ty); - const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(resolved_ty).?; switch (union_obj.status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { @@ -30889,7 +33475,7 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { union_obj.status = .have_layout; _ = try sema.resolveTypeRequiresComptime(resolved_ty); - if (union_obj.assumed_runtime_bits and !resolved_ty.hasRuntimeBits()) { + if (union_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) { const msg = try Module.ErrorMsg.create( sema.gpa, union_obj.srcLoc(sema.mod), @@ -30904,190 +33490,154 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { // for hasRuntimeBits() of each field, so we need "requires comptime" // to be known already before this function returns. pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { - return switch (ty.tag()) { - .u1, - .u8, - .i8, - .u16, - .i16, - .u29, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_char, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .f16, - .f32, - .f64, - .f80, - .f128, - .anyopaque, - .bool, - .void, - .anyerror, - .noreturn, - .@"anyframe", - .null, - .undefined, - .atomic_order, - .atomic_rmw_op, - .calling_convention, - .address_space, - .float_mode, - .reduce_op, - .modifier, - .prefetch_options, - .export_options, - .extern_options, - .manyptr_u8, - .manyptr_const_u8, - .manyptr_const_u8_sentinel_0, - .const_slice_u8, - .const_slice_u8_sentinel_0, - .anyerror_void_error_union, - .empty_struct_literal, - .empty_struct, - .error_set, - .error_set_single, - .error_set_inferred, - .error_set_merged, - .@"opaque", - .generic_poison, - .array_u8, - .array_u8_sentinel_0, - .int_signed, - .int_unsigned, - .enum_simple, - => false, - - .single_const_pointer_to_comptime_int, - .type, - .comptime_int, - .comptime_float, - .enum_literal, - .type_info, - // These are function bodies, not function pointers. - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - => true, - - .var_args_param => unreachable, - .inferred_alloc_mut => unreachable, - .inferred_alloc_const => unreachable, - .bound_fn => unreachable, - - .array, - .array_sentinel, - .vector, - => return sema.resolveTypeRequiresComptime(ty.childType()), - - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - => { - const child_ty = ty.childType(); - if (child_ty.zigTypeTag() == .Fn) { - return child_ty.fnInfo().is_generic; - } else { - return sema.resolveTypeRequiresComptime(child_ty); - } - }, - - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - => { - var buf: Type.Payload.ElemType = undefined; - return sema.resolveTypeRequiresComptime(ty.optionalChild(&buf)); - }, + const mod = sema.mod; - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - for (tuple.types, 0..) |field_ty, i| { - const have_comptime_val = tuple.values[i].tag() != .unreachable_value; - if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty)) { - return true; + return switch (ty.toIntern()) { + .empty_struct_type => false, + else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { + .int_type => false, + .ptr_type => |ptr_type| { + const child_ty = ptr_type.child.toType(); + if (child_ty.zigTypeTag(mod) == .Fn) { + return mod.typeToFunc(child_ty).?.is_generic; + } else { + return sema.resolveTypeRequiresComptime(child_ty); } - } - return false; - }, + }, + .anyframe_type => |child| { + if (child == .none) return false; + return sema.resolveTypeRequiresComptime(child.toType()); + }, + .array_type => |array_type| return sema.resolveTypeRequiresComptime(array_type.child.toType()), + .vector_type => |vector_type| return sema.resolveTypeRequiresComptime(vector_type.child.toType()), + .opt_type => |child| return sema.resolveTypeRequiresComptime(child.toType()), + .error_union_type => |error_union_type| return sema.resolveTypeRequiresComptime(error_union_type.payload_type.toType()), + .error_set_type, .inferred_error_set_type => false, + + .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, + .noreturn, + .generic_poison, + .atomic_order, + .atomic_rmw_op, + .calling_convention, + .address_space, + .float_mode, + .reduce_op, + .call_modifier, + .prefetch_options, + .export_options, + .extern_options, + => false, + + .type, + .comptime_int, + .comptime_float, + .null, + .undefined, + .enum_literal, + .type_info, + => true, + }, + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false; + switch (struct_obj.requires_comptime) { + .no, .wip => return false, + .yes => return true, + .unknown => { + var requires_comptime = false; + struct_obj.requires_comptime = .wip; + for (struct_obj.fields.values()) |field| { + if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; + } + if (requires_comptime) { + struct_obj.requires_comptime = .yes; + } else { + struct_obj.requires_comptime = .no; + } + return requires_comptime; + }, + } + }, - .@"struct" => { - const struct_obj = ty.castTag(.@"struct").?.data; - switch (struct_obj.requires_comptime) { - .no, .wip => return false, - .yes => return true, - .unknown => { - var requires_comptime = false; - struct_obj.requires_comptime = .wip; - for (struct_obj.fields.values()) |field| { - if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; - } - if (requires_comptime) { - struct_obj.requires_comptime = .yes; - } else { - struct_obj.requires_comptime = .no; + .anon_struct_type => |tuple| { + for (tuple.types, tuple.values) |field_ty, field_val| { + const have_comptime_val = field_val != .none; + if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty.toType())) { + return true; } - return requires_comptime; - }, - } - }, + } + return false; + }, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Type.Payload.Union).?.data; - switch (union_obj.requires_comptime) { - .no, .wip => return false, - .yes => return true, - .unknown => { - var requires_comptime = false; - union_obj.requires_comptime = .wip; - for (union_obj.fields.values()) |field| { - if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; - } - if (requires_comptime) { - union_obj.requires_comptime = .yes; - } else { - union_obj.requires_comptime = .no; - } - return requires_comptime; - }, - } - }, + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + switch (union_obj.requires_comptime) { + .no, .wip => return false, + .yes => return true, + .unknown => { + var requires_comptime = false; + union_obj.requires_comptime = .wip; + for (union_obj.fields.values()) |field| { + if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; + } + if (requires_comptime) { + union_obj.requires_comptime = .yes; + } else { + union_obj.requires_comptime = .no; + } + return requires_comptime; + }, + } + }, - .error_union => return sema.resolveTypeRequiresComptime(ty.errorUnionPayload()), - .anyframe_T => { - const child_ty = ty.castTag(.anyframe_T).?.data; - return sema.resolveTypeRequiresComptime(child_ty); - }, - .enum_numbered => { - const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty; - return sema.resolveTypeRequiresComptime(tag_ty); - }, - .enum_full, .enum_nonexhaustive => { - const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty; - return sema.resolveTypeRequiresComptime(tag_ty); + .opaque_type => false, + + .enum_type => |enum_type| try sema.resolveTypeRequiresComptime(enum_type.tag_ty.toType()), + + // values, not types + .undef, + .runtime_value, + .simple_value, + .variable, + .extern_func, + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .empty_enum_value, + .float, + .ptr, + .opt, + .aggregate, + .un, + // memoization, not types + .memoized_call, + => unreachable, }, }; } @@ -31095,40 +33645,38 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { /// Returns `error.AnalysisFail` if any of the types (recursively) failed to /// be resolved. pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void { - switch (ty.zigTypeTag()) { + const mod = sema.mod; + switch (ty.zigTypeTag(mod)) { .Pointer => { - const child_ty = try sema.resolveTypeFields(ty.childType()); + const child_ty = try sema.resolveTypeFields(ty.childType(mod)); return sema.resolveTypeFully(child_ty); }, - .Struct => switch (ty.tag()) { - .@"struct" => return sema.resolveStructFully(ty), - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - + .Struct => switch (mod.intern_pool.indexToKey(ty.toIntern())) { + .struct_type => return sema.resolveStructFully(ty), + .anon_struct_type => |tuple| { for (tuple.types) |field_ty| { - try sema.resolveTypeFully(field_ty); + try sema.resolveTypeFully(field_ty.toType()); } }, else => {}, }, .Union => return sema.resolveUnionFully(ty), - .Array => return sema.resolveTypeFully(ty.childType()), + .Array => return sema.resolveTypeFully(ty.childType(mod)), .Optional => { - var buf: Type.Payload.ElemType = undefined; - return sema.resolveTypeFully(ty.optionalChild(&buf)); + return sema.resolveTypeFully(ty.optionalChild(mod)); }, - .ErrorUnion => return sema.resolveTypeFully(ty.errorUnionPayload()), + .ErrorUnion => return sema.resolveTypeFully(ty.errorUnionPayload(mod)), .Fn => { - const info = ty.fnInfo(); + const info = mod.typeToFunc(ty).?; if (info.is_generic) { // Resolving of generic function types is deferred to when // the function is instantiated. return; } for (info.param_types) |param_ty| { - try sema.resolveTypeFully(param_ty); + try sema.resolveTypeFully(param_ty.toType()); } - try sema.resolveTypeFully(info.return_type); + try sema.resolveTypeFully(info.return_type.toType()); }, else => {}, } @@ -31137,9 +33685,9 @@ pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void { fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void { try sema.resolveStructLayout(ty); + const mod = sema.mod; const resolved_ty = try sema.resolveTypeFields(ty); - const payload = resolved_ty.castTag(.@"struct").?; - const struct_obj = payload.data; + const struct_obj = mod.typeToStruct(resolved_ty).?; switch (struct_obj.status) { .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, @@ -31167,8 +33715,9 @@ fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void { fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void { try sema.resolveUnionLayout(ty); + const mod = sema.mod; const resolved_ty = try sema.resolveTypeFields(ty); - const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(resolved_ty).?; switch (union_obj.status) { .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, .fully_resolved_wip, .fully_resolved => return, @@ -31193,30 +33742,111 @@ fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void { } pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type { - switch (ty.tag()) { - .@"struct" => { - const struct_obj = ty.castTag(.@"struct").?.data; - try sema.resolveTypeFieldsStruct(ty, struct_obj); - return ty; - }, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Type.Payload.Union).?.data; - try sema.resolveTypeFieldsUnion(ty, union_obj); - return ty; - }, - .type_info => return sema.getBuiltinType("Type"), - .extern_options => return sema.getBuiltinType("ExternOptions"), - .export_options => return sema.getBuiltinType("ExportOptions"), - .atomic_order => return sema.getBuiltinType("AtomicOrder"), - .atomic_rmw_op => return sema.getBuiltinType("AtomicRmwOp"), - .calling_convention => return sema.getBuiltinType("CallingConvention"), - .address_space => return sema.getBuiltinType("AddressSpace"), - .float_mode => return sema.getBuiltinType("FloatMode"), - .reduce_op => return sema.getBuiltinType("ReduceOp"), - .modifier => return sema.getBuiltinType("CallModifier"), - .prefetch_options => return sema.getBuiltinType("PrefetchOptions"), + const mod = sema.mod; + + switch (ty.toIntern()) { + .var_args_param_type => unreachable, + + .none => unreachable, + + .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, + .comptime_int_type, + .comptime_float_type, + .noreturn_type, + .anyframe_type, + .null_type, + .undefined_type, + .enum_literal_type, + .manyptr_u8_type, + .manyptr_const_u8_type, + .manyptr_const_u8_sentinel_0_type, + .single_const_pointer_to_comptime_int_type, + .slice_const_u8_type, + .slice_const_u8_sentinel_0_type, + .anyerror_void_error_union_type, + .generic_poison_type, + .empty_struct_type, + => return ty, + + .undef => unreachable, + .zero => unreachable, + .zero_usize => unreachable, + .zero_u8 => unreachable, + .one => unreachable, + .one_usize => unreachable, + .one_u8 => unreachable, + .four_u8 => unreachable, + .negative_one => unreachable, + .calling_convention_c => unreachable, + .calling_convention_inline => unreachable, + .void_value => unreachable, + .unreachable_value => unreachable, + .null_value => unreachable, + .bool_true => unreachable, + .bool_false => unreachable, + .empty_struct => unreachable, + .generic_poison => unreachable, + + .type_info_type => return sema.getBuiltinType("Type"), + .extern_options_type => return sema.getBuiltinType("ExternOptions"), + .export_options_type => return sema.getBuiltinType("ExportOptions"), + .atomic_order_type => return sema.getBuiltinType("AtomicOrder"), + .atomic_rmw_op_type => return sema.getBuiltinType("AtomicRmwOp"), + .calling_convention_type => return sema.getBuiltinType("CallingConvention"), + .address_space_type => return sema.getBuiltinType("AddressSpace"), + .float_mode_type => return sema.getBuiltinType("FloatMode"), + .reduce_op_type => return sema.getBuiltinType("ReduceOp"), + .call_modifier_type => return sema.getBuiltinType("CallModifier"), + .prefetch_options_type => return sema.getBuiltinType("PrefetchOptions"), + + _ => switch (mod.intern_pool.indexToKey(ty.toIntern())) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return ty; + try sema.resolveTypeFieldsStruct(ty, struct_obj); + return ty; + }, + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + try sema.resolveTypeFieldsUnion(ty, union_obj); + return ty; + }, - else => return ty, + else => return ty, + }, } } @@ -31303,29 +33933,43 @@ fn resolveInferredErrorSet( sema: *Sema, block: *Block, src: LazySrcLoc, - ies: *Module.Fn.InferredErrorSet, + ies_index: Module.Fn.InferredErrorSet.Index, ) CompileError!void { + const mod = sema.mod; + const ies = mod.inferredErrorSetPtr(ies_index); + if (ies.is_resolved) return; - if (ies.func.state == .in_progress) { + const func = mod.funcPtr(ies.func); + if (func.state == .in_progress) { return sema.fail(block, src, "unable to resolve inferred error set", .{}); } // In order to ensure that all dependencies are properly added to the set, we // need to ensure the function body is analyzed of the inferred error set. // However, in the case of comptime/inline function calls with inferred error sets, - // each call gets a new InferredErrorSet object, which points to the same - // `*Module.Fn`. Not only is the function not relevant to the inferred error set + // each call gets a new InferredErrorSet object, which contains the same + // `Module.Fn.Index`. Not only is the function not relevant to the inferred error set // in this case, it may be a generic function which would cause an assertion failure // if we called `ensureFuncBodyAnalyzed` on it here. - const ies_func_owner_decl = sema.mod.declPtr(ies.func.owner_decl); - const ies_func_info = ies_func_owner_decl.ty.fnInfo(); + const ies_func_owner_decl = mod.declPtr(func.owner_decl); + const ies_func_info = mod.typeToFunc(ies_func_owner_decl.ty).?; // if ies declared by a inline function with generic return type, the return_type should be generic_poison, // because inline function does not create a new declaration, and the ies has been filled with analyzeCall, // so here we can simply skip this case. - if (ies_func_info.return_type.tag() == .generic_poison) { + if (ies_func_info.return_type == .generic_poison_type) { assert(ies_func_info.cc == .Inline); - } else if (ies_func_info.return_type.errorUnionSet().castTag(.error_set_inferred).?.data == ies) { + } else if (mod.typeToInferredErrorSet(ies_func_info.return_type.toType().errorUnionSet(mod)).? == ies) { + if (ies_func_info.is_generic) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "unable to resolve inferred error set of generic function", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.mod.errNoteNonLazy(ies_func_owner_decl.srcLoc(mod), msg, "generic function declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } // In this case we are dealing with the actual InferredErrorSet object that // corresponds to the function, not one created to track an inline/comptime call. try sema.ensureFuncBodyAnalyzed(ies.func); @@ -31333,10 +33977,11 @@ fn resolveInferredErrorSet( ies.is_resolved = true; - for (ies.inferred_error_sets.keys()) |other_ies| { - if (ies == other_ies) continue; - try sema.resolveInferredErrorSet(block, src, other_ies); + for (ies.inferred_error_sets.keys()) |other_ies_index| { + if (ies_index == other_ies_index) continue; + try sema.resolveInferredErrorSet(block, src, other_ies_index); + const other_ies = mod.inferredErrorSetPtr(other_ies_index); for (other_ies.errors.keys()) |key| { try ies.errors.put(sema.gpa, key, {}); } @@ -31351,15 +33996,17 @@ fn resolveInferredErrorSetTy( src: LazySrcLoc, ty: Type, ) CompileError!void { - if (ty.castTag(.error_set_inferred)) |inferred| { - try sema.resolveInferredErrorSet(block, src, inferred.data); + const mod = sema.mod; + if (mod.typeToInferredErrorSetIndex(ty).unwrap()) |ies_index| { + try sema.resolveInferredErrorSet(block, src, ies_index); } } fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void { const gpa = mod.gpa; + const ip = &mod.intern_pool; const decl_index = struct_obj.owner_decl; - const zir = struct_obj.namespace.file_scope.zir; + const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir; const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; assert(extended.opcode == .struct_decl); const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); @@ -31405,35 +34052,37 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void } const decl = mod.declPtr(decl_index); - var decl_arena: std.heap.ArenaAllocator = undefined; - const decl_arena_allocator = decl.value_arena.?.acquire(gpa, &decl_arena); - defer decl.value_arena.?.release(&decl_arena); var analysis_arena = std.heap.ArenaAllocator.init(gpa); defer analysis_arena.deinit(); + var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa); + defer comptime_mutable_decls.deinit(); + var sema: Sema = .{ .mod = mod, .gpa = gpa, .arena = analysis_arena.allocator(), - .perm_arena = decl_arena_allocator, .code = zir, .owner_decl = decl, .owner_decl_index = decl_index, .func = null, + .func_index = .none, .fn_ret_ty = Type.void, .owner_func = null, + .owner_func_index = .none, + .comptime_mutable_decls = &comptime_mutable_decls, }; defer sema.deinit(); - var wip_captures = try WipCaptureScope.init(gpa, decl_arena_allocator, decl.src_scope); + var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope); defer wip_captures.deinit(); var block_scope: Block = .{ .parent = null, .sema = &sema, .src_decl = decl_index, - .namespace = &struct_obj.namespace, + .namespace = struct_obj.namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, .inlining = null, @@ -31445,13 +34094,13 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void } struct_obj.fields = .{}; - try struct_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len); + try struct_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); const Field = struct { type_body_len: u32 = 0, align_body_len: u32 = 0, init_body_len: u32 = 0, - type_ref: Air.Inst.Ref = .none, + type_ref: Zir.Inst.Ref = .none, }; const fields = try sema.arena.alloc(Field, fields_len); var any_inits = false; @@ -31496,30 +34145,30 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void extra_index += 1; // This string needs to outlive the ZIR code. - const field_name = if (field_name_zir) |some| - try decl_arena_allocator.dupe(u8, some) + const field_name = try ip.getOrPutString(gpa, if (field_name_zir) |s| + s else - try std.fmt.allocPrint(decl_arena_allocator, "{d}", .{field_i}); + try std.fmt.allocPrint(sema.arena, "{d}", .{field_i})); const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { const msg = msg: { - const field_src = struct_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy; - const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{s}'", .{field_name}); + const field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i }).lazy; + const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{}'", .{field_name.fmt(ip)}); errdefer msg.destroy(gpa); const prev_field_index = struct_obj.fields.getIndex(field_name).?; - const prev_field_src = struct_obj.fieldSrcLoc(sema.mod, .{ .index = prev_field_index }); - try sema.mod.errNoteNonLazy(prev_field_src, msg, "other field here", .{}); + const prev_field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = prev_field_index }); + try mod.errNoteNonLazy(prev_field_src, msg, "other field here", .{}); try sema.errNote(&block_scope, src, msg, "struct declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } gop.value_ptr.* = .{ - .ty = Type.initTag(.noreturn), + .ty = Type.noreturn, .abi_align = 0, - .default_val = Value.initTag(.unreachable_value), + .default_val = .none, .is_comptime = is_comptime, .offset = undefined, }; @@ -31545,7 +34194,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void if (zir_field.type_ref != .none) { break :ty sema.resolveType(&block_scope, .unneeded, zir_field.type_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -31561,7 +34210,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const ty_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -31571,16 +34220,16 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void else => |e| return e, }; }; - if (field_ty.tag() == .generic_poison) { + if (field_ty.isGenericPoison()) { return error.GenericPoison; } const field = &struct_obj.fields.values()[field_i]; - field.ty = try field_ty.copy(decl_arena_allocator); + field.ty = field_ty; - if (field_ty.zigTypeTag() == .Opaque) { + if (field_ty.zigTypeTag(mod) == .Opaque) { const msg = msg: { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -31592,9 +34241,9 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void }; return sema.failWithOwnedErrorMsg(msg); } - if (field_ty.zigTypeTag() == .NoReturn) { + if (field_ty.zigTypeTag(mod) == .NoReturn) { const msg = msg: { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -31608,11 +34257,11 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void } if (struct_obj.layout == .Extern and !try sema.validateExternType(field.ty, .struct_field)) { const msg = msg: { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }); - const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); + const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); try sema.explainWhyTypeIsNotExtern(msg, ty_src, field.ty, .struct_field); @@ -31621,13 +34270,13 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty))) { + } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty, mod))) { const msg = msg: { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }); - const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); + const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); try sema.explainWhyTypeIsNotPacked(msg, ty_src, field.ty); @@ -31644,7 +34293,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const align_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); field.abi_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const align_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const align_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .alignment, }).lazy; @@ -31672,7 +34321,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const field = &struct_obj.fields.values()[field_i]; const coerced = sema.coerce(&block_scope, field.ty, init, .unneeded) catch |err| switch (err) { error.NeededSourceLocation => { - const init_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .value, }).lazy; @@ -31682,17 +34331,21 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void else => |e| return e, }; const default_val = (try sema.resolveMaybeUndefVal(coerced)) orelse { - const init_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .value, }).lazy; return sema.failWithNeededComptime(&block_scope, init_src, "struct field default value must be comptime-known"); }; - field.default_val = try default_val.copy(decl_arena_allocator); + field.default_val = try default_val.intern(field.ty, mod); } } } try wip_captures.finalize(); + for (comptime_mutable_decls.items) |ct_decl_index| { + const ct_decl = mod.declPtr(ct_decl_index); + try ct_decl.intern(mod); + } struct_obj.have_field_inits = true; } @@ -31702,8 +34355,9 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { defer tracy.end(); const gpa = mod.gpa; + const ip = &mod.intern_pool; const decl_index = union_obj.owner_decl; - const zir = union_obj.namespace.file_scope.zir; + const zir = mod.namespacePtr(union_obj.namespace).file_scope.zir; const extended = zir.instructions.items(.data)[union_obj.zir_index].extended; assert(extended.opcode == .union_decl); const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); @@ -31745,35 +34399,37 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { extra_index += body.len; const decl = mod.declPtr(decl_index); - var decl_arena: std.heap.ArenaAllocator = undefined; - const decl_arena_allocator = decl.value_arena.?.acquire(gpa, &decl_arena); - defer decl.value_arena.?.release(&decl_arena); var analysis_arena = std.heap.ArenaAllocator.init(gpa); defer analysis_arena.deinit(); + var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa); + defer comptime_mutable_decls.deinit(); + var sema: Sema = .{ .mod = mod, .gpa = gpa, .arena = analysis_arena.allocator(), - .perm_arena = decl_arena_allocator, .code = zir, .owner_decl = decl, .owner_decl_index = decl_index, .func = null, + .func_index = .none, .fn_ret_ty = Type.void, .owner_func = null, + .owner_func_index = .none, + .comptime_mutable_decls = &comptime_mutable_decls, }; defer sema.deinit(); - var wip_captures = try WipCaptureScope.init(gpa, decl_arena_allocator, decl.src_scope); + var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope); defer wip_captures.deinit(); var block_scope: Block = .{ .parent = null, .sema = &sema, .src_decl = decl_index, - .namespace = &union_obj.namespace, + .namespace = union_obj.namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, .inlining = null, @@ -31789,66 +34445,61 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } try wip_captures.finalize(); + for (comptime_mutable_decls.items) |ct_decl_index| { + const ct_decl = mod.declPtr(ct_decl_index); + try ct_decl.intern(mod); + } - try union_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len); + try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); var int_tag_ty: Type = undefined; - var enum_field_names: ?*Module.EnumNumbered.NameMap = null; - var enum_value_map: ?*Module.EnumNumbered.ValueMap = null; - var tag_ty_field_names: ?Module.EnumFull.NameMap = null; + var enum_field_names: []InternPool.NullTerminatedString = &.{}; + var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}; + var explicit_tags_seen: []bool = &.{}; if (tag_type_ref != .none) { const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; 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() != .Int and int_tag_ty.zigTypeTag() != .ComptimeInt) { - return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{}'", .{int_tag_ty.fmt(sema.mod)}); + if (int_tag_ty.zigTypeTag(mod) != .Int and int_tag_ty.zigTypeTag(mod) != .ComptimeInt) { + return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{}'", .{int_tag_ty.fmt(mod)}); } if (fields_len > 0) { - var field_count_val: Value.Payload.U64 = .{ - .base = .{ .tag = .int_u64 }, - .data = fields_len - 1, - }; - if (!(try sema.intFitsInType(Value.initPayload(&field_count_val.base), int_tag_ty, null))) { + const field_count_val = try mod.intValue(Type.comptime_int, fields_len - 1); + if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) { const msg = msg: { const msg = try sema.errMsg(&block_scope, tag_ty_src, "specified integer tag type cannot represent every field", .{}); errdefer msg.destroy(sema.gpa); try sema.errNote(&block_scope, tag_ty_src, msg, "type '{}' cannot fit values in range 0...{d}", .{ - int_tag_ty.fmt(sema.mod), + int_tag_ty.fmt(mod), fields_len - 1, }); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } + enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); + try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len); } - union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, fields_len, provided_ty, union_obj); - const enum_obj = union_obj.tag_ty.castTag(.enum_numbered).?.data; - enum_field_names = &enum_obj.fields; - enum_value_map = &enum_obj.values; } else { // The provided type is the enum tag type. - union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator); - if (union_obj.tag_ty.zigTypeTag() != .Enum) { - return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(sema.mod)}); - } + union_obj.tag_ty = provided_ty; + const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) { + .enum_type => |x| x, + else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(mod)}), + }; // The fields of the union must match the enum exactly. - // Store a copy of the enum field names so we can check for - // missing or extraneous fields later. - tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(sema.arena); + // 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). - union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, fields_len, union_obj); - enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields; - } - - if (fields_len == 0) { - return; + enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); } const bits_per_field = 4; @@ -31892,17 +34543,17 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { break :blk align_ref; } else .none; - const tag_ref: Zir.Inst.Ref = if (has_tag) blk: { + const tag_ref: Air.Inst.Ref = if (has_tag) blk: { const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); extra_index += 1; break :blk try sema.resolveInst(tag_ref); } else .none; - if (enum_value_map) |map| { - const copied_val = if (tag_ref != .none) blk: { + if (enum_field_vals.capacity() > 0) { + const enum_tag_val = if (tag_ref != .none) blk: { const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const val_src = union_obj.fieldSrcLoc(sema.mod, .{ + const val_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .value, }).lazy; @@ -31913,27 +34564,22 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { }; last_tag_val = val; - // This puts the memory into the union arena, not the enum arena, but - // it is OK since they share the same lifetime. - break :blk try val.copy(decl_arena_allocator); + break :blk val; } else blk: { const val = if (last_tag_val) |val| - try sema.intAdd(val, Value.one, int_tag_ty) + try sema.intAdd(val, Value.one_comptime_int, int_tag_ty, undefined) else - Value.zero; + try mod.intValue(int_tag_ty, 0); last_tag_val = val; - break :blk try val.copy(decl_arena_allocator); + break :blk val; }; - const gop = map.getOrPutAssumeCapacityContext(copied_val, .{ - .ty = int_tag_ty, - .mod = mod, - }); + const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern()); if (gop.found_existing) { - const field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy; - const other_field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = gop.index }).lazy; + const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy; + const other_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = gop.index }).lazy; const msg = msg: { - const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{copied_val.fmtValue(int_tag_ty, sema.mod)}); + const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{enum_tag_val.fmtValue(int_tag_ty, mod)}); errdefer msg.destroy(gpa); try sema.errNote(&block_scope, other_field_src, msg, "other occurrence here", .{}); break :msg msg; @@ -31943,19 +34589,19 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } // This string needs to outlive the ZIR code. - const field_name = try decl_arena_allocator.dupe(u8, field_name_zir); - if (enum_field_names) |set| { - set.putAssumeCapacity(field_name, {}); + const field_name = try ip.getOrPutString(gpa, field_name_zir); + if (enum_field_names.len != 0) { + enum_field_names[field_i] = field_name; } const field_ty: Type = if (!has_type) Type.void else if (field_type_ref == .none) - Type.initTag(.noreturn) + Type.noreturn else sema.resolveType(&block_scope, .unneeded, field_type_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const ty_src = union_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -31965,46 +34611,54 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { else => |e| return e, }; - if (field_ty.tag() == .generic_poison) { + if (field_ty.isGenericPoison()) { return error.GenericPoison; } const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { const msg = msg: { - const field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy; - const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{s}'", .{field_name}); + const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy; + const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{}'", .{ + field_name.fmt(ip), + }); errdefer msg.destroy(gpa); const prev_field_index = union_obj.fields.getIndex(field_name).?; - const prev_field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = prev_field_index }).lazy; - try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{}); + const prev_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = prev_field_index }).lazy; + try mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl, mod), msg, "other field here", .{}); try sema.errNote(&block_scope, src, msg, "union declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } - if (tag_ty_field_names) |*names| { - const enum_has_field = names.orderedRemove(field_name); - if (!enum_has_field) { + if (explicit_tags_seen.len > 0) { + const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; + const enum_index = tag_info.nameIndex(ip, field_name) orelse { const msg = msg: { - const ty_src = union_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; - const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) }); + const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{}' in enum '{}'", .{ + field_name.fmt(ip), union_obj.tag_ty.fmt(mod), + }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } + }; + // 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; } - if (field_ty.zigTypeTag() == .Opaque) { + if (field_ty.zigTypeTag(mod) == .Opaque) { const msg = msg: { - const ty_src = union_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -32018,11 +34672,11 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { const msg = msg: { - const ty_src = union_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .type, }); - const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); + const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .union_field); @@ -32031,13 +34685,13 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty))) { + } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { const msg = msg: { - const ty_src = union_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .type, }); - const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); + const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty); @@ -32049,14 +34703,14 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } gop.value_ptr.* = .{ - .ty = try field_ty.copy(decl_arena_allocator), + .ty = field_ty, .abi_align = 0, }; if (align_ref != .none) { gop.value_ptr.abi_align = sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const align_src = union_obj.fieldSrcLoc(sema.mod, .{ + const align_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .alignment, }).lazy; @@ -32070,22 +34724,29 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } } - if (tag_ty_field_names) |names| { - if (names.count() > 0) { + if (explicit_tags_seen.len > 0) { + const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; + if (tag_info.names.len > fields_len) { const msg = msg: { const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{}); errdefer msg.destroy(sema.gpa); const enum_ty = union_obj.tag_ty; - for (names.keys()) |field_name| { - const field_index = enum_ty.enumFieldIndex(field_name).?; - try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name}); + for (tag_info.names, 0..) |field_name, field_index| { + if (explicit_tags_seen[field_index]) continue; + try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{ + field_name.fmt(ip), + }); } try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } + } else if (enum_field_vals.count() > 0) { + union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_obj); + } else { + union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_obj); } } @@ -32097,116 +34758,103 @@ fn semaUnionFieldVal(sema: *Sema, block: *Block, src: LazySrcLoc, int_tag_ty: Ty fn generateUnionTagTypeNumbered( sema: *Sema, block: *Block, - fields_len: u32, - int_ty: Type, + enum_field_names: []const InternPool.NullTerminatedString, + enum_field_vals: []const InternPool.Index, union_obj: *Module.Union, ) !Type { const mod = sema.mod; - - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); - - const enum_obj = try new_decl_arena_allocator.create(Module.EnumNumbered); - const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumNumbered); - enum_ty_payload.* = .{ - .base = .{ .tag = .enum_numbered }, - .data = enum_obj, - }; - const enum_ty = Type.initPayload(&enum_ty_payload.base); - const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); + const gpa = sema.gpa; const src_decl = mod.declPtr(block.src_decl); const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); errdefer mod.destroyDecl(new_decl_index); - const name = name: { - const fqn = try union_obj.getFullyQualifiedName(mod); - defer sema.gpa.free(fqn); - break :name try std.fmt.allocPrintZ(mod.gpa, "@typeInfo({s}).Union.tag_type.?", .{fqn}); - }; + const fqn = try union_obj.getFullyQualifiedName(mod); + const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)}); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{ - .ty = Type.type, - .val = enum_val, + .ty = Type.noreturn, + .val = Value.@"unreachable", }, name); - sema.mod.declPtr(new_decl_index).name_fully_qualified = true; + errdefer mod.abortAnonDecl(new_decl_index); const new_decl = mod.declPtr(new_decl_index); + new_decl.name_fully_qualified = true; new_decl.owns_tv = true; new_decl.name_fully_qualified = true; - errdefer mod.abortAnonDecl(new_decl_index); - const copied_int_ty = try int_ty.copy(new_decl_arena_allocator); - enum_obj.* = .{ - .owner_decl = new_decl_index, - .tag_ty = copied_int_ty, - .fields = .{}, - .values = .{}, - }; - // Here we pre-allocate the maps using the decl arena. - try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); - try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ - .ty = copied_int_ty, - .mod = mod, - }); - try new_decl.finalizeNewArena(&new_decl_arena); - return enum_ty; -} + const enum_ty = try mod.intern(.{ .enum_type = .{ + .decl = new_decl_index, + .namespace = .none, + .tag_ty = if (enum_field_vals.len == 0) + (try mod.intType(.unsigned, 0)).toIntern() + else + mod.intern_pool.typeOf(enum_field_vals[0]), + .names = enum_field_names, + .values = enum_field_vals, + .tag_mode = .explicit, + } }); -fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize, maybe_union_obj: ?*Module.Union) !Type { - const mod = sema.mod; + new_decl.ty = Type.type; + new_decl.val = enum_ty.toValue(); - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); + try mod.finalizeAnonDecl(new_decl_index); + return enum_ty.toType(); +} - const enum_obj = try new_decl_arena_allocator.create(Module.EnumSimple); - const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumSimple); - enum_ty_payload.* = .{ - .base = .{ .tag = .enum_simple }, - .data = enum_obj, - }; - const enum_ty = Type.initPayload(&enum_ty_payload.base); - const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); +fn generateUnionTagTypeSimple( + sema: *Sema, + block: *Block, + enum_field_names: []const InternPool.NullTerminatedString, + maybe_union_obj: ?*Module.Union, +) !Type { + const mod = sema.mod; + const gpa = sema.gpa; const new_decl_index = new_decl_index: { const union_obj = maybe_union_obj orelse { break :new_decl_index try mod.createAnonymousDecl(block, .{ - .ty = Type.type, - .val = enum_val, + .ty = Type.noreturn, + .val = Value.@"unreachable", }); }; const src_decl = mod.declPtr(block.src_decl); const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); errdefer mod.destroyDecl(new_decl_index); - const name = name: { - const fqn = try union_obj.getFullyQualifiedName(mod); - defer sema.gpa.free(fqn); - break :name try std.fmt.allocPrintZ(mod.gpa, "@typeInfo({s}).Union.tag_type.?", .{fqn}); - }; + const fqn = try union_obj.getFullyQualifiedName(mod); + const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)}); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{ - .ty = Type.type, - .val = enum_val, + .ty = Type.noreturn, + .val = Value.@"unreachable", }, name); - sema.mod.declPtr(new_decl_index).name_fully_qualified = true; + mod.declPtr(new_decl_index).name_fully_qualified = true; break :new_decl_index new_decl_index; }; + errdefer mod.abortAnonDecl(new_decl_index); + + const enum_ty = try mod.intern(.{ .enum_type = .{ + .decl = new_decl_index, + .namespace = .none, + .tag_ty = if (enum_field_names.len == 0) + (try mod.intType(.unsigned, 0)).toIntern() + else + (try mod.smallestUnsignedInt(enum_field_names.len - 1)).toIntern(), + .names = enum_field_names, + .values = &.{}, + .tag_mode = .auto, + } }); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl_index); + new_decl.ty = Type.type; + new_decl.val = enum_ty.toValue(); - enum_obj.* = .{ - .owner_decl = new_decl_index, - .fields = .{}, - }; - // Here we pre-allocate the maps using the decl arena. - try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); - try new_decl.finalizeNewArena(&new_decl_arena); - return enum_ty; + try mod.finalizeAnonDecl(new_decl_index); + return enum_ty.toType(); } fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref { - var wip_captures = try WipCaptureScope.init(sema.gpa, sema.perm_arena, sema.owner_decl.src_scope); + const gpa = sema.gpa; + + var wip_captures = try WipCaptureScope.init(gpa, sema.owner_decl.src_scope); defer wip_captures.deinit(); var block: Block = .{ @@ -32220,19 +34868,20 @@ fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref { .is_comptime = true, }; defer { - block.instructions.deinit(sema.gpa); - block.params.deinit(sema.gpa); + block.instructions.deinit(gpa); + block.params.deinit(gpa); } const src = LazySrcLoc.nodeOffset(0); const mod = sema.mod; + const ip = &mod.intern_pool; const std_pkg = mod.main_pkg.table.get("std").?; const std_file = (mod.importPkg(std_pkg) catch unreachable).file; const opt_builtin_inst = (try sema.namespaceLookupRef( &block, src, mod.declPtr(std_file.root_decl.unwrap().?).src_namespace, - "builtin", + try ip.getOrPutString(gpa, "builtin"), )) orelse @panic("lib/std.zig is corrupt and missing 'builtin'"); const builtin_inst = try sema.analyzeLoad(&block, src, opt_builtin_inst, src); const builtin_ty = sema.analyzeAsType(&block, src, builtin_inst) catch |err| switch (err) { @@ -32242,8 +34891,8 @@ fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref { const opt_ty_decl = (try sema.namespaceLookup( &block, src, - builtin_ty.getNamespace().?, - name, + builtin_ty.getNamespaceIndex(mod).unwrap().?, + try ip.getOrPutString(gpa, name), )) orelse std.debug.panic("lib/std/builtin.zig is corrupt and missing '{s}'", .{name}); return sema.analyzeDeclVal(&block, src, opt_ty_decl); } @@ -32251,7 +34900,7 @@ fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref { fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type { const ty_inst = try sema.getBuiltin(name); - var wip_captures = try WipCaptureScope.init(sema.gpa, sema.perm_arena, sema.owner_decl.src_scope); + var wip_captures = try WipCaptureScope.init(sema.gpa, sema.owner_decl.src_scope); defer wip_captures.deinit(); var block: Block = .{ @@ -32284,343 +34933,287 @@ fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type { /// that the types are already resolved. /// TODO assert the return value matches `ty.onePossibleValue` pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { - switch (ty.tag()) { - .f16, - .f32, - .f64, - .f80, - .f128, - .c_longdouble, - .comptime_int, - .comptime_float, - .u1, - .u8, - .i8, - .u16, - .i16, - .u29, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_char, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .bool, - .type, - .anyerror, - .error_set_single, - .error_set, - .error_set_merged, - .error_union, - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - .single_const_pointer_to_comptime_int, - .array_sentinel, - .array_u8_sentinel_0, - .const_slice_u8, - .const_slice_u8_sentinel_0, - .const_slice, - .mut_slice, - .anyopaque, - .optional_single_mut_pointer, - .optional_single_const_pointer, - .enum_literal, - .anyerror_void_error_union, - .error_set_inferred, - .@"opaque", - .var_args_param, - .manyptr_u8, - .manyptr_const_u8, - .manyptr_const_u8_sentinel_0, - .atomic_order, - .atomic_rmw_op, - .calling_convention, - .address_space, - .float_mode, - .reduce_op, - .modifier, - .prefetch_options, - .export_options, - .extern_options, - .type_info, - .@"anyframe", - .anyframe_T, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .single_const_pointer, - .single_mut_pointer, - .pointer, - .bound_fn, - => return null, + const mod = sema.mod; + return switch (ty.toIntern()) { + .empty_struct_type => Value.empty_struct, + else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { + .int_type => |int_type| { + if (int_type.bits == 0) { + return try mod.intValue(ty, 0); + } else { + return null; + } + }, - .optional => { - var buf: Type.Payload.ElemType = undefined; - const child_ty = ty.optionalChild(&buf); - if (child_ty.isNoReturn()) { - return Value.null; - } else { + .ptr_type, + .error_union_type, + .func_type, + .anyframe_type, + .error_set_type, + .inferred_error_set_type, + => null, + + 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 + @boolToInt(has_sentinel) == 0) return (try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .elems = &.{} }, + } })).toValue(); + + if (try sema.typeHasOnePossibleValue(seq_type.child.toType())) |opv| { + return (try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .repeated_elem = opv.toIntern() }, + } })).toValue(); + } return null; - } - }, + }, + .opt_type => |child| { + if (child == .noreturn_type) { + return try mod.nullValue(ty); + } else { + return null; + } + }, - .@"struct" => { - const resolved_ty = try sema.resolveTypeFields(ty); - const s = resolved_ty.castTag(.@"struct").?.data; - for (s.fields.values(), 0..) |field, i| { - if (field.is_comptime) continue; - if (field.ty.eql(resolved_ty, sema.mod)) { + .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, + .type, + .anyerror, + .comptime_int, + .comptime_float, + .enum_literal, + .atomic_order, + .atomic_rmw_op, + .calling_convention, + .address_space, + .float_mode, + .reduce_op, + .call_modifier, + .prefetch_options, + .export_options, + .extern_options, + .type_info, + => null, + + .void => Value.void, + .noreturn => Value.@"unreachable", + .null => Value.null, + .undefined => Value.undef, + + .generic_poison => return error.GenericPoison, + }, + .struct_type => |struct_type| { + const resolved_ty = try sema.resolveTypeFields(ty); + if (mod.structPtrUnwrap(struct_type.index)) |s| { + const field_vals = try sema.arena.alloc(InternPool.Index, s.fields.count()); + for (field_vals, s.fields.values(), 0..) |*field_val, field, i| { + if (field.is_comptime) { + field_val.* = field.default_val; + continue; + } + if (field.ty.eql(resolved_ty, sema.mod)) { + const msg = try Module.ErrorMsg.create( + sema.gpa, + s.srcLoc(sema.mod), + "struct '{}' depends on itself", + .{ty.fmt(sema.mod)}, + ); + try sema.addFieldErrNote(resolved_ty, i, msg, "while checking this field", .{}); + return sema.failWithOwnedErrorMsg(msg); + } + if (try sema.typeHasOnePossibleValue(field.ty)) |field_opv| { + field_val.* = try field_opv.intern(field.ty, mod); + } else return null; + } + + // In this case the struct has no runtime-known fields and + // therefore has one possible value. + return (try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .elems = field_vals }, + } })).toValue(); + } + + // In this case the struct has no fields at all and + // therefore has one possible value. + return (try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .elems = &.{} }, + } })).toValue(); + }, + + .anon_struct_type => |tuple| { + for (tuple.values) |val| { + if (val == .none) return null; + } + // In this case the struct has all comptime-known fields and + // therefore has one possible value. + // TODO: write something like getCoercedInts to avoid needing to dupe + return (try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values) }, + } })).toValue(); + }, + + .union_type => |union_type| { + const resolved_ty = try sema.resolveTypeFields(ty); + const union_obj = mod.unionPtr(union_type.index); + const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse + return null; + const fields = union_obj.fields.values(); + if (fields.len == 0) { + const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); + return only.toValue(); + } + const only_field = fields[0]; + if (only_field.ty.eql(resolved_ty, sema.mod)) { const msg = try Module.ErrorMsg.create( sema.gpa, - s.srcLoc(sema.mod), - "struct '{}' depends on itself", + union_obj.srcLoc(sema.mod), + "union '{}' depends on itself", .{ty.fmt(sema.mod)}, ); - try sema.addFieldErrNote(resolved_ty, i, msg, "while checking this field", .{}); + try sema.addFieldErrNote(resolved_ty, 0, msg, "while checking this field", .{}); return sema.failWithOwnedErrorMsg(msg); } - if ((try sema.typeHasOnePossibleValue(field.ty)) == null) { + const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse return null; - } - } - return Value.initTag(.empty_struct_value); - }, - - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - for (tuple.values, 0..) |val, i| { - const is_comptime = val.tag() != .unreachable_value; - if (is_comptime) continue; - if ((try sema.typeHasOnePossibleValue(tuple.types[i])) != null) continue; - return null; - } - return Value.initTag(.empty_struct_value); - }, + const only = try mod.intern(.{ .un = .{ + .ty = resolved_ty.toIntern(), + .tag = tag_val.toIntern(), + .val = val_val.toIntern(), + } }); + return only.toValue(); + }, + .opaque_type => null, + .enum_type => |enum_type| switch (enum_type.tag_mode) { + .nonexhaustive => { + if (enum_type.tag_ty == .comptime_int_type) return null; + + if (try sema.typeHasOnePossibleValue(enum_type.tag_ty.toType())) |int_opv| { + const only = try mod.intern(.{ .enum_tag = .{ + .ty = ty.toIntern(), + .int = int_opv.toIntern(), + } }); + return only.toValue(); + } - .enum_numbered => { - const resolved_ty = try sema.resolveTypeFields(ty); - const enum_obj = resolved_ty.castTag(.enum_numbered).?.data; - // An explicit tag type is always provided for enum_numbered. - if (enum_obj.tag_ty.hasRuntimeBits()) { - return null; - } - if (enum_obj.fields.count() == 1) { - if (enum_obj.values.count() == 0) { - return Value.zero; // auto-numbered - } else { - return enum_obj.values.keys()[0]; - } - } else { - return null; - } - }, - .enum_full => { - const resolved_ty = try sema.resolveTypeFields(ty); - const enum_obj = resolved_ty.castTag(.enum_full).?.data; - if (enum_obj.tag_ty.hasRuntimeBits()) { - return null; - } - switch (enum_obj.fields.count()) { - 0 => return Value.initTag(.unreachable_value), - 1 => if (enum_obj.values.count() == 0) { - return Value.zero; // auto-numbered - } else { - return enum_obj.values.keys()[0]; + return null; }, - else => return null, - } - }, - .enum_simple => { - const resolved_ty = try sema.resolveTypeFields(ty); - const enum_simple = resolved_ty.castTag(.enum_simple).?.data; - switch (enum_simple.fields.count()) { - 0 => return Value.initTag(.unreachable_value), - 1 => return Value.zero, - else => return null, - } - }, - .enum_nonexhaustive => { - const tag_ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty; - if (tag_ty.zigTypeTag() != .ComptimeInt and !(try sema.typeHasRuntimeBits(tag_ty))) { - return Value.zero; - } else { - return null; - } - }, - .@"union", .union_safety_tagged, .union_tagged => { - const resolved_ty = try sema.resolveTypeFields(ty); - const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; - const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse - return null; - const fields = union_obj.fields.values(); - if (fields.len == 0) return Value.initTag(.unreachable_value); - const only_field = fields[0]; - if (only_field.ty.eql(resolved_ty, sema.mod)) { - const msg = try Module.ErrorMsg.create( - sema.gpa, - union_obj.srcLoc(sema.mod), - "union '{}' depends on itself", - .{ty.fmt(sema.mod)}, - ); - try sema.addFieldErrNote(resolved_ty, 0, msg, "while checking this field", .{}); - return sema.failWithOwnedErrorMsg(msg); - } - const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse - return null; - // TODO make this not allocate. The function in `Type.onePossibleValue` - // currently returns `empty_struct_value` and we should do that here too. - return try Value.Tag.@"union".create(sema.arena, .{ - .tag = tag_val, - .val = val_val, - }); - }, + .auto, .explicit => { + if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null; - .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value), - .void => return Value.void, - .noreturn => return Value.initTag(.unreachable_value), - .null => return Value.null, - .undefined => return Value.initTag(.undef), + switch (enum_type.names.len) { + 0 => { + const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); + return only.toValue(); + }, + 1 => return try mod.getCoerced((if (enum_type.values.len == 0) + try mod.intern(.{ .int = .{ + .ty = enum_type.tag_ty, + .storage = .{ .u64 = 0 }, + } }) + else + enum_type.values[0]).toValue(), ty), + else => return null, + } + }, + }, - .int_unsigned, .int_signed => { - if (ty.cast(Type.Payload.Bits).?.data == 0) { - return Value.zero; - } else { - return null; - } + // values, not types + .undef, + .runtime_value, + .simple_value, + .variable, + .extern_func, + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .empty_enum_value, + .float, + .ptr, + .opt, + .aggregate, + .un, + // memoization, not types + .memoized_call, + => unreachable, }, - .vector, .array, .array_u8 => { - if (ty.arrayLen() == 0) - return Value.initTag(.empty_array); - if ((try sema.typeHasOnePossibleValue(ty.elemType())) != null) { - return Value.initTag(.the_only_possible_value); - } - return null; - }, - - .inferred_alloc_const => unreachable, - .inferred_alloc_mut => unreachable, - .generic_poison => return error.GenericPoison, - } + }; } /// Returns the type of the AIR instruction. fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type { - return sema.getTmpAir().typeOf(inst); + return sema.getTmpAir().typeOf(inst, &sema.mod.intern_pool); } pub fn getTmpAir(sema: Sema) Air { return .{ .instructions = sema.air_instructions.slice(), .extra = sema.air_extra.items, - .values = sema.air_values.items, }; } pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref { - switch (ty.tag()) { - .u1 => return .u1_type, - .u8 => return .u8_type, - .i8 => return .i8_type, - .u16 => return .u16_type, - .u29 => return .u29_type, - .i16 => return .i16_type, - .u32 => return .u32_type, - .i32 => return .i32_type, - .u64 => return .u64_type, - .i64 => return .i64_type, - .u128 => return .u128_type, - .i128 => return .i128_type, - .usize => return .usize_type, - .isize => return .isize_type, - .c_short => return .c_short_type, - .c_ushort => return .c_ushort_type, - .c_int => return .c_int_type, - .c_uint => return .c_uint_type, - .c_long => return .c_long_type, - .c_ulong => return .c_ulong_type, - .c_longlong => return .c_longlong_type, - .c_ulonglong => return .c_ulonglong_type, - .c_longdouble => return .c_longdouble_type, - .f16 => return .f16_type, - .f32 => return .f32_type, - .f64 => return .f64_type, - .f80 => return .f80_type, - .f128 => return .f128_type, - .anyopaque => return .anyopaque_type, - .bool => return .bool_type, - .void => return .void_type, - .type => return .type_type, - .anyerror => return .anyerror_type, - .comptime_int => return .comptime_int_type, - .comptime_float => return .comptime_float_type, - .noreturn => return .noreturn_type, - .@"anyframe" => return .anyframe_type, - .null => return .null_type, - .undefined => return .undefined_type, - .enum_literal => return .enum_literal_type, - .atomic_order => return .atomic_order_type, - .atomic_rmw_op => return .atomic_rmw_op_type, - .calling_convention => return .calling_convention_type, - .address_space => return .address_space_type, - .float_mode => return .float_mode_type, - .reduce_op => return .reduce_op_type, - .modifier => return .modifier_type, - .prefetch_options => return .prefetch_options_type, - .export_options => return .export_options_type, - .extern_options => return .extern_options_type, - .type_info => return .type_info_type, - .manyptr_u8 => return .manyptr_u8_type, - .manyptr_const_u8 => return .manyptr_const_u8_type, - .fn_noreturn_no_args => return .fn_noreturn_no_args_type, - .fn_void_no_args => return .fn_void_no_args_type, - .fn_naked_noreturn_no_args => return .fn_naked_noreturn_no_args_type, - .fn_ccc_void_no_args => return .fn_ccc_void_no_args_type, - .single_const_pointer_to_comptime_int => return .single_const_pointer_to_comptime_int_type, - .const_slice_u8 => return .const_slice_u8_type, - .anyerror_void_error_union => return .anyerror_void_error_union_type, - .generic_poison => return .generic_poison_type, - else => {}, - } + if (@enumToInt(ty.toIntern()) < Air.ref_start_index) + return @intToEnum(Air.Inst.Ref, @enumToInt(ty.toIntern())); try sema.air_instructions.append(sema.gpa, .{ - .tag = .const_ty, - .data = .{ .ty = ty }, + .tag = .interned, + .data = .{ .interned = ty.toIntern() }, }); return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1)); } fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref { - return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int)); + const mod = sema.mod; + return sema.addConstant(ty, try mod.intValue(ty, int)); } fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref { - return sema.addConstant(ty, Value.undef); + return sema.addConstant(ty, (try sema.mod.intern(.{ .undef = ty.toIntern() })).toValue()); } pub fn addConstant(sema: *Sema, ty: Type, val: Value) SemaError!Air.Inst.Ref { + const mod = sema.mod; const gpa = sema.gpa; - const ty_inst = try sema.addType(ty); - try sema.air_values.append(gpa, val); + + // This assertion can be removed when the `ty` parameter is removed from + // this function thanks to the InternPool transition being complete. + if (std.debug.runtime_safety) { + const val_ty = mod.intern_pool.typeOf(val.toIntern()); + if (ty.toIntern() != val_ty) { + std.debug.panic("addConstant type mismatch: '{}' vs '{}'\n", .{ + ty.fmt(mod), val_ty.toType().fmt(mod), + }); + } + } + if (@enumToInt(val.toIntern()) < Air.ref_start_index) + return @intToEnum(Air.Inst.Ref, @enumToInt(val.toIntern())); try sema.air_instructions.append(gpa, .{ - .tag = .constant, - .data = .{ .ty_pl = .{ - .ty = ty_inst, - .payload = @intCast(u32, sema.air_values.items.len - 1), - } }, + .tag = .interned, + .data = .{ .interned = val.toIntern() }, }); return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1)); } @@ -32639,7 +35232,8 @@ pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 { u32 => @field(extra, field.name), Air.Inst.Ref => @enumToInt(@field(extra, field.name)), i32 => @bitCast(u32, @field(extra, field.name)), - else => @compileError("bad field type"), + InternPool.Index => @enumToInt(@field(extra, field.name)), + else => @compileError("bad field type: " ++ @typeName(field.type)), }); } return result; @@ -32685,21 +35279,25 @@ fn analyzeComptimeAlloc( defer anon_decl.deinit(); const decl_index = try anon_decl.finish( - try var_type.copy(anon_decl.arena()), + var_type, // There will be stores before the first load, but they may be to sub-elements or // sub-fields. So we need to initialize with undef to allow the mechanism to expand // into fields/elements and have those overridden with stored values. - Value.undef, + (try sema.mod.intern(.{ .undef = var_type.toIntern() })).toValue(), alignment, ); const decl = sema.mod.declPtr(decl_index); decl.@"align" = alignment; + try sema.comptime_mutable_decls.append(decl_index); try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index); - return sema.addConstant(ptr_type, try Value.Tag.decl_ref_mut.create(sema.arena, .{ - .runtime_index = block.runtime_index, - .decl_index = decl_index, - })); + return sema.addConstant(ptr_type, (try sema.mod.intern(.{ .ptr = .{ + .ty = ptr_type.toIntern(), + .addr = .{ .mut_decl = .{ + .decl = decl_index, + .runtime_index = block.runtime_index, + } }, + } })).toValue()); } /// The places where a user can specify an address space attribute @@ -32727,8 +35325,9 @@ pub fn analyzeAddressSpace( zir_ref: Zir.Inst.Ref, ctx: AddressSpaceContext, ) !std.builtin.AddressSpace { + const mod = sema.mod; const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref, "addresspace must be comptime-known"); - const address_space = addrspace_tv.val.toEnum(std.builtin.AddressSpace); + const address_space = mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val); const target = sema.mod.getTarget(); const arch = target.cpu.arch; @@ -32771,8 +35370,9 @@ pub fn analyzeAddressSpace( /// Asserts the value is a pointer and dereferences it. /// Returns `null` if the pointer contents cannot be loaded at comptime. fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value { - const load_ty = ptr_ty.childType(); - const res = try sema.pointerDerefExtra(block, src, ptr_val, load_ty, true); + const mod = sema.mod; + const load_ty = ptr_ty.childType(mod); + const res = try sema.pointerDerefExtra(block, src, ptr_val, load_ty); switch (res) { .runtime_load => return null, .val => |v| return v, @@ -32798,8 +35398,9 @@ const DerefResult = union(enum) { out_of_bounds: Type, }; -fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, load_ty: Type, want_mutable: bool) CompileError!DerefResult { - const target = sema.mod.getTarget(); +fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, load_ty: Type) CompileError!DerefResult { + const mod = sema.mod; + const target = mod.getTarget(); const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) { error.RuntimeLoad => return DerefResult{ .runtime_load = {} }, else => |e| return e, @@ -32812,19 +35413,17 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value if (coerce_in_mem_ok) { // We have a Value that lines up in virtual memory exactly with what we want to load, // and it is in-memory coercible to load_ty. It may be returned without modifications. - if (deref.is_mutable and want_mutable) { - // The decl whose value we are obtaining here may be overwritten with - // a different value upon further semantic analysis, which would - // invalidate this memory. So we must copy here. - return DerefResult{ .val = try tv.val.copy(sema.arena) }; - } - return DerefResult{ .val = tv.val }; + // Move mutable decl values to the InternPool and assert other decls are already in + // the InternPool. + const uncoerced_val = if (deref.is_mutable) try tv.val.intern(tv.ty, mod) else tv.val.toIntern(); + const coerced_val = try mod.getCoerced(uncoerced_val.toValue(), load_ty); + return .{ .val = coerced_val }; } } // The type is not in-memory coercible or the direct dereference failed, so it must // be bitcast according to the pointer type we are performing the load through. - if (!load_ty.hasWellDefinedLayout()) { + if (!load_ty.hasWellDefinedLayout(mod)) { return DerefResult{ .needed_well_defined = load_ty }; } @@ -32861,59 +35460,32 @@ fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError /// 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, - buf: *Type.Payload.ElemType, -) !?Type { - switch (ty.tag()) { - .optional_single_const_pointer, - .optional_single_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - => return ty.optionalChild(buf), - - .single_const_pointer_to_comptime_int, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .manyptr_u8, - .manyptr_const_u8, - .manyptr_const_u8_sentinel_0, - => return ty, - - .pointer => switch (ty.ptrSize()) { - .Slice => return null, - .C => return ty.optionalChild(buf), - else => return ty, - }, - - .inferred_alloc_const => unreachable, - .inferred_alloc_mut => unreachable, - - .optional => { - const child_type = ty.optionalChild(buf); - if (child_type.zigTypeTag() != .Pointer) return null; - - const info = child_type.ptrInfo().data; - switch (info.size) { - .Slice, .C => return null, +fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type { + const mod = sema.mod; + return switch (mod.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 (mod.intern_pool.indexToKey(opt_child)) { + .ptr_type => |ptr_type| switch (ptr_type.flags.size) { + .Slice, .C => null, .Many, .One => { - if (info.@"allowzero") return null; + if (ptr_type.flags.is_allowzero) return null; // optionals of zero sized types behave like bools, not pointers - if ((try sema.typeHasOnePossibleValue(child_type)) != null) { + const payload_ty = opt_child.toType(); + if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) { return null; } - return child_type; + return payload_ty; }, - } + }, + else => null, }, - - else => return null, - } + else => null, + }; } /// `generic_poison` will return false. @@ -32923,203 +35495,170 @@ fn typePtrOrOptionalPtrTy( /// TODO merge these implementations together with the "advanced"/opt_sema pattern seen /// elsewhere in value.zig pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { - return switch (ty.tag()) { - .u1, - .u8, - .i8, - .u16, - .i16, - .u29, - .u32, - .i32, - .u64, - .i64, - .u128, - .i128, - .usize, - .isize, - .c_char, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .f16, - .f32, - .f64, - .f80, - .f128, - .anyopaque, - .bool, - .void, - .anyerror, - .noreturn, - .@"anyframe", - .null, - .undefined, - .atomic_order, - .atomic_rmw_op, - .calling_convention, - .address_space, - .float_mode, - .reduce_op, - .modifier, - .prefetch_options, - .export_options, - .extern_options, - .manyptr_u8, - .manyptr_const_u8, - .manyptr_const_u8_sentinel_0, - .const_slice_u8, - .const_slice_u8_sentinel_0, - .anyerror_void_error_union, - .empty_struct_literal, - .empty_struct, - .error_set, - .error_set_single, - .error_set_inferred, - .error_set_merged, - .@"opaque", - .generic_poison, - .array_u8, - .array_u8_sentinel_0, - .int_signed, - .int_unsigned, - .enum_simple, - => false, - - .single_const_pointer_to_comptime_int, - .type, - .comptime_int, - .comptime_float, - .enum_literal, - .type_info, - // These are function bodies, not function pointers. - .fn_noreturn_no_args, - .fn_void_no_args, - .fn_naked_noreturn_no_args, - .fn_ccc_void_no_args, - .function, - => true, - - .var_args_param => unreachable, - .inferred_alloc_mut => unreachable, - .inferred_alloc_const => unreachable, - .bound_fn => unreachable, - - .array, - .array_sentinel, - .vector, - => return sema.typeRequiresComptime(ty.childType()), - - .pointer, - .single_const_pointer, - .single_mut_pointer, - .many_const_pointer, - .many_mut_pointer, - .c_const_pointer, - .c_mut_pointer, - .const_slice, - .mut_slice, - => { - const child_ty = ty.childType(); - if (child_ty.zigTypeTag() == .Fn) { - return child_ty.fnInfo().is_generic; - } else { - return sema.typeRequiresComptime(child_ty); - } - }, - - .optional, - .optional_single_mut_pointer, - .optional_single_const_pointer, - => { - var buf: Type.Payload.ElemType = undefined; - return sema.typeRequiresComptime(ty.optionalChild(&buf)); - }, - - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - for (tuple.types, 0..) |field_ty, i| { - const have_comptime_val = tuple.values[i].tag() != .unreachable_value; - if (!have_comptime_val and try sema.typeRequiresComptime(field_ty)) { - return true; + const mod = sema.mod; + return switch (ty.toIntern()) { + .empty_struct_type => false, + + else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { + .int_type => return false, + .ptr_type => |ptr_type| { + const child_ty = ptr_type.child.toType(); + if (child_ty.zigTypeTag(mod) == .Fn) { + return mod.typeToFunc(child_ty).?.is_generic; + } else { + return sema.typeRequiresComptime(child_ty); } - } - return false; - }, - - .@"struct" => { - const struct_obj = ty.castTag(.@"struct").?.data; - switch (struct_obj.requires_comptime) { - .no, .wip => return false, - .yes => return true, - .unknown => { - if (struct_obj.status == .field_types_wip) - return false; + }, + .anyframe_type => |child| { + if (child == .none) return false; + return sema.typeRequiresComptime(child.toType()); + }, + .array_type => |array_type| return sema.typeRequiresComptime(array_type.child.toType()), + .vector_type => |vector_type| return sema.typeRequiresComptime(vector_type.child.toType()), + .opt_type => |child| return sema.typeRequiresComptime(child.toType()), - try sema.resolveTypeFieldsStruct(ty, struct_obj); + .error_union_type => |error_union_type| { + return sema.typeRequiresComptime(error_union_type.payload_type.toType()); + }, - struct_obj.requires_comptime = .wip; - for (struct_obj.fields.values()) |field| { - if (field.is_comptime) continue; - if (try sema.typeRequiresComptime(field.ty)) { - struct_obj.requires_comptime = .yes; - return true; + .error_set_type, .inferred_error_set_type => false, + + .func_type => true, + + .simple_type => |t| return 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, + .noreturn, + .generic_poison, + .atomic_order, + .atomic_rmw_op, + .calling_convention, + .address_space, + .float_mode, + .reduce_op, + .call_modifier, + .prefetch_options, + .export_options, + .extern_options, + => false, + + .type, + .comptime_int, + .comptime_float, + .null, + .undefined, + .enum_literal, + .type_info, + => true, + }, + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false; + switch (struct_obj.requires_comptime) { + .no, .wip => return false, + .yes => return true, + .unknown => { + if (struct_obj.status == .field_types_wip) + return false; + + try sema.resolveTypeFieldsStruct(ty, struct_obj); + + struct_obj.requires_comptime = .wip; + for (struct_obj.fields.values()) |field| { + if (field.is_comptime) continue; + if (try sema.typeRequiresComptime(field.ty)) { + struct_obj.requires_comptime = .yes; + return true; + } } - } - struct_obj.requires_comptime = .no; - return false; - }, - } - }, - - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Type.Payload.Union).?.data; - switch (union_obj.requires_comptime) { - .no, .wip => return false, - .yes => return true, - .unknown => { - if (union_obj.status == .field_types_wip) + struct_obj.requires_comptime = .no; return false; + }, + } + }, + .anon_struct_type => |tuple| { + for (tuple.types, tuple.values) |field_ty, val| { + const have_comptime_val = val != .none; + if (!have_comptime_val and try sema.typeRequiresComptime(field_ty.toType())) { + return true; + } + } + return false; + }, - try sema.resolveTypeFieldsUnion(ty, union_obj); - - union_obj.requires_comptime = .wip; - for (union_obj.fields.values()) |field| { - if (try sema.typeRequiresComptime(field.ty)) { - union_obj.requires_comptime = .yes; - return true; + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + switch (union_obj.requires_comptime) { + .no, .wip => return false, + .yes => return true, + .unknown => { + if (union_obj.status == .field_types_wip) + return false; + + try sema.resolveTypeFieldsUnion(ty, union_obj); + + union_obj.requires_comptime = .wip; + for (union_obj.fields.values()) |field| { + if (try sema.typeRequiresComptime(field.ty)) { + union_obj.requires_comptime = .yes; + return true; + } } - } - union_obj.requires_comptime = .no; - return false; - }, - } - }, + union_obj.requires_comptime = .no; + return false; + }, + } + }, - .error_union => return sema.typeRequiresComptime(ty.errorUnionPayload()), - .anyframe_T => { - const child_ty = ty.castTag(.anyframe_T).?.data; - return sema.typeRequiresComptime(child_ty); - }, - .enum_numbered => { - const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty; - return sema.typeRequiresComptime(tag_ty); - }, - .enum_full, .enum_nonexhaustive => { - const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty; - return sema.typeRequiresComptime(tag_ty); + .opaque_type => false, + .enum_type => |enum_type| try sema.typeRequiresComptime(enum_type.tag_ty.toType()), + + // values, not types + .undef, + .runtime_value, + .simple_value, + .variable, + .extern_func, + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .empty_enum_value, + .float, + .ptr, + .opt, + .aggregate, + .un, + // memoization, not types + .memoized_call, + => unreachable, }, }; } pub fn typeHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { - return ty.hasRuntimeBitsAdvanced(false, .{ .sema = sema }) catch |err| switch (err) { + const mod = sema.mod; + return ty.hasRuntimeBitsAdvanced(mod, false, .{ .sema = sema }) catch |err| switch (err) { error.NeedLazy => unreachable, else => |e| return e, }; @@ -33127,19 +35666,18 @@ pub fn typeHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { fn typeAbiSize(sema: *Sema, ty: Type) !u64 { try sema.resolveTypeLayout(ty); - const target = sema.mod.getTarget(); - return ty.abiSize(target); + return ty.abiSize(sema.mod); } fn typeAbiAlignment(sema: *Sema, ty: Type) CompileError!u32 { - const target = sema.mod.getTarget(); - return (try ty.abiAlignmentAdvanced(target, .{ .sema = sema })).scalar; + return (try ty.abiAlignmentAdvanced(sema.mod, .{ .sema = sema })).scalar; } /// Not valid to call for packed unions. /// Keep implementation in sync with `Module.Union.Field.normalAlignment`. fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 { - if (field.ty.zigTypeTag() == .NoReturn) { + const mod = sema.mod; + if (field.ty.zigTypeTag(mod) == .NoReturn) { return @as(u32, 0); } else if (field.abi_align == 0) { return sema.typeAbiAlignment(field.ty); @@ -33150,7 +35688,8 @@ fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 { /// Synchronize logic with `Type.isFnOrHasRuntimeBits`. pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { - const fn_info = ty.fnInfo(); + const mod = sema.mod; + const fn_info = mod.typeToFunc(ty).?; if (fn_info.is_generic) return false; if (fn_info.is_var_args) return true; switch (fn_info.cc) { @@ -33158,7 +35697,7 @@ pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { .Inline => return false, else => {}, } - if (try sema.typeRequiresComptime(fn_info.return_type)) { + if (try sema.typeRequiresComptime(fn_info.return_type.toType())) { return false; } return true; @@ -33168,11 +35707,12 @@ fn unionFieldIndex( sema: *Sema, block: *Block, unresolved_union_ty: Type, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_src: LazySrcLoc, ) !u32 { + const mod = sema.mod; const union_ty = try sema.resolveTypeFields(unresolved_union_ty); - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const field_index_usize = union_obj.fields.getIndex(field_name) orelse return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); return @intCast(u32, field_index_usize); @@ -33182,14 +35722,15 @@ fn structFieldIndex( sema: *Sema, block: *Block, unresolved_struct_ty: Type, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_src: LazySrcLoc, ) !u32 { + const mod = sema.mod; const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); - if (struct_ty.isAnonStruct()) { + if (struct_ty.isAnonStruct(mod)) { return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src); } else { - const struct_obj = struct_ty.castTag(.@"struct").?.data; + const struct_obj = mod.typeToStruct(struct_ty).?; const field_index_usize = struct_obj.fields.getIndex(field_name) orelse return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); return @intCast(u32, field_index_usize); @@ -33200,55 +35741,98 @@ fn anonStructFieldIndex( sema: *Sema, block: *Block, struct_ty: Type, - field_name: []const u8, + field_name: InternPool.NullTerminatedString, field_src: LazySrcLoc, ) !u32 { - const anon_struct = struct_ty.castTag(.anon_struct).?.data; - for (anon_struct.names, 0..) |name, i| { - if (mem.eql(u8, name, field_name)) { - return @intCast(u32, i); - } + const mod = sema.mod; + switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) { + .anon_struct_type => |anon_struct_type| for (anon_struct_type.names, 0..) |name, i| { + if (name == field_name) return @intCast(u32, i); + }, + .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| { + for (struct_obj.fields.keys(), 0..) |name, i| { + if (name == field_name) { + return @intCast(u32, i); + } + } + }, + else => unreachable, } - return sema.fail(block, field_src, "no field named '{s}' in anonymous struct '{}'", .{ - field_name, struct_ty.fmt(sema.mod), + return sema.fail(block, field_src, "no field named '{}' in anonymous struct '{}'", .{ + field_name.fmt(&mod.intern_pool), struct_ty.fmt(sema.mod), }); } fn queueFullTypeResolution(sema: *Sema, ty: Type) !void { - const inst_ref = try sema.addType(ty); - try sema.types_to_resolve.append(sema.gpa, inst_ref); + try sema.types_to_resolve.put(sema.gpa, ty.toIntern(), {}); +} + +/// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting +/// overflow_idx to the vector index the overflow was at (or 0 for a scalar). +fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value { + var overflow: usize = undefined; + return sema.intAddInner(lhs, rhs, ty, &overflow) catch |err| switch (err) { + error.Overflow => { + const is_vec = ty.isVector(sema.mod); + overflow_idx.* = if (is_vec) overflow else 0; + const safe_ty = if (is_vec) try sema.mod.vectorType(.{ + .len = ty.vectorLen(sema.mod), + .child = .comptime_int_type, + }) else Type.comptime_int; + return sema.intAddInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) { + error.Overflow => unreachable, + else => |e| return e, + }; + }, + else => |e| return e, + }; } -fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value { - if (ty.zigTypeTag() == .Vector) { - const result_data = try sema.arena.alloc(Value, ty.vectorLen()); +fn intAddInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value { + const mod = sema.mod; + if (ty.zigTypeTag(mod) == .Vector) { + const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); + const scalar_ty = ty.scalarType(mod); for (result_data, 0..) |*scalar, i| { - var lhs_buf: Value.ElemValueBuffer = undefined; - var rhs_buf: Value.ElemValueBuffer = undefined; - const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf); - const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf); - scalar.* = try sema.intAddScalar(lhs_elem, rhs_elem); + const lhs_elem = try lhs.elemValue(mod, i); + const rhs_elem = try rhs.elemValue(mod, i); + const val = sema.intAddScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) { + error.Overflow => { + overflow_idx.* = i; + return error.Overflow; + }, + else => |e| return e, + }; + scalar.* = try val.intern(scalar_ty, mod); } - return Value.Tag.aggregate.create(sema.arena, result_data); + return (try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .elems = result_data }, + } })).toValue(); } - return sema.intAddScalar(lhs, rhs); + return sema.intAddScalar(lhs, rhs, ty); } -fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value) !Value { +fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value { + const mod = sema.mod; + if (scalar_ty.toIntern() != .comptime_int_type) { + const res = try sema.intAddWithOverflowScalar(lhs, rhs, scalar_ty); + if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow; + return res.wrapped_result; + } // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const target = sema.mod.getTarget(); - const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema); - const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema); + const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); + const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); const limbs = try sema.arena.alloc( std.math.big.Limb, - std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, + @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, ); var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.add(lhs_bigint, rhs_bigint); - return Value.fromBigInt(sema.arena, result_bigint.toConst()); + return mod.intValue_big(scalar_ty, result_bigint.toConst()); } /// Supports both floats and ints; handles undefined. @@ -33258,55 +35842,87 @@ fn numberAddWrapScalar( rhs: Value, ty: Type, ) !Value { - if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); + const mod = sema.mod; + if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; - if (ty.zigTypeTag() == .ComptimeInt) { - return sema.intAdd(lhs, rhs, ty); + if (ty.zigTypeTag(mod) == .ComptimeInt) { + return sema.intAdd(lhs, rhs, ty, undefined); } if (ty.isAnyFloat()) { - return sema.floatAdd(lhs, rhs, ty); + return Value.floatAdd(lhs, rhs, ty, sema.arena, mod); } const overflow_result = try sema.intAddWithOverflow(lhs, rhs, ty); return overflow_result.wrapped_result; } -fn intSub( - sema: *Sema, - lhs: Value, - rhs: Value, - ty: Type, -) !Value { - if (ty.zigTypeTag() == .Vector) { - const result_data = try sema.arena.alloc(Value, ty.vectorLen()); +/// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting +/// overflow_idx to the vector index the overflow was at (or 0 for a scalar). +fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value { + var overflow: usize = undefined; + return sema.intSubInner(lhs, rhs, ty, &overflow) catch |err| switch (err) { + error.Overflow => { + const is_vec = ty.isVector(sema.mod); + overflow_idx.* = if (is_vec) overflow else 0; + const safe_ty = if (is_vec) try sema.mod.vectorType(.{ + .len = ty.vectorLen(sema.mod), + .child = .comptime_int_type, + }) else Type.comptime_int; + return sema.intSubInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) { + error.Overflow => unreachable, + else => |e| return e, + }; + }, + else => |e| return e, + }; +} + +fn intSubInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value { + const mod = sema.mod; + if (ty.zigTypeTag(mod) == .Vector) { + const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); + const scalar_ty = ty.scalarType(mod); for (result_data, 0..) |*scalar, i| { - var lhs_buf: Value.ElemValueBuffer = undefined; - var rhs_buf: Value.ElemValueBuffer = undefined; - const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf); - const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf); - scalar.* = try sema.intSubScalar(lhs_elem, rhs_elem); + const lhs_elem = try lhs.elemValue(sema.mod, i); + const rhs_elem = try rhs.elemValue(sema.mod, i); + const val = sema.intSubScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) { + error.Overflow => { + overflow_idx.* = i; + return error.Overflow; + }, + else => |e| return e, + }; + scalar.* = try val.intern(scalar_ty, mod); } - return Value.Tag.aggregate.create(sema.arena, result_data); + return (try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .elems = result_data }, + } })).toValue(); } - return sema.intSubScalar(lhs, rhs); + return sema.intSubScalar(lhs, rhs, ty); } -fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value) !Value { +fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value { + const mod = sema.mod; + if (scalar_ty.toIntern() != .comptime_int_type) { + const res = try sema.intSubWithOverflowScalar(lhs, rhs, scalar_ty); + if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow; + return res.wrapped_result; + } // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const target = sema.mod.getTarget(); - const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema); - const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema); + const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); + const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); const limbs = try sema.arena.alloc( std.math.big.Limb, - std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, + @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, ); var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.sub(lhs_bigint, rhs_bigint); - return Value.fromBigInt(sema.arena, result_bigint.toConst()); + return mod.intValue_big(scalar_ty, result_bigint.toConst()); } /// Supports both floats and ints; handles undefined. @@ -33316,155 +35932,49 @@ fn numberSubWrapScalar( rhs: Value, ty: Type, ) !Value { - if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); + const mod = sema.mod; + if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; - if (ty.zigTypeTag() == .ComptimeInt) { - return sema.intSub(lhs, rhs, ty); + if (ty.zigTypeTag(mod) == .ComptimeInt) { + return sema.intSub(lhs, rhs, ty, undefined); } if (ty.isAnyFloat()) { - return sema.floatSub(lhs, rhs, ty); + return Value.floatSub(lhs, rhs, ty, sema.arena, mod); } const overflow_result = try sema.intSubWithOverflow(lhs, rhs, ty); return overflow_result.wrapped_result; } -fn floatAdd( - sema: *Sema, - lhs: Value, - rhs: Value, - float_type: Type, -) !Value { - if (float_type.zigTypeTag() == .Vector) { - const result_data = try sema.arena.alloc(Value, float_type.vectorLen()); - for (result_data, 0..) |*scalar, i| { - var lhs_buf: Value.ElemValueBuffer = undefined; - var rhs_buf: Value.ElemValueBuffer = undefined; - const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf); - const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf); - scalar.* = try sema.floatAddScalar(lhs_elem, rhs_elem, float_type.scalarType()); - } - return Value.Tag.aggregate.create(sema.arena, result_data); - } - return sema.floatAddScalar(lhs, rhs, float_type); -} - -fn floatAddScalar( - sema: *Sema, - lhs: Value, - rhs: Value, - float_type: Type, -) !Value { - const target = sema.mod.getTarget(); - switch (float_type.floatBits(target)) { - 16 => { - const lhs_val = lhs.toFloat(f16); - const rhs_val = rhs.toFloat(f16); - return Value.Tag.float_16.create(sema.arena, lhs_val + rhs_val); - }, - 32 => { - const lhs_val = lhs.toFloat(f32); - const rhs_val = rhs.toFloat(f32); - return Value.Tag.float_32.create(sema.arena, lhs_val + rhs_val); - }, - 64 => { - const lhs_val = lhs.toFloat(f64); - const rhs_val = rhs.toFloat(f64); - return Value.Tag.float_64.create(sema.arena, lhs_val + rhs_val); - }, - 80 => { - const lhs_val = lhs.toFloat(f80); - const rhs_val = rhs.toFloat(f80); - return Value.Tag.float_80.create(sema.arena, lhs_val + rhs_val); - }, - 128 => { - const lhs_val = lhs.toFloat(f128); - const rhs_val = rhs.toFloat(f128); - return Value.Tag.float_128.create(sema.arena, lhs_val + rhs_val); - }, - else => unreachable, - } -} - -fn floatSub( - sema: *Sema, - lhs: Value, - rhs: Value, - float_type: Type, -) !Value { - if (float_type.zigTypeTag() == .Vector) { - const result_data = try sema.arena.alloc(Value, float_type.vectorLen()); - for (result_data, 0..) |*scalar, i| { - var lhs_buf: Value.ElemValueBuffer = undefined; - var rhs_buf: Value.ElemValueBuffer = undefined; - const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf); - const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf); - scalar.* = try sema.floatSubScalar(lhs_elem, rhs_elem, float_type.scalarType()); - } - return Value.Tag.aggregate.create(sema.arena, result_data); - } - return sema.floatSubScalar(lhs, rhs, float_type); -} - -fn floatSubScalar( - sema: *Sema, - lhs: Value, - rhs: Value, - float_type: Type, -) !Value { - const target = sema.mod.getTarget(); - switch (float_type.floatBits(target)) { - 16 => { - const lhs_val = lhs.toFloat(f16); - const rhs_val = rhs.toFloat(f16); - return Value.Tag.float_16.create(sema.arena, lhs_val - rhs_val); - }, - 32 => { - const lhs_val = lhs.toFloat(f32); - const rhs_val = rhs.toFloat(f32); - return Value.Tag.float_32.create(sema.arena, lhs_val - rhs_val); - }, - 64 => { - const lhs_val = lhs.toFloat(f64); - const rhs_val = rhs.toFloat(f64); - return Value.Tag.float_64.create(sema.arena, lhs_val - rhs_val); - }, - 80 => { - const lhs_val = lhs.toFloat(f80); - const rhs_val = rhs.toFloat(f80); - return Value.Tag.float_80.create(sema.arena, lhs_val - rhs_val); - }, - 128 => { - const lhs_val = lhs.toFloat(f128); - const rhs_val = rhs.toFloat(f128); - return Value.Tag.float_128.create(sema.arena, lhs_val - rhs_val); - }, - else => unreachable, - } -} - fn intSubWithOverflow( sema: *Sema, lhs: Value, rhs: Value, ty: Type, ) !Value.OverflowArithmeticResult { - if (ty.zigTypeTag() == .Vector) { - const overflowed_data = try sema.arena.alloc(Value, ty.vectorLen()); - const result_data = try sema.arena.alloc(Value, ty.vectorLen()); - for (result_data, 0..) |*scalar, i| { - var lhs_buf: Value.ElemValueBuffer = undefined; - var rhs_buf: Value.ElemValueBuffer = undefined; - const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf); - const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf); - const of_math_result = try sema.intSubWithOverflowScalar(lhs_elem, rhs_elem, ty.scalarType()); - overflowed_data[i] = of_math_result.overflow_bit; - scalar.* = of_math_result.wrapped_result; + const mod = sema.mod; + if (ty.zigTypeTag(mod) == .Vector) { + const vec_len = ty.vectorLen(mod); + const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len); + const result_data = try sema.arena.alloc(InternPool.Index, vec_len); + const scalar_ty = ty.scalarType(mod); + for (overflowed_data, result_data, 0..) |*of, *scalar, i| { + const lhs_elem = try lhs.elemValue(sema.mod, i); + const rhs_elem = try rhs.elemValue(sema.mod, i); + const of_math_result = try sema.intSubWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty); + of.* = try of_math_result.overflow_bit.intern(Type.u1, mod); + scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod); } return Value.OverflowArithmeticResult{ - .overflow_bit = try Value.Tag.aggregate.create(sema.arena, overflowed_data), - .wrapped_result = try Value.Tag.aggregate.create(sema.arena, result_data), + .overflow_bit = (try mod.intern(.{ .aggregate = .{ + .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(), + .storage = .{ .elems = overflowed_data }, + } })).toValue(), + .wrapped_result = (try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .elems = result_data }, + } })).toValue(), }; } return sema.intSubWithOverflowScalar(lhs, rhs, ty); @@ -33476,22 +35986,22 @@ fn intSubWithOverflowScalar( rhs: Value, ty: Type, ) !Value.OverflowArithmeticResult { - const target = sema.mod.getTarget(); - const info = ty.intInfo(target); + const mod = sema.mod; + const info = ty.intInfo(mod); var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema); - const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema); + const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); + const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); const limbs = try sema.arena.alloc( std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(info.bits), ); var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); - const wrapped_result = try Value.fromBigInt(sema.arena, result_bigint.toConst()); + const wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()); return Value.OverflowArithmeticResult{ - .overflow_bit = Value.boolToInt(overflowed), + .overflow_bit = try mod.intValue(Type.u1, @boolToInt(overflowed)), .wrapped_result = wrapped_result, }; } @@ -33504,15 +36014,19 @@ fn floatToInt( float_ty: Type, int_ty: Type, ) CompileError!Value { - if (float_ty.zigTypeTag() == .Vector) { - const elem_ty = float_ty.childType(); - const result_data = try sema.arena.alloc(Value, float_ty.vectorLen()); + const mod = sema.mod; + if (float_ty.zigTypeTag(mod) == .Vector) { + const elem_ty = float_ty.scalarType(mod); + const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(mod)); + const scalar_ty = int_ty.scalarType(mod); for (result_data, 0..) |*scalar, i| { - var buf: Value.ElemValueBuffer = undefined; - const elem_val = val.elemValueBuffer(sema.mod, i, &buf); - scalar.* = try sema.floatToIntScalar(block, src, elem_val, elem_ty, int_ty.scalarType()); + const elem_val = try val.elemValue(sema.mod, i); + scalar.* = try (try sema.floatToIntScalar(block, src, elem_val, elem_ty, int_ty.scalarType(mod))).intern(scalar_ty, mod); } - return Value.Tag.aggregate.create(sema.arena, result_data); + return (try mod.intern(.{ .aggregate = .{ + .ty = int_ty.toIntern(), + .storage = .{ .elems = result_data }, + } })).toValue(); } return sema.floatToIntScalar(block, src, val, float_ty, int_ty); } @@ -33550,9 +36064,9 @@ fn floatToIntScalar( float_ty: Type, int_ty: Type, ) CompileError!Value { - const Limb = std.math.big.Limb; + const mod = sema.mod; - const float = val.toFloat(f128); + const float = val.toFloat(f128, mod); if (std.math.isNan(float)) { return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{ int_ty.fmt(sema.mod), @@ -33567,18 +36081,14 @@ fn floatToIntScalar( var big_int = try float128IntPartToBigInt(sema.arena, float); defer big_int.deinit(); - const result_limbs = try sema.arena.dupe(Limb, big_int.toConst().limbs); - const result = if (!big_int.isPositive()) - try Value.Tag.int_big_negative.create(sema.arena, result_limbs) - else - try Value.Tag.int_big_positive.create(sema.arena, result_limbs); + const cti_result = try mod.intValue_big(Type.comptime_int, big_int.toConst()); - if (!(try sema.intFitsInType(result, int_ty, null))) { + if (!(try sema.intFitsInType(cti_result, int_ty, null))) { return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{ val.fmtValue(float_ty, sema.mod), int_ty.fmt(sema.mod), }); } - return result; + return mod.getCoerced(cti_result, int_ty); } /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. @@ -33591,208 +36101,91 @@ fn intFitsInType( ty: Type, vector_index: ?*usize, ) CompileError!bool { - const target = sema.mod.getTarget(); - switch (val.tag()) { - .zero, - .undef, - .bool_false, - => return true, - - .one, - .bool_true, - => switch (ty.zigTypeTag()) { - .Int => { - const info = ty.intInfo(target); + const mod = sema.mod; + if (ty.toIntern() == .comptime_int_type) return true; + const info = ty.intInfo(mod); + switch (val.toIntern()) { + .zero_usize, .zero_u8 => return true, + else => switch (mod.intern_pool.indexToKey(val.toIntern())) { + .undef => return true, + .variable, .extern_func, .func, .ptr => { + const target = mod.getTarget(); + const ptr_bits = target.ptrBitWidth(); return switch (info.signedness) { - .signed => info.bits >= 2, - .unsigned => info.bits >= 1, + .signed => info.bits > ptr_bits, + .unsigned => info.bits >= ptr_bits, }; }, - .ComptimeInt => return true, - else => unreachable, - }, - - .lazy_align => switch (ty.zigTypeTag()) { - .Int => { - const info = ty.intInfo(target); - const max_needed_bits = @as(u16, 16) + @boolToInt(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 sema.typeAbiAlignment(val.castTag(.lazy_align).?.data); - if (x == 0) return true; - const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); - return info.bits >= actual_needed_bits; - }, - .ComptimeInt => return true, - else => unreachable, - }, - .lazy_size => switch (ty.zigTypeTag()) { - .Int => { - const info = ty.intInfo(target); - const max_needed_bits = @as(u16, 64) + @boolToInt(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 sema.typeAbiSize(val.castTag(.lazy_size).?.data); - if (x == 0) return true; - const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); - return info.bits >= actual_needed_bits; - }, - .ComptimeInt => return true, - else => unreachable, - }, - - .int_u64 => switch (ty.zigTypeTag()) { - .Int => { - const x = val.castTag(.int_u64).?.data; - if (x == 0) return true; - const info = ty.intInfo(target); - const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); - return info.bits >= needed_bits; - }, - .ComptimeInt => return true, - else => unreachable, - }, - .int_i64 => switch (ty.zigTypeTag()) { - .Int => { - const x = val.castTag(.int_i64).?.data; - if (x == 0) return true; - const info = ty.intInfo(target); - if (info.signedness == .unsigned and x < 0) - return false; - var buffer: Value.BigIntSpace = undefined; - return (try val.toBigIntAdvanced(&buffer, target, sema)).fitsInTwosComp(info.signedness, info.bits); - }, - .ComptimeInt => return true, - else => unreachable, - }, - .int_big_positive => switch (ty.zigTypeTag()) { - .Int => { - const info = ty.intInfo(target); - return val.castTag(.int_big_positive).?.asBigInt().fitsInTwosComp(info.signedness, info.bits); - }, - .ComptimeInt => return true, - else => unreachable, - }, - .int_big_negative => switch (ty.zigTypeTag()) { - .Int => { - const info = ty.intInfo(target); - return val.castTag(.int_big_negative).?.asBigInt().fitsInTwosComp(info.signedness, info.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) + @boolToInt(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 sema.typeAbiAlignment(lazy_ty.toType()); + if (x == 0) return true; + const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); + return info.bits >= actual_needed_bits; + }, + .lazy_size => |lazy_ty| { + const max_needed_bits = @as(u16, 64) + @boolToInt(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 sema.typeAbiSize(lazy_ty.toType()); + if (x == 0) return true; + const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); + return info.bits >= actual_needed_bits; + }, }, - .ComptimeInt => return true, - else => unreachable, - }, - - .the_only_possible_value => { - assert(ty.intInfo(target).bits == 0); - return true; - }, - - .decl_ref_mut, - .extern_fn, - .decl_ref, - .function, - .variable, - => switch (ty.zigTypeTag()) { - .Int => { - const info = ty.intInfo(target); - const ptr_bits = target.cpu.arch.ptrBitWidth(); - return switch (info.signedness) { - .signed => info.bits > ptr_bits, - .unsigned => info.bits >= ptr_bits, + .aggregate => |aggregate| { + assert(ty.zigTypeTag(mod) == .Vector); + return switch (aggregate.storage) { + .bytes => |bytes| for (bytes, 0..) |byte, i| { + if (byte == 0) continue; + const actual_needed_bits = std.math.log2(byte) + 1 + @boolToInt(info.signedness == .signed); + if (info.bits >= actual_needed_bits) continue; + if (vector_index) |vi| vi.* = i; + break false; + } else true, + .elems, .repeated_elem => for (switch (aggregate.storage) { + .bytes => unreachable, + .elems => |elems| elems, + .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem), + }, 0..) |elem, i| { + if (try sema.intFitsInType(elem.toValue(), ty.scalarType(mod), null)) continue; + if (vector_index) |vi| vi.* = i; + break false; + } else true, }; }, - .ComptimeInt => return true, else => unreachable, }, - - .aggregate => { - assert(ty.zigTypeTag() == .Vector); - for (val.castTag(.aggregate).?.data, 0..) |elem, i| { - if (!(try sema.intFitsInType(elem, ty.scalarType(), null))) { - if (vector_index) |some| some.* = i; - return false; - } - } - return true; - }, - - else => unreachable, } } -fn intInRange( - sema: *Sema, - tag_ty: Type, - int_val: Value, - end: usize, -) !bool { +fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool { + const mod = sema.mod; if (!(try int_val.compareAllWithZeroAdvanced(.gte, sema))) return false; - var end_payload: Value.Payload.U64 = .{ - .base = .{ .tag = .int_u64 }, - .data = end, - }; - const end_val = Value.initPayload(&end_payload.base); + const end_val = try mod.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. -fn enumHasInt( - sema: *Sema, - ty: Type, - int: Value, -) CompileError!bool { - switch (ty.tag()) { - .enum_nonexhaustive => unreachable, - .enum_full => { - const enum_full = ty.castTag(.enum_full).?.data; - const tag_ty = enum_full.tag_ty; - if (enum_full.values.count() == 0) { - return sema.intInRange(tag_ty, int, enum_full.fields.count()); - } else { - return enum_full.values.containsContext(int, .{ - .ty = tag_ty, - .mod = sema.mod, - }); - } - }, - .enum_numbered => { - const enum_obj = ty.castTag(.enum_numbered).?.data; - const tag_ty = enum_obj.tag_ty; - if (enum_obj.values.count() == 0) { - return sema.intInRange(tag_ty, int, enum_obj.fields.count()); - } else { - return enum_obj.values.containsContext(int, .{ - .ty = tag_ty, - .mod = sema.mod, - }); - } - }, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - const fields_len = enum_simple.fields.count(); - const bits = std.math.log2_int_ceil(usize, fields_len); - var buffer: Type.Payload.Bits = .{ - .base = .{ .tag = .int_unsigned }, - .data = bits, - }; - const tag_ty = Type.initPayload(&buffer.base); - return sema.intInRange(tag_ty, int, fields_len); - }, - .atomic_order, - .atomic_rmw_op, - .calling_convention, - .address_space, - .float_mode, - .reduce_op, - .modifier, - .prefetch_options, - .export_options, - .extern_options, - => unreachable, +fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { + const mod = sema.mod; + const enum_type = mod.intern_pool.indexToKey(ty.toIntern()).enum_type; + assert(enum_type.tag_mode != .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, enum_type.tag_ty.toType(), null))) return false; + const int_coerced = try mod.getCoerced(int, enum_type.tag_ty.toType()); - else => unreachable, - } + return enum_type.tagValueIndex(&mod.intern_pool, int_coerced.toIntern()) != null; } fn intAddWithOverflow( @@ -33801,21 +36194,28 @@ fn intAddWithOverflow( rhs: Value, ty: Type, ) !Value.OverflowArithmeticResult { - if (ty.zigTypeTag() == .Vector) { - const overflowed_data = try sema.arena.alloc(Value, ty.vectorLen()); - const result_data = try sema.arena.alloc(Value, ty.vectorLen()); - for (result_data, 0..) |*scalar, i| { - var lhs_buf: Value.ElemValueBuffer = undefined; - var rhs_buf: Value.ElemValueBuffer = undefined; - const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf); - const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf); - const of_math_result = try sema.intAddWithOverflowScalar(lhs_elem, rhs_elem, ty.scalarType()); - overflowed_data[i] = of_math_result.overflow_bit; - scalar.* = of_math_result.wrapped_result; + const mod = sema.mod; + if (ty.zigTypeTag(mod) == .Vector) { + const vec_len = ty.vectorLen(mod); + const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len); + const result_data = try sema.arena.alloc(InternPool.Index, vec_len); + const scalar_ty = ty.scalarType(mod); + for (overflowed_data, result_data, 0..) |*of, *scalar, i| { + const lhs_elem = try lhs.elemValue(sema.mod, i); + const rhs_elem = try rhs.elemValue(sema.mod, i); + const of_math_result = try sema.intAddWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty); + of.* = try of_math_result.overflow_bit.intern(Type.u1, mod); + scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod); } return Value.OverflowArithmeticResult{ - .overflow_bit = try Value.Tag.aggregate.create(sema.arena, overflowed_data), - .wrapped_result = try Value.Tag.aggregate.create(sema.arena, result_data), + .overflow_bit = (try mod.intern(.{ .aggregate = .{ + .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(), + .storage = .{ .elems = overflowed_data }, + } })).toValue(), + .wrapped_result = (try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .elems = result_data }, + } })).toValue(), }; } return sema.intAddWithOverflowScalar(lhs, rhs, ty); @@ -33827,22 +36227,22 @@ fn intAddWithOverflowScalar( rhs: Value, ty: Type, ) !Value.OverflowArithmeticResult { - const target = sema.mod.getTarget(); - const info = ty.intInfo(target); + const mod = sema.mod; + const info = ty.intInfo(mod); var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema); - const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema); + const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); + const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); const limbs = try sema.arena.alloc( std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(info.bits), ); var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); - const result = try Value.fromBigInt(sema.arena, result_bigint.toConst()); + const result = try mod.intValue_big(ty, result_bigint.toConst()); return Value.OverflowArithmeticResult{ - .overflow_bit = Value.boolToInt(overflowed), + .overflow_bit = try mod.intValue(Type.u1, @boolToInt(overflowed)), .wrapped_result = result, }; } @@ -33858,14 +36258,13 @@ fn compareAll( rhs: Value, ty: Type, ) CompileError!bool { - if (ty.zigTypeTag() == .Vector) { + const mod = sema.mod; + if (ty.zigTypeTag(mod) == .Vector) { var i: usize = 0; - while (i < ty.vectorLen()) : (i += 1) { - var lhs_buf: Value.ElemValueBuffer = undefined; - var rhs_buf: Value.ElemValueBuffer = undefined; - const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf); - const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf); - if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType()))) { + while (i < ty.vectorLen(mod)) : (i += 1) { + const lhs_elem = try lhs.elemValue(sema.mod, i); + const rhs_elem = try rhs.elemValue(sema.mod, i); + if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)))) { return false; } } @@ -33882,10 +36281,13 @@ fn compareScalar( rhs: Value, ty: Type, ) CompileError!bool { + const mod = sema.mod; + const coerced_lhs = try mod.getCoerced(lhs, ty); + const coerced_rhs = try mod.getCoerced(rhs, ty); switch (op) { - .eq => return sema.valuesEqual(lhs, rhs, ty), - .neq => return !(try sema.valuesEqual(lhs, rhs, ty)), - else => return Value.compareHeteroAdvanced(lhs, op, rhs, sema.mod.getTarget(), sema), + .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty), + .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)), + else => return Value.compareHeteroAdvanced(coerced_lhs, op, coerced_rhs, mod, sema), } } @@ -33906,17 +36308,19 @@ fn compareVector( rhs: Value, ty: Type, ) !Value { - assert(ty.zigTypeTag() == .Vector); - const result_data = try sema.arena.alloc(Value, ty.vectorLen()); + const mod = sema.mod; + assert(ty.zigTypeTag(mod) == .Vector); + const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); for (result_data, 0..) |*scalar, i| { - var lhs_buf: Value.ElemValueBuffer = undefined; - var rhs_buf: Value.ElemValueBuffer = undefined; - const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf); - const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf); - const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType()); - scalar.* = Value.makeBool(res_bool); + const lhs_elem = try lhs.elemValue(sema.mod, i); + const rhs_elem = try rhs.elemValue(sema.mod, i); + const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)); + scalar.* = try Value.makeBool(res_bool).intern(Type.bool, mod); } - return Value.Tag.aggregate.create(sema.arena, result_data); + return (try mod.intern(.{ .aggregate = .{ + .ty = (try mod.vectorType(.{ .len = ty.vectorLen(mod), .child = .bool_type })).toIntern(), + .storage = .{ .elems = result_data }, + } })).toValue(); } /// Returns the type of a pointer to an element. @@ -33927,11 +36331,11 @@ fn compareVector( /// Handles const-ness and address spaces in particular. /// This code is duplicated in `analyzePtrArithmetic`. fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type { - const ptr_info = ptr_ty.ptrInfo().data; - const elem_ty = ptr_ty.elemType2(); + const mod = sema.mod; + const ptr_info = ptr_ty.ptrInfo(mod); + const elem_ty = ptr_ty.elemType2(mod); const allow_zero = ptr_info.@"allowzero" and (offset orelse 0) == 0; - const target = sema.mod.getTarget(); - const parent_ty = ptr_ty.childType(); + const parent_ty = ptr_ty.childType(mod); const VI = Type.Payload.Pointer.Data.VectorIndex; @@ -33939,15 +36343,15 @@ fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type { host_size: u16 = 0, alignment: u32 = 0, vector_index: VI = .none, - } = if (parent_ty.tag() == .vector and ptr_info.size == .One) blk: { - const elem_bits = elem_ty.bitSize(target); + } = if (parent_ty.isVector(mod) and ptr_info.size == .One) blk: { + const elem_bits = elem_ty.bitSize(mod); if (elem_bits == 0) break :blk .{}; const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits); if (!is_packed) break :blk .{}; break :blk .{ - .host_size = @intCast(u16, parent_ty.arrayLen()), - .alignment = @intCast(u16, parent_ty.abiAlignment(target)), + .host_size = @intCast(u16, parent_ty.arrayLen(mod)), + .alignment = @intCast(u16, parent_ty.abiAlignment(mod)), .vector_index = if (offset) |some| @intToEnum(VI, some) else .runtime, }; } else .{}; @@ -33981,3 +36385,42 @@ fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type { .vector_index = vector_info.vector_index, }); } + +/// Merge lhs with rhs. +/// Asserts that lhs and rhs are both error sets and are resolved. +fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type { + const mod = sema.mod; + const arena = sema.arena; + const lhs_names = lhs.errorSetNames(mod); + const rhs_names = rhs.errorSetNames(mod); + var names: Module.Fn.InferredErrorSet.NameMap = .{}; + try names.ensureUnusedCapacity(arena, lhs_names.len); + + for (lhs_names) |name| { + names.putAssumeCapacityNoClobber(name, {}); + } + for (rhs_names) |name| { + try names.put(arena, name, {}); + } + + return mod.errorSetFromUnsortedNames(names.keys()); +} + +/// Avoids crashing the compiler when asking if inferred allocations are noreturn. +fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool { + if (ref == .unreachable_value) return true; + if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) { + .inferred_alloc, .inferred_alloc_comptime => return false, + else => {}, + }; + return sema.typeOf(ref).isNoReturn(sema.mod); +} + +/// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type. +fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool { + if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) { + .inferred_alloc, .inferred_alloc_comptime => return false, + else => {}, + }; + return sema.typeOf(ref).zigTypeTag(sema.mod) == tag; +} |
