zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob a2ed8116 (1641998B) - Raw


      1 //! Semantic analysis of ZIR instructions.
      2 //! Shared to every Block. Stored on the stack.
      3 //! State used for compiling a ZIR into AIR.
      4 //! Transforms untyped ZIR instructions into semantically-analyzed AIR instructions.
      5 //! Does type checking, comptime control flow, and safety-check generation.
      6 //! This is the the heart of the Zig compiler.
      7 
      8 mod: *Module,
      9 /// Alias to `mod.gpa`.
     10 gpa: Allocator,
     11 /// Points to the temporary arena allocator of the Sema.
     12 /// This arena will be cleared when the sema is destroyed.
     13 arena: Allocator,
     14 code: Zir,
     15 air_instructions: std.MultiArrayList(Air.Inst) = .{},
     16 air_extra: std.ArrayListUnmanaged(u32) = .{},
     17 /// Maps ZIR to AIR.
     18 inst_map: InstMap = .{},
     19 /// When analyzing an inline function call, owner_decl is the Decl of the caller
     20 /// and `src_decl` of `Block` is the `Decl` of the callee.
     21 /// This `Decl` owns the arena memory of this `Sema`.
     22 owner_decl: *Decl,
     23 owner_decl_index: InternPool.DeclIndex,
     24 /// For an inline or comptime function call, this will be the root parent function
     25 /// which contains the callsite. Corresponds to `owner_decl`.
     26 /// This could be `none`, a `func_decl`, or a `func_instance`.
     27 owner_func_index: InternPool.Index,
     28 /// The function this ZIR code is the body of, according to the source code.
     29 /// This starts out the same as `owner_func_index` and then diverges in the case of
     30 /// an inline or comptime function call.
     31 /// This could be `none`, a `func_decl`, or a `func_instance`.
     32 func_index: InternPool.Index,
     33 /// Whether the type of func_index has a calling convention of `.Naked`.
     34 func_is_naked: bool,
     35 /// Used to restore the error return trace when returning a non-error from a function.
     36 error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none,
     37 /// When semantic analysis needs to know the return type of the function whose body
     38 /// is being analyzed, this `Type` should be used instead of going through `func`.
     39 /// This will correctly handle the case of a comptime/inline function call of a
     40 /// generic function which uses a type expression for the return type.
     41 /// The type will be `void` in the case that `func` is `null`.
     42 fn_ret_ty: Type,
     43 /// In case of the return type being an error union with an inferred error
     44 /// set, this is the inferred error set. `null` otherwise. Allocated with
     45 /// `Sema.arena`.
     46 fn_ret_ty_ies: ?*InferredErrorSet,
     47 branch_quota: u32 = default_branch_quota,
     48 branch_count: u32 = 0,
     49 /// Populated when returning `error.ComptimeBreak`. Used to communicate the
     50 /// break instruction up the stack to find the corresponding Block.
     51 comptime_break_inst: Zir.Inst.Index = undefined,
     52 /// This field is updated when a new source location becomes active, so that
     53 /// instructions which do not have explicitly mapped source locations still have
     54 /// access to the source location set by the previous instruction which did
     55 /// contain a mapped source location.
     56 src: LazySrcLoc = .{ .token_offset = 0 },
     57 decl_val_table: std.AutoHashMapUnmanaged(InternPool.DeclIndex, Air.Inst.Ref) = .{},
     58 /// When doing a generic function instantiation, this array collects a value
     59 /// for each parameter of the generic owner. `none` for non-comptime parameters.
     60 /// This is a separate array from `block.params` so that it can be passed
     61 /// directly to `comptime_args` when calling `InternPool.getFuncInstance`.
     62 /// This memory is allocated by a parent `Sema` in the temporary arena, and is
     63 /// used only to add a `func_instance` into the `InternPool`.
     64 comptime_args: []InternPool.Index = &.{},
     65 /// Used to communicate from a generic function instantiation to the logic that
     66 /// creates a generic function instantiation value in `funcCommon`.
     67 generic_owner: InternPool.Index = .none,
     68 /// When `generic_owner` is not none, this contains the generic function
     69 /// instantiation callsite so that compile errors on the parameter types of the
     70 /// instantiation can point back to the instantiation site in addition to the
     71 /// declaration site.
     72 generic_call_src: LazySrcLoc = .unneeded,
     73 /// Corresponds to `generic_call_src`.
     74 generic_call_decl: InternPool.OptionalDeclIndex = .none,
     75 /// The key is types that must be fully resolved prior to machine code
     76 /// generation pass. Types are added to this set when resolving them
     77 /// immediately could cause a dependency loop, but they do need to be resolved
     78 /// before machine code generation passes process the AIR.
     79 /// It would work fine if this were an array list instead of an array hash map.
     80 /// I chose array hash map with the intention to save time by omitting
     81 /// duplicates.
     82 types_to_resolve: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},
     83 /// These are lazily created runtime blocks from block_inline instructions.
     84 /// They are created when an break_inline passes through a runtime condition, because
     85 /// Sema must convert comptime control flow to runtime control flow, which means
     86 /// breaking from a block.
     87 post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{},
     88 /// Populated with the last compile error created.
     89 err: ?*Module.ErrorMsg = null,
     90 /// Set to true when analyzing a func type instruction so that nested generic
     91 /// function types will emit generic poison instead of a partial type.
     92 no_partial_func_ty: bool = false,
     93 
     94 /// The temporary arena is used for the memory of the `InferredAlloc` values
     95 /// here so the values can be dropped without any cleanup.
     96 unresolved_inferred_allocs: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, InferredAlloc) = .{},
     97 
     98 /// Indices of comptime-mutable decls created by this Sema. These decls' values
     99 /// should be interned after analysis completes, as they may refer to memory in
    100 /// the Sema arena.
    101 /// TODO: this is a workaround for memory bugs triggered by the removal of
    102 /// Decl.value_arena. A better solution needs to be found. Probably this will
    103 /// involve transitioning comptime-mutable memory away from using Decls at all.
    104 comptime_mutable_decls: *std.ArrayList(InternPool.DeclIndex),
    105 
    106 /// This is populated when `@setAlignStack` occurs so that if there is a duplicate
    107 /// one encountered, the conflicting source location can be shown.
    108 prev_stack_alignment_src: ?LazySrcLoc = null,
    109 
    110 /// While analyzing a type which has a special InternPool index, this is set to the index at which
    111 /// the struct/enum/union type created should be placed. Otherwise, it is `.none`.
    112 builtin_type_target_index: InternPool.Index = .none,
    113 
    114 /// Links every pointer derived from a base `alloc` back to that `alloc`. Used
    115 /// to detect comptime-known `const`s.
    116 /// TODO: ZIR liveness analysis would allow us to remove elements from this map.
    117 base_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, Air.Inst.Index) = .{},
    118 
    119 /// Runtime `alloc`s are placed in this map to track all comptime-known writes
    120 /// before the corresponding `make_ptr_const` instruction.
    121 /// If any store to the alloc depends on a runtime condition or stores a runtime
    122 /// value, the corresponding element in this map is erased, to indicate that the
    123 /// alloc is not comptime-known.
    124 /// If the alloc remains in this map when `make_ptr_const` is reached, its value
    125 /// is comptime-known, and all stores to the pointer must be applied at comptime
    126 /// to determine the comptime value.
    127 /// Backed by gpa.
    128 maybe_comptime_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, MaybeComptimeAlloc) = .{},
    129 
    130 const MaybeComptimeAlloc = struct {
    131     /// The runtime index of the `alloc` instruction.
    132     runtime_index: Value.RuntimeIndex,
    133     /// Backed by sema.arena. Tracks all comptime-known stores to this `alloc`. Due to
    134     /// RLS, a single comptime-known allocation may have arbitrarily many stores.
    135     /// This may also contain `set_union_tag` instructions.
    136     stores: std.ArrayListUnmanaged(Air.Inst.Index) = .{},
    137     /// Backed by sema.arena. Contains instructions such as `optional_payload_ptr_set`
    138     /// which have side effects so will not be elided by Liveness: we must rewrite these
    139     /// instructions to be nops instead of relying on Liveness.
    140     non_elideable_pointers: std.ArrayListUnmanaged(Air.Inst.Index) = .{},
    141 };
    142 
    143 const std = @import("std");
    144 const math = std.math;
    145 const mem = std.mem;
    146 const Allocator = mem.Allocator;
    147 const assert = std.debug.assert;
    148 const log = std.log.scoped(.sema);
    149 
    150 const Sema = @This();
    151 const Value = @import("value.zig").Value;
    152 const Type = @import("type.zig").Type;
    153 const TypedValue = @import("TypedValue.zig");
    154 const Air = @import("Air.zig");
    155 const Zir = @import("Zir.zig");
    156 const Module = @import("Module.zig");
    157 const trace = @import("tracy.zig").trace;
    158 const Namespace = Module.Namespace;
    159 const CompileError = Module.CompileError;
    160 const SemaError = Module.SemaError;
    161 const Decl = Module.Decl;
    162 const CaptureScope = Module.CaptureScope;
    163 const LazySrcLoc = Module.LazySrcLoc;
    164 const RangeSet = @import("RangeSet.zig");
    165 const target_util = @import("target.zig");
    166 const Package = @import("Package.zig");
    167 const crash_report = @import("crash_report.zig");
    168 const build_options = @import("build_options");
    169 const Compilation = @import("Compilation.zig");
    170 const InternPool = @import("InternPool.zig");
    171 const Alignment = InternPool.Alignment;
    172 
    173 pub const default_branch_quota = 1000;
    174 pub const default_reference_trace_len = 2;
    175 
    176 pub const InferredErrorSet = struct {
    177     /// The function body from which this error set originates.
    178     /// This is `none` in the case of a comptime/inline function call, corresponding to
    179     /// `InternPool.Index.adhoc_inferred_error_set_type`.
    180     /// The function's resolved error set is not set until analysis of the
    181     /// function body completes.
    182     func: InternPool.Index,
    183     /// All currently known errors that this error set contains. This includes
    184     /// direct additions via `return error.Foo;`, and possibly also errors that
    185     /// are returned from any dependent functions.
    186     errors: NameMap = .{},
    187     /// Other inferred error sets which this inferred error set should include.
    188     inferred_error_sets: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},
    189     /// The regular error set created by resolving this inferred error set.
    190     resolved: InternPool.Index = .none,
    191 
    192     pub const NameMap = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void);
    193 
    194     pub fn addErrorSet(
    195         self: *InferredErrorSet,
    196         err_set_ty: Type,
    197         ip: *InternPool,
    198         arena: Allocator,
    199     ) !void {
    200         switch (err_set_ty.toIntern()) {
    201             .anyerror_type => self.resolved = .anyerror_type,
    202             .adhoc_inferred_error_set_type => {}, // Adding an inferred error set to itself.
    203 
    204             else => switch (ip.indexToKey(err_set_ty.toIntern())) {
    205                 .error_set_type => |error_set_type| {
    206                     for (error_set_type.names.get(ip)) |name| {
    207                         try self.errors.put(arena, name, {});
    208                     }
    209                 },
    210                 .inferred_error_set_type => {
    211                     try self.inferred_error_sets.put(arena, err_set_ty.toIntern(), {});
    212                 },
    213                 else => unreachable,
    214             },
    215         }
    216     }
    217 };
    218 
    219 /// Stores the mapping from `Zir.Inst.Index -> Air.Inst.Ref`, which is used by sema to resolve
    220 /// instructions during analysis.
    221 /// Instead of a hash table approach, InstMap is simply a slice that is indexed into using the
    222 /// zir instruction index and a start offset. An index is not pressent in the map if the value
    223 /// at the index is `Air.Inst.Ref.none`.
    224 /// `ensureSpaceForInstructions` can be called to force InstMap to have a mapped range that
    225 /// includes all instructions in a slice. After calling this function, `putAssumeCapacity*` can
    226 /// be called safely for any of the instructions passed in.
    227 pub const InstMap = struct {
    228     items: []Air.Inst.Ref = &[_]Air.Inst.Ref{},
    229     start: Zir.Inst.Index = @enumFromInt(0),
    230 
    231     pub fn deinit(map: InstMap, allocator: mem.Allocator) void {
    232         allocator.free(map.items);
    233     }
    234 
    235     pub fn get(map: InstMap, key: Zir.Inst.Index) ?Air.Inst.Ref {
    236         if (!map.contains(key)) return null;
    237         return map.items[@intFromEnum(key) - @intFromEnum(map.start)];
    238     }
    239 
    240     pub fn putAssumeCapacity(
    241         map: *InstMap,
    242         key: Zir.Inst.Index,
    243         ref: Air.Inst.Ref,
    244     ) void {
    245         map.items[@intFromEnum(key) - @intFromEnum(map.start)] = ref;
    246     }
    247 
    248     pub fn putAssumeCapacityNoClobber(
    249         map: *InstMap,
    250         key: Zir.Inst.Index,
    251         ref: Air.Inst.Ref,
    252     ) void {
    253         assert(!map.contains(key));
    254         map.putAssumeCapacity(key, ref);
    255     }
    256 
    257     pub const GetOrPutResult = struct {
    258         value_ptr: *Air.Inst.Ref,
    259         found_existing: bool,
    260     };
    261 
    262     pub fn getOrPutAssumeCapacity(
    263         map: *InstMap,
    264         key: Zir.Inst.Index,
    265     ) GetOrPutResult {
    266         const index = @intFromEnum(key) - @intFromEnum(map.start);
    267         return GetOrPutResult{
    268             .value_ptr = &map.items[index],
    269             .found_existing = map.items[index] != .none,
    270         };
    271     }
    272 
    273     pub fn remove(map: InstMap, key: Zir.Inst.Index) bool {
    274         if (!map.contains(key)) return false;
    275         map.items[@intFromEnum(key) - @intFromEnum(map.start)] = .none;
    276         return true;
    277     }
    278 
    279     pub fn contains(map: InstMap, key: Zir.Inst.Index) bool {
    280         return map.items[@intFromEnum(key) - @intFromEnum(map.start)] != .none;
    281     }
    282 
    283     pub fn ensureSpaceForInstructions(
    284         map: *InstMap,
    285         allocator: mem.Allocator,
    286         insts: []const Zir.Inst.Index,
    287     ) !void {
    288         const start, const end = mem.minMax(u32, @ptrCast(insts));
    289         const map_start = @intFromEnum(map.start);
    290         if (map_start <= start and end < map.items.len + map_start)
    291             return;
    292 
    293         const old_start = if (map.items.len == 0) start else map_start;
    294         var better_capacity = map.items.len;
    295         var better_start = old_start;
    296         while (true) {
    297             const extra_capacity = better_capacity / 2 + 16;
    298             better_capacity += extra_capacity;
    299             better_start -|= @intCast(extra_capacity / 2);
    300             if (better_start <= start and end < better_capacity + better_start)
    301                 break;
    302         }
    303 
    304         const start_diff = old_start - better_start;
    305         const new_items = try allocator.alloc(Air.Inst.Ref, better_capacity);
    306         @memset(new_items[0..start_diff], .none);
    307         @memcpy(new_items[start_diff..][0..map.items.len], map.items);
    308         @memset(new_items[start_diff + map.items.len ..], .none);
    309 
    310         allocator.free(map.items);
    311         map.items = new_items;
    312         map.start = @enumFromInt(better_start);
    313     }
    314 };
    315 
    316 /// This is the context needed to semantically analyze ZIR instructions and
    317 /// produce AIR instructions.
    318 /// This is a temporary structure stored on the stack; references to it are valid only
    319 /// during semantic analysis of the block.
    320 pub const Block = struct {
    321     parent: ?*Block,
    322     /// Shared among all child blocks.
    323     sema: *Sema,
    324     /// The namespace to use for lookups from this source block
    325     /// When analyzing fields, this is different from src_decl.src_namespace.
    326     namespace: InternPool.NamespaceIndex,
    327     /// The AIR instructions generated for this block.
    328     instructions: std.ArrayListUnmanaged(Air.Inst.Index),
    329     // `param` instructions are collected here to be used by the `func` instruction.
    330     /// When doing a generic function instantiation, this array collects a type
    331     /// for each *runtime-known* parameter. This array corresponds to the instance
    332     /// function type, while `Sema.comptime_args` corresponds to the generic owner
    333     /// function type.
    334     /// This memory is allocated by a parent `Sema` in the temporary arena, and is
    335     /// used to add a `func_instance` into the `InternPool`.
    336     params: std.MultiArrayList(Param) = .{},
    337 
    338     wip_capture_scope: CaptureScope.Index,
    339 
    340     label: ?*Label = null,
    341     inlining: ?*Inlining,
    342     /// If runtime_index is not 0 then one of these is guaranteed to be non null.
    343     runtime_cond: ?LazySrcLoc = null,
    344     runtime_loop: ?LazySrcLoc = null,
    345     /// This Decl is the Decl according to the Zig source code corresponding to this Block.
    346     /// This can vary during inline or comptime function calls. See `Sema.owner_decl`
    347     /// for the one that will be the same for all Block instances.
    348     src_decl: InternPool.DeclIndex,
    349     /// Non zero if a non-inline loop or a runtime conditional have been encountered.
    350     /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index.
    351     runtime_index: Value.RuntimeIndex = .zero,
    352     inline_block: Zir.Inst.OptionalIndex = .none,
    353 
    354     comptime_reason: ?*const ComptimeReason = null,
    355     // TODO is_comptime and comptime_reason should probably be merged together.
    356     is_comptime: bool,
    357     is_typeof: bool = false,
    358 
    359     /// Keep track of the active error return trace index around blocks so that we can correctly
    360     /// pop the error trace upon block exit.
    361     error_return_trace_index: Air.Inst.Ref = .none,
    362 
    363     /// when null, it is determined by build mode, changed by @setRuntimeSafety
    364     want_safety: ?bool = null,
    365 
    366     /// What mode to generate float operations in, set by @setFloatMode
    367     float_mode: std.builtin.FloatMode = .Strict,
    368 
    369     c_import_buf: ?*std.ArrayList(u8) = null,
    370 
    371     const ComptimeReason = union(enum) {
    372         c_import: struct {
    373             block: *Block,
    374             src: LazySrcLoc,
    375         },
    376         comptime_ret_ty: struct {
    377             block: *Block,
    378             func: Air.Inst.Ref,
    379             func_src: LazySrcLoc,
    380             return_ty: Type,
    381         },
    382 
    383         fn explain(cr: ComptimeReason, sema: *Sema, msg: ?*Module.ErrorMsg) !void {
    384             const parent = msg orelse return;
    385             const mod = sema.mod;
    386             const prefix = "expression is evaluated at comptime because ";
    387             switch (cr) {
    388                 .c_import => |ci| {
    389                     try sema.errNote(ci.block, ci.src, parent, prefix ++ "it is inside a @cImport", .{});
    390                 },
    391                 .comptime_ret_ty => |rt| {
    392                     const src_loc = if (try sema.funcDeclSrc(rt.func)) |fn_decl| blk: {
    393                         var src_loc = fn_decl.srcLoc(mod);
    394                         src_loc.lazy = .{ .node_offset_fn_type_ret_ty = 0 };
    395                         break :blk src_loc;
    396                     } else blk: {
    397                         const src_decl = mod.declPtr(rt.block.src_decl);
    398                         break :blk rt.func_src.toSrcLoc(src_decl, mod);
    399                     };
    400                     if (rt.return_ty.isGenericPoison()) {
    401                         return mod.errNoteNonLazy(src_loc, parent, prefix ++ "the generic function was instantiated with a comptime-only return type", .{});
    402                     }
    403                     try mod.errNoteNonLazy(
    404                         src_loc,
    405                         parent,
    406                         prefix ++ "the function returns a comptime-only type '{}'",
    407                         .{rt.return_ty.fmt(mod)},
    408                     );
    409                     try sema.explainWhyTypeIsComptime(parent, src_loc, rt.return_ty);
    410                 },
    411             }
    412         }
    413     };
    414 
    415     const Param = struct {
    416         /// `none` means `anytype`.
    417         ty: InternPool.Index,
    418         is_comptime: bool,
    419         name: Zir.NullTerminatedString,
    420     };
    421 
    422     /// This `Block` maps a block ZIR instruction to the corresponding
    423     /// AIR instruction for break instruction analysis.
    424     pub const Label = struct {
    425         zir_block: Zir.Inst.Index,
    426         merges: Merges,
    427     };
    428 
    429     /// This `Block` indicates that an inline function call is happening
    430     /// and return instructions should be analyzed as a break instruction
    431     /// to this AIR block instruction.
    432     /// It is shared among all the blocks in an inline or comptime called
    433     /// function.
    434     pub const Inlining = struct {
    435         call_block: *Block,
    436         call_src: LazySrcLoc,
    437         has_comptime_args: bool,
    438         func: InternPool.Index,
    439         comptime_result: Air.Inst.Ref,
    440         merges: Merges,
    441     };
    442 
    443     pub const Merges = struct {
    444         block_inst: Air.Inst.Index,
    445         /// Separate array list from break_inst_list so that it can be passed directly
    446         /// to resolvePeerTypes.
    447         results: std.ArrayListUnmanaged(Air.Inst.Ref),
    448         /// Keeps track of the break instructions so that the operand can be replaced
    449         /// if we need to add type coercion at the end of block analysis.
    450         /// Same indexes, capacity, length as `results`.
    451         br_list: std.ArrayListUnmanaged(Air.Inst.Index),
    452         /// Keeps the source location of the rhs operand of the break instruction,
    453         /// to enable more precise compile errors.
    454         /// Same indexes, capacity, length as `results`.
    455         src_locs: std.ArrayListUnmanaged(?LazySrcLoc),
    456 
    457         pub fn deinit(merges: *@This(), allocator: mem.Allocator) void {
    458             merges.results.deinit(allocator);
    459             merges.br_list.deinit(allocator);
    460             merges.src_locs.deinit(allocator);
    461         }
    462     };
    463 
    464     /// For debugging purposes.
    465     pub fn dump(block: *Block, mod: Module) void {
    466         Zir.dumpBlock(mod, block);
    467     }
    468 
    469     pub fn makeSubBlock(parent: *Block) Block {
    470         return .{
    471             .parent = parent,
    472             .sema = parent.sema,
    473             .src_decl = parent.src_decl,
    474             .namespace = parent.namespace,
    475             .instructions = .{},
    476             .wip_capture_scope = parent.wip_capture_scope,
    477             .label = null,
    478             .inlining = parent.inlining,
    479             .is_comptime = parent.is_comptime,
    480             .comptime_reason = parent.comptime_reason,
    481             .is_typeof = parent.is_typeof,
    482             .runtime_cond = parent.runtime_cond,
    483             .runtime_loop = parent.runtime_loop,
    484             .runtime_index = parent.runtime_index,
    485             .want_safety = parent.want_safety,
    486             .float_mode = parent.float_mode,
    487             .c_import_buf = parent.c_import_buf,
    488             .error_return_trace_index = parent.error_return_trace_index,
    489         };
    490     }
    491 
    492     pub fn wantSafety(block: *const Block) bool {
    493         return block.want_safety orelse switch (block.sema.mod.optimizeMode()) {
    494             .Debug => true,
    495             .ReleaseSafe => true,
    496             .ReleaseFast => false,
    497             .ReleaseSmall => false,
    498         };
    499     }
    500 
    501     pub fn getFileScope(block: *Block, mod: *Module) *Module.File {
    502         return mod.namespacePtr(block.namespace).file_scope;
    503     }
    504 
    505     fn addTy(
    506         block: *Block,
    507         tag: Air.Inst.Tag,
    508         ty: Type,
    509     ) error{OutOfMemory}!Air.Inst.Ref {
    510         return block.addInst(.{
    511             .tag = tag,
    512             .data = .{ .ty = ty },
    513         });
    514     }
    515 
    516     fn addTyOp(
    517         block: *Block,
    518         tag: Air.Inst.Tag,
    519         ty: Type,
    520         operand: Air.Inst.Ref,
    521     ) error{OutOfMemory}!Air.Inst.Ref {
    522         return block.addInst(.{
    523             .tag = tag,
    524             .data = .{ .ty_op = .{
    525                 .ty = Air.internedToRef(ty.toIntern()),
    526                 .operand = operand,
    527             } },
    528         });
    529     }
    530 
    531     fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref {
    532         return block.addInst(.{
    533             .tag = .bitcast,
    534             .data = .{ .ty_op = .{
    535                 .ty = Air.internedToRef(ty.toIntern()),
    536                 .operand = operand,
    537             } },
    538         });
    539     }
    540 
    541     fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref {
    542         return block.addInst(.{
    543             .tag = tag,
    544             .data = .{ .no_op = {} },
    545         });
    546     }
    547 
    548     fn addUnOp(
    549         block: *Block,
    550         tag: Air.Inst.Tag,
    551         operand: Air.Inst.Ref,
    552     ) error{OutOfMemory}!Air.Inst.Ref {
    553         return block.addInst(.{
    554             .tag = tag,
    555             .data = .{ .un_op = operand },
    556         });
    557     }
    558 
    559     fn addBr(
    560         block: *Block,
    561         target_block: Air.Inst.Index,
    562         operand: Air.Inst.Ref,
    563     ) error{OutOfMemory}!Air.Inst.Ref {
    564         return block.addInst(.{
    565             .tag = .br,
    566             .data = .{ .br = .{
    567                 .block_inst = target_block,
    568                 .operand = operand,
    569             } },
    570         });
    571     }
    572 
    573     fn addBinOp(
    574         block: *Block,
    575         tag: Air.Inst.Tag,
    576         lhs: Air.Inst.Ref,
    577         rhs: Air.Inst.Ref,
    578     ) error{OutOfMemory}!Air.Inst.Ref {
    579         return block.addInst(.{
    580             .tag = tag,
    581             .data = .{ .bin_op = .{
    582                 .lhs = lhs,
    583                 .rhs = rhs,
    584             } },
    585         });
    586     }
    587 
    588     fn addStructFieldPtr(
    589         block: *Block,
    590         struct_ptr: Air.Inst.Ref,
    591         field_index: u32,
    592         ptr_field_ty: Type,
    593     ) !Air.Inst.Ref {
    594         const ty = Air.internedToRef(ptr_field_ty.toIntern());
    595         const tag: Air.Inst.Tag = switch (field_index) {
    596             0 => .struct_field_ptr_index_0,
    597             1 => .struct_field_ptr_index_1,
    598             2 => .struct_field_ptr_index_2,
    599             3 => .struct_field_ptr_index_3,
    600             else => {
    601                 return block.addInst(.{
    602                     .tag = .struct_field_ptr,
    603                     .data = .{ .ty_pl = .{
    604                         .ty = ty,
    605                         .payload = try block.sema.addExtra(Air.StructField{
    606                             .struct_operand = struct_ptr,
    607                             .field_index = field_index,
    608                         }),
    609                     } },
    610                 });
    611             },
    612         };
    613         return block.addInst(.{
    614             .tag = tag,
    615             .data = .{ .ty_op = .{
    616                 .ty = ty,
    617                 .operand = struct_ptr,
    618             } },
    619         });
    620     }
    621 
    622     fn addStructFieldVal(
    623         block: *Block,
    624         struct_val: Air.Inst.Ref,
    625         field_index: u32,
    626         field_ty: Type,
    627     ) !Air.Inst.Ref {
    628         return block.addInst(.{
    629             .tag = .struct_field_val,
    630             .data = .{ .ty_pl = .{
    631                 .ty = Air.internedToRef(field_ty.toIntern()),
    632                 .payload = try block.sema.addExtra(Air.StructField{
    633                     .struct_operand = struct_val,
    634                     .field_index = field_index,
    635                 }),
    636             } },
    637         });
    638     }
    639 
    640     fn addSliceElemPtr(
    641         block: *Block,
    642         slice: Air.Inst.Ref,
    643         elem_index: Air.Inst.Ref,
    644         elem_ptr_ty: Type,
    645     ) !Air.Inst.Ref {
    646         return block.addInst(.{
    647             .tag = .slice_elem_ptr,
    648             .data = .{ .ty_pl = .{
    649                 .ty = Air.internedToRef(elem_ptr_ty.toIntern()),
    650                 .payload = try block.sema.addExtra(Air.Bin{
    651                     .lhs = slice,
    652                     .rhs = elem_index,
    653                 }),
    654             } },
    655         });
    656     }
    657 
    658     fn addPtrElemPtr(
    659         block: *Block,
    660         array_ptr: Air.Inst.Ref,
    661         elem_index: Air.Inst.Ref,
    662         elem_ptr_ty: Type,
    663     ) !Air.Inst.Ref {
    664         const ty_ref = Air.internedToRef(elem_ptr_ty.toIntern());
    665         return block.addPtrElemPtrTypeRef(array_ptr, elem_index, ty_ref);
    666     }
    667 
    668     fn addPtrElemPtrTypeRef(
    669         block: *Block,
    670         array_ptr: Air.Inst.Ref,
    671         elem_index: Air.Inst.Ref,
    672         elem_ptr_ty: Air.Inst.Ref,
    673     ) !Air.Inst.Ref {
    674         return block.addInst(.{
    675             .tag = .ptr_elem_ptr,
    676             .data = .{ .ty_pl = .{
    677                 .ty = elem_ptr_ty,
    678                 .payload = try block.sema.addExtra(Air.Bin{
    679                     .lhs = array_ptr,
    680                     .rhs = elem_index,
    681                 }),
    682             } },
    683         });
    684     }
    685 
    686     fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref {
    687         const sema = block.sema;
    688         const mod = sema.mod;
    689         return block.addInst(.{
    690             .tag = if (block.float_mode == .Optimized) .cmp_vector_optimized else .cmp_vector,
    691             .data = .{ .ty_pl = .{
    692                 .ty = Air.internedToRef((try mod.vectorType(.{
    693                     .len = sema.typeOf(lhs).vectorLen(mod),
    694                     .child = .bool_type,
    695                 })).toIntern()),
    696                 .payload = try sema.addExtra(Air.VectorCmp{
    697                     .lhs = lhs,
    698                     .rhs = rhs,
    699                     .op = Air.VectorCmp.encodeOp(cmp_op),
    700                 }),
    701             } },
    702         });
    703     }
    704 
    705     fn addAggregateInit(
    706         block: *Block,
    707         aggregate_ty: Type,
    708         elements: []const Air.Inst.Ref,
    709     ) !Air.Inst.Ref {
    710         const sema = block.sema;
    711         const ty_ref = Air.internedToRef(aggregate_ty.toIntern());
    712         try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len);
    713         const extra_index: u32 = @intCast(sema.air_extra.items.len);
    714         sema.appendRefsAssumeCapacity(elements);
    715 
    716         return block.addInst(.{
    717             .tag = .aggregate_init,
    718             .data = .{ .ty_pl = .{
    719                 .ty = ty_ref,
    720                 .payload = extra_index,
    721             } },
    722         });
    723     }
    724 
    725     fn addUnionInit(
    726         block: *Block,
    727         union_ty: Type,
    728         field_index: u32,
    729         init: Air.Inst.Ref,
    730     ) !Air.Inst.Ref {
    731         return block.addInst(.{
    732             .tag = .union_init,
    733             .data = .{ .ty_pl = .{
    734                 .ty = Air.internedToRef(union_ty.toIntern()),
    735                 .payload = try block.sema.addExtra(Air.UnionInit{
    736                     .field_index = field_index,
    737                     .init = init,
    738                 }),
    739             } },
    740         });
    741     }
    742 
    743     pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
    744         return (try block.addInstAsIndex(inst)).toRef();
    745     }
    746 
    747     pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index {
    748         const sema = block.sema;
    749         const gpa = sema.gpa;
    750 
    751         try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
    752         try block.instructions.ensureUnusedCapacity(gpa, 1);
    753 
    754         const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
    755         sema.air_instructions.appendAssumeCapacity(inst);
    756         block.instructions.appendAssumeCapacity(result_index);
    757         return result_index;
    758     }
    759 
    760     /// Insert an instruction into the block at `index`. Moves all following
    761     /// instructions forward in the block to make room. Operation is O(N).
    762     pub fn insertInst(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
    763         return (try block.insertInstAsIndex(index, inst)).toRef();
    764     }
    765 
    766     pub fn insertInstAsIndex(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index {
    767         const sema = block.sema;
    768         const gpa = sema.gpa;
    769 
    770         try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
    771 
    772         const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
    773         sema.air_instructions.appendAssumeCapacity(inst);
    774 
    775         try block.instructions.insert(gpa, @intFromEnum(index), result_index);
    776         return result_index;
    777     }
    778 
    779     fn addUnreachable(block: *Block, src: LazySrcLoc, safety_check: bool) !void {
    780         if (safety_check and block.wantSafety()) {
    781             try block.sema.safetyPanic(block, src, .unreach);
    782         } else {
    783             _ = try block.addNoOp(.unreach);
    784         }
    785     }
    786 
    787     pub fn ownerModule(block: Block) *Package.Module {
    788         const zcu = block.sema.mod;
    789         return zcu.namespacePtr(block.namespace).file_scope.mod;
    790     }
    791 
    792     pub fn startAnonDecl(block: *Block) !WipAnonDecl {
    793         return WipAnonDecl{
    794             .block = block,
    795             .finished = false,
    796         };
    797     }
    798 
    799     pub const WipAnonDecl = struct {
    800         block: *Block,
    801         finished: bool,
    802 
    803         pub fn deinit(wad: *WipAnonDecl) void {
    804             wad.* = undefined;
    805         }
    806 
    807         /// `alignment` value of 0 means to use ABI alignment.
    808         pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: Alignment) !InternPool.DeclIndex {
    809             const sema = wad.block.sema;
    810             // Do this ahead of time because `createAnonymousDecl` depends on calling
    811             // `type.hasRuntimeBits()`.
    812             _ = try sema.typeHasRuntimeBits(ty);
    813             const new_decl_index = try sema.mod.createAnonymousDecl(wad.block, .{
    814                 .ty = ty,
    815                 .val = val,
    816             });
    817             const new_decl = sema.mod.declPtr(new_decl_index);
    818             new_decl.alignment = alignment;
    819             errdefer sema.mod.abortAnonDecl(new_decl_index);
    820             wad.finished = true;
    821             try sema.mod.finalizeAnonDecl(new_decl_index);
    822             return new_decl_index;
    823         }
    824     };
    825 };
    826 
    827 const LabeledBlock = struct {
    828     block: Block,
    829     label: Block.Label,
    830 
    831     fn destroy(lb: *LabeledBlock, gpa: Allocator) void {
    832         lb.block.instructions.deinit(gpa);
    833         lb.label.merges.deinit(gpa);
    834         gpa.destroy(lb);
    835     }
    836 };
    837 
    838 /// The value stored in the inferred allocation. This will go into
    839 /// peer type resolution. This is stored in a separate list so that
    840 /// the items are contiguous in memory and thus can be passed to
    841 /// `Module.resolvePeerTypes`.
    842 const InferredAlloc = struct {
    843     /// The placeholder `store` instructions used before the result pointer type
    844     /// is known. These should be rewritten to perform any required coercions
    845     /// when the type is resolved.
    846     /// Allocated from `sema.arena`.
    847     prongs: std.ArrayListUnmanaged(Air.Inst.Index) = .{},
    848 };
    849 
    850 const NeededComptimeReason = struct {
    851     needed_comptime_reason: []const u8,
    852     block_comptime_reason: ?*const Block.ComptimeReason = null,
    853 };
    854 
    855 pub fn deinit(sema: *Sema) void {
    856     const gpa = sema.gpa;
    857     sema.air_instructions.deinit(gpa);
    858     sema.air_extra.deinit(gpa);
    859     sema.inst_map.deinit(gpa);
    860     sema.decl_val_table.deinit(gpa);
    861     sema.types_to_resolve.deinit(gpa);
    862     {
    863         var it = sema.post_hoc_blocks.iterator();
    864         while (it.next()) |entry| {
    865             const labeled_block = entry.value_ptr.*;
    866             labeled_block.destroy(gpa);
    867         }
    868         sema.post_hoc_blocks.deinit(gpa);
    869     }
    870     sema.unresolved_inferred_allocs.deinit(gpa);
    871     sema.base_allocs.deinit(gpa);
    872     sema.maybe_comptime_allocs.deinit(gpa);
    873     sema.* = undefined;
    874 }
    875 
    876 /// Returns only the result from the body that is specified.
    877 /// Only appropriate to call when it is determined at comptime that this body
    878 /// has no peers.
    879 fn resolveBody(
    880     sema: *Sema,
    881     block: *Block,
    882     body: []const Zir.Inst.Index,
    883     /// This is the instruction that a break instruction within `body` can
    884     /// use to return from the body.
    885     body_inst: Zir.Inst.Index,
    886 ) CompileError!Air.Inst.Ref {
    887     const break_data = (try sema.analyzeBodyBreak(block, body)) orelse
    888         return .unreachable_value;
    889     // For comptime control flow, we need to detect when `analyzeBody` reports
    890     // that we need to break from an outer block. In such case we
    891     // use Zig's error mechanism to send control flow up the stack until
    892     // we find the corresponding block to this break.
    893     if (block.is_comptime and break_data.block_inst != body_inst) {
    894         sema.comptime_break_inst = break_data.inst;
    895         return error.ComptimeBreak;
    896     }
    897     return try sema.resolveInst(break_data.operand);
    898 }
    899 
    900 fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !void {
    901     _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
    902         error.ComptimeBreak => {
    903             const zir_datas = sema.code.instructions.items(.data);
    904             const break_data = zir_datas[@intFromEnum(sema.comptime_break_inst)].@"break";
    905             const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
    906             try sema.addRuntimeBreak(block, .{
    907                 .block_inst = extra.block_inst,
    908                 .operand = break_data.operand,
    909                 .inst = sema.comptime_break_inst,
    910             });
    911         },
    912         else => |e| return e,
    913     };
    914 }
    915 
    916 pub fn analyzeBody(
    917     sema: *Sema,
    918     block: *Block,
    919     body: []const Zir.Inst.Index,
    920 ) !void {
    921     _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
    922         error.ComptimeBreak => unreachable, // unexpected comptime control flow
    923         else => |e| return e,
    924     };
    925 }
    926 
    927 const BreakData = struct {
    928     block_inst: Zir.Inst.Index,
    929     operand: Zir.Inst.Ref,
    930     inst: Zir.Inst.Index,
    931 };
    932 
    933 pub fn analyzeBodyBreak(
    934     sema: *Sema,
    935     block: *Block,
    936     body: []const Zir.Inst.Index,
    937 ) CompileError!?BreakData {
    938     const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
    939         error.ComptimeBreak => sema.comptime_break_inst,
    940         else => |e| return e,
    941     };
    942     if (block.instructions.items.len != 0 and
    943         sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef()))
    944         return null;
    945     const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
    946     const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
    947     return BreakData{
    948         .block_inst = extra.block_inst,
    949         .operand = break_data.operand,
    950         .inst = break_inst,
    951     };
    952 }
    953 
    954 /// ZIR instructions which are always `noreturn` return this. This matches the
    955 /// return type of `analyzeBody` so that we can tail call them.
    956 /// Only appropriate to return when the instruction is known to be NoReturn
    957 /// solely based on the ZIR tag.
    958 const always_noreturn: CompileError!Zir.Inst.Index = @as(Zir.Inst.Index, undefined);
    959 
    960 /// This function is the main loop of `Sema` and it can be used in two different ways:
    961 /// * The traditional way where there are N breaks out of the block and peer type
    962 ///   resolution is done on the break operands. In this case, the `Zir.Inst.Index`
    963 ///   part of the return value will be `undefined`, and callsites should ignore it,
    964 ///   finding the block result value via the block scope.
    965 /// * The "flat" way. There is only 1 break out of the block, and it is with a `break_inline`
    966 ///   instruction. In this case, the `Zir.Inst.Index` part of the return value will be
    967 ///   the break instruction. This communicates both which block the break applies to, as
    968 ///   well as the operand. No block scope needs to be created for this strategy.
    969 fn analyzeBodyInner(
    970     sema: *Sema,
    971     block: *Block,
    972     body: []const Zir.Inst.Index,
    973 ) CompileError!Zir.Inst.Index {
    974     // No tracy calls here, to avoid interfering with the tail call mechanism.
    975 
    976     try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body);
    977 
    978     // Most of the time, we don't need to construct a new capture scope for a
    979     // block. However, successive iterations of comptime loops can capture
    980     // different values for the same Zir.Inst.Index, so in those cases, we will
    981     // have to create nested capture scopes; see the `.repeat` case below.
    982     const parent_capture_scope = block.wip_capture_scope;
    983 
    984     const mod = sema.mod;
    985     const map = &sema.inst_map;
    986     const tags = sema.code.instructions.items(.tag);
    987     const datas = sema.code.instructions.items(.data);
    988 
    989     var crash_info = crash_report.prepAnalyzeBody(sema, block, body);
    990     crash_info.push();
    991     defer crash_info.pop();
    992 
    993     var dbg_block_begins: u32 = 0;
    994 
    995     // We use a while (true) loop here to avoid a redundant way of breaking out of
    996     // the loop. The only way to break out of the loop is with a `noreturn`
    997     // instruction.
    998     var i: u32 = 0;
    999     const result = while (true) {
   1000         crash_info.setBodyIndex(i);
   1001         const inst = body[i];
   1002         std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{
   1003             mod.namespacePtr(mod.declPtr(block.src_decl).src_namespace).file_scope.sub_file_path, inst,
   1004         });
   1005         const air_inst: Air.Inst.Ref = switch (tags[@intFromEnum(inst)]) {
   1006             // zig fmt: off
   1007             .alloc                        => try sema.zirAlloc(block, inst),
   1008             .alloc_inferred               => try sema.zirAllocInferred(block, inst, true),
   1009             .alloc_inferred_mut           => try sema.zirAllocInferred(block, inst, false),
   1010             .alloc_inferred_comptime      => try sema.zirAllocInferredComptime(inst, true),
   1011             .alloc_inferred_comptime_mut  => try sema.zirAllocInferredComptime(inst, false),
   1012             .alloc_mut                    => try sema.zirAllocMut(block, inst),
   1013             .alloc_comptime_mut           => try sema.zirAllocComptime(block, inst),
   1014             .make_ptr_const               => try sema.zirMakePtrConst(block, inst),
   1015             .anyframe_type                => try sema.zirAnyframeType(block, inst),
   1016             .array_cat                    => try sema.zirArrayCat(block, inst),
   1017             .array_mul                    => try sema.zirArrayMul(block, inst),
   1018             .array_type                   => try sema.zirArrayType(block, inst),
   1019             .array_type_sentinel          => try sema.zirArrayTypeSentinel(block, inst),
   1020             .vector_type                  => try sema.zirVectorType(block, inst),
   1021             .as                           => try sema.zirAs(block, inst),
   1022             .as_node                      => try sema.zirAsNode(block, inst),
   1023             .as_shift_operand             => try sema.zirAsShiftOperand(block, inst),
   1024             .bit_and                      => try sema.zirBitwise(block, inst, .bit_and),
   1025             .bit_not                      => try sema.zirBitNot(block, inst),
   1026             .bit_or                       => try sema.zirBitwise(block, inst, .bit_or),
   1027             .bitcast                      => try sema.zirBitcast(block, inst),
   1028             .suspend_block                => try sema.zirSuspendBlock(block, inst),
   1029             .bool_not                     => try sema.zirBoolNot(block, inst),
   1030             .bool_br_and                  => try sema.zirBoolBr(block, inst, false),
   1031             .bool_br_or                   => try sema.zirBoolBr(block, inst, true),
   1032             .c_import                     => try sema.zirCImport(block, inst),
   1033             .call                         => try sema.zirCall(block, inst, .direct),
   1034             .field_call                   => try sema.zirCall(block, inst, .field),
   1035             .closure_get                  => try sema.zirClosureGet(block, inst),
   1036             .cmp_lt                       => try sema.zirCmp(block, inst, .lt),
   1037             .cmp_lte                      => try sema.zirCmp(block, inst, .lte),
   1038             .cmp_eq                       => try sema.zirCmpEq(block, inst, .eq, Air.Inst.Tag.fromCmpOp(.eq, block.float_mode == .Optimized)),
   1039             .cmp_gte                      => try sema.zirCmp(block, inst, .gte),
   1040             .cmp_gt                       => try sema.zirCmp(block, inst, .gt),
   1041             .cmp_neq                      => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .Optimized)),
   1042             .decl_ref                     => try sema.zirDeclRef(block, inst),
   1043             .decl_val                     => try sema.zirDeclVal(block, inst),
   1044             .load                         => try sema.zirLoad(block, inst),
   1045             .elem_ptr                     => try sema.zirElemPtr(block, inst),
   1046             .elem_ptr_node                => try sema.zirElemPtrNode(block, inst),
   1047             .elem_val                     => try sema.zirElemVal(block, inst),
   1048             .elem_val_node                => try sema.zirElemValNode(block, inst),
   1049             .elem_val_imm                 => try sema.zirElemValImm(block, inst),
   1050             .elem_type                    => try sema.zirElemType(block, inst),
   1051             .indexable_ptr_elem_type      => try sema.zirIndexablePtrElemType(block, inst),
   1052             .vector_elem_type             => try sema.zirVectorElemType(block, inst),
   1053             .enum_literal                 => try sema.zirEnumLiteral(block, inst),
   1054             .int_from_enum                => try sema.zirIntFromEnum(block, inst),
   1055             .enum_from_int                => try sema.zirEnumFromInt(block, inst),
   1056             .err_union_code               => try sema.zirErrUnionCode(block, inst),
   1057             .err_union_code_ptr           => try sema.zirErrUnionCodePtr(block, inst),
   1058             .err_union_payload_unsafe     => try sema.zirErrUnionPayload(block, inst),
   1059             .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst),
   1060             .error_union_type             => try sema.zirErrorUnionType(block, inst),
   1061             .error_value                  => try sema.zirErrorValue(block, inst),
   1062             .field_ptr                    => try sema.zirFieldPtr(block, inst),
   1063             .field_ptr_named              => try sema.zirFieldPtrNamed(block, inst),
   1064             .field_val                    => try sema.zirFieldVal(block, inst),
   1065             .field_val_named              => try sema.zirFieldValNamed(block, inst),
   1066             .func                         => try sema.zirFunc(block, inst, false),
   1067             .func_inferred                => try sema.zirFunc(block, inst, true),
   1068             .func_fancy                   => try sema.zirFuncFancy(block, inst),
   1069             .import                       => try sema.zirImport(block, inst),
   1070             .indexable_ptr_len            => try sema.zirIndexablePtrLen(block, inst),
   1071             .int                          => try sema.zirInt(block, inst),
   1072             .int_big                      => try sema.zirIntBig(block, inst),
   1073             .float                        => try sema.zirFloat(block, inst),
   1074             .float128                     => try sema.zirFloat128(block, inst),
   1075             .int_type                     => try sema.zirIntType(inst),
   1076             .is_non_err                   => try sema.zirIsNonErr(block, inst),
   1077             .is_non_err_ptr               => try sema.zirIsNonErrPtr(block, inst),
   1078             .ret_is_non_err               => try sema.zirRetIsNonErr(block, inst),
   1079             .is_non_null                  => try sema.zirIsNonNull(block, inst),
   1080             .is_non_null_ptr              => try sema.zirIsNonNullPtr(block, inst),
   1081             .merge_error_sets             => try sema.zirMergeErrorSets(block, inst),
   1082             .negate                       => try sema.zirNegate(block, inst),
   1083             .negate_wrap                  => try sema.zirNegateWrap(block, inst),
   1084             .optional_payload_safe        => try sema.zirOptionalPayload(block, inst, true),
   1085             .optional_payload_safe_ptr    => try sema.zirOptionalPayloadPtr(block, inst, true),
   1086             .optional_payload_unsafe      => try sema.zirOptionalPayload(block, inst, false),
   1087             .optional_payload_unsafe_ptr  => try sema.zirOptionalPayloadPtr(block, inst, false),
   1088             .optional_type                => try sema.zirOptionalType(block, inst),
   1089             .ptr_type                     => try sema.zirPtrType(block, inst),
   1090             .ref                          => try sema.zirRef(block, inst),
   1091             .ret_err_value_code           => try sema.zirRetErrValueCode(inst),
   1092             .shr                          => try sema.zirShr(block, inst, .shr),
   1093             .shr_exact                    => try sema.zirShr(block, inst, .shr_exact),
   1094             .slice_end                    => try sema.zirSliceEnd(block, inst),
   1095             .slice_sentinel               => try sema.zirSliceSentinel(block, inst),
   1096             .slice_start                  => try sema.zirSliceStart(block, inst),
   1097             .slice_length                 => try sema.zirSliceLength(block, inst),
   1098             .str                          => try sema.zirStr(inst),
   1099             .switch_block                 => try sema.zirSwitchBlock(block, inst, false),
   1100             .switch_block_ref             => try sema.zirSwitchBlock(block, inst, true),
   1101             .type_info                    => try sema.zirTypeInfo(block, inst),
   1102             .size_of                      => try sema.zirSizeOf(block, inst),
   1103             .bit_size_of                  => try sema.zirBitSizeOf(block, inst),
   1104             .typeof                       => try sema.zirTypeof(block, inst),
   1105             .typeof_builtin               => try sema.zirTypeofBuiltin(block, inst),
   1106             .typeof_log2_int_type         => try sema.zirTypeofLog2IntType(block, inst),
   1107             .xor                          => try sema.zirBitwise(block, inst, .xor),
   1108             .struct_init_empty            => try sema.zirStructInitEmpty(block, inst),
   1109             .struct_init_empty_result     => try sema.zirStructInitEmptyResult(block, inst, false),
   1110             .struct_init_empty_ref_result => try sema.zirStructInitEmptyResult(block, inst, true),
   1111             .struct_init_anon             => try sema.zirStructInitAnon(block, inst),
   1112             .struct_init                  => try sema.zirStructInit(block, inst, false),
   1113             .struct_init_ref              => try sema.zirStructInit(block, inst, true),
   1114             .struct_init_field_type       => try sema.zirStructInitFieldType(block, inst),
   1115             .struct_init_field_ptr        => try sema.zirStructInitFieldPtr(block, inst),
   1116             .array_init_anon              => try sema.zirArrayInitAnon(block, inst),
   1117             .array_init                   => try sema.zirArrayInit(block, inst, false),
   1118             .array_init_ref               => try sema.zirArrayInit(block, inst, true),
   1119             .array_init_elem_type         => try sema.zirArrayInitElemType(block, inst),
   1120             .array_init_elem_ptr          => try sema.zirArrayInitElemPtr(block, inst),
   1121             .union_init                   => try sema.zirUnionInit(block, inst),
   1122             .field_type_ref               => try sema.zirFieldTypeRef(block, inst),
   1123             .int_from_ptr                 => try sema.zirIntFromPtr(block, inst),
   1124             .align_of                     => try sema.zirAlignOf(block, inst),
   1125             .int_from_bool                => try sema.zirIntFromBool(block, inst),
   1126             .embed_file                   => try sema.zirEmbedFile(block, inst),
   1127             .error_name                   => try sema.zirErrorName(block, inst),
   1128             .tag_name                     => try sema.zirTagName(block, inst),
   1129             .type_name                    => try sema.zirTypeName(block, inst),
   1130             .frame_type                   => try sema.zirFrameType(block, inst),
   1131             .frame_size                   => try sema.zirFrameSize(block, inst),
   1132             .int_from_float               => try sema.zirIntFromFloat(block, inst),
   1133             .float_from_int               => try sema.zirFloatFromInt(block, inst),
   1134             .ptr_from_int                 => try sema.zirPtrFromInt(block, inst),
   1135             .float_cast                   => try sema.zirFloatCast(block, inst),
   1136             .int_cast                     => try sema.zirIntCast(block, inst),
   1137             .ptr_cast                     => try sema.zirPtrCast(block, inst),
   1138             .truncate                     => try sema.zirTruncate(block, inst),
   1139             .has_decl                     => try sema.zirHasDecl(block, inst),
   1140             .has_field                    => try sema.zirHasField(block, inst),
   1141             .byte_swap                    => try sema.zirByteSwap(block, inst),
   1142             .bit_reverse                  => try sema.zirBitReverse(block, inst),
   1143             .bit_offset_of                => try sema.zirBitOffsetOf(block, inst),
   1144             .offset_of                    => try sema.zirOffsetOf(block, inst),
   1145             .splat                        => try sema.zirSplat(block, inst),
   1146             .reduce                       => try sema.zirReduce(block, inst),
   1147             .shuffle                      => try sema.zirShuffle(block, inst),
   1148             .atomic_load                  => try sema.zirAtomicLoad(block, inst),
   1149             .atomic_rmw                   => try sema.zirAtomicRmw(block, inst),
   1150             .mul_add                      => try sema.zirMulAdd(block, inst),
   1151             .builtin_call                 => try sema.zirBuiltinCall(block, inst),
   1152             .field_parent_ptr             => try sema.zirFieldParentPtr(block, inst),
   1153             .@"resume"                    => try sema.zirResume(block, inst),
   1154             .@"await"                     => try sema.zirAwait(block, inst),
   1155             .for_len                      => try sema.zirForLen(block, inst),
   1156             .validate_array_init_ref_ty   => try sema.zirValidateArrayInitRefTy(block, inst),
   1157             .opt_eu_base_ptr_init         => try sema.zirOptEuBasePtrInit(block, inst),
   1158             .coerce_ptr_elem_ty           => try sema.zirCoercePtrElemTy(block, inst),
   1159 
   1160             .clz       => try sema.zirBitCount(block, inst, .clz,      Value.clz),
   1161             .ctz       => try sema.zirBitCount(block, inst, .ctz,      Value.ctz),
   1162             .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount),
   1163             .abs       => try sema.zirAbs(block, inst),
   1164 
   1165             .sqrt  => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt),
   1166             .sin   => try sema.zirUnaryMath(block, inst, .sin, Value.sin),
   1167             .cos   => try sema.zirUnaryMath(block, inst, .cos, Value.cos),
   1168             .tan   => try sema.zirUnaryMath(block, inst, .tan, Value.tan),
   1169             .exp   => try sema.zirUnaryMath(block, inst, .exp, Value.exp),
   1170             .exp2  => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2),
   1171             .log   => try sema.zirUnaryMath(block, inst, .log, Value.log),
   1172             .log2  => try sema.zirUnaryMath(block, inst, .log2, Value.log2),
   1173             .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10),
   1174             .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor),
   1175             .ceil  => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil),
   1176             .round => try sema.zirUnaryMath(block, inst, .round, Value.round),
   1177             .trunc => try sema.zirUnaryMath(block, inst, .trunc_float, Value.trunc),
   1178 
   1179             .error_set_decl      => try sema.zirErrorSetDecl(block, inst, .parent),
   1180             .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon),
   1181             .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func),
   1182 
   1183             .add        => try sema.zirArithmetic(block, inst, .add,        true),
   1184             .addwrap    => try sema.zirArithmetic(block, inst, .addwrap,    true),
   1185             .add_sat    => try sema.zirArithmetic(block, inst, .add_sat,    true),
   1186             .add_unsafe => try sema.zirArithmetic(block, inst, .add_unsafe, false),
   1187             .mul        => try sema.zirArithmetic(block, inst, .mul,        true),
   1188             .mulwrap    => try sema.zirArithmetic(block, inst, .mulwrap,    true),
   1189             .mul_sat    => try sema.zirArithmetic(block, inst, .mul_sat,    true),
   1190             .sub        => try sema.zirArithmetic(block, inst, .sub,        true),
   1191             .subwrap    => try sema.zirArithmetic(block, inst, .subwrap,    true),
   1192             .sub_sat    => try sema.zirArithmetic(block, inst, .sub_sat,    true),
   1193 
   1194             .div       => try sema.zirDiv(block, inst),
   1195             .div_exact => try sema.zirDivExact(block, inst),
   1196             .div_floor => try sema.zirDivFloor(block, inst),
   1197             .div_trunc => try sema.zirDivTrunc(block, inst),
   1198 
   1199             .mod_rem => try sema.zirModRem(block, inst),
   1200             .mod     => try sema.zirMod(block, inst),
   1201             .rem     => try sema.zirRem(block, inst),
   1202 
   1203             .max => try sema.zirMinMax(block, inst, .max),
   1204             .min => try sema.zirMinMax(block, inst, .min),
   1205 
   1206             .shl       => try sema.zirShl(block, inst, .shl),
   1207             .shl_exact => try sema.zirShl(block, inst, .shl_exact),
   1208             .shl_sat   => try sema.zirShl(block, inst, .shl_sat),
   1209 
   1210             .ret_ptr  => try sema.zirRetPtr(block),
   1211             .ret_type => Air.internedToRef(sema.fn_ret_ty.toIntern()),
   1212 
   1213             // Instructions that we know to *always* be noreturn based solely on their tag.
   1214             // These functions match the return type of analyzeBody so that we can
   1215             // tail call them here.
   1216             .compile_error  => break sema.zirCompileError(block, inst),
   1217             .ret_implicit   => break sema.zirRetImplicit(block, inst),
   1218             .ret_node       => break sema.zirRetNode(block, inst),
   1219             .ret_load       => break sema.zirRetLoad(block, inst),
   1220             .ret_err_value  => break sema.zirRetErrValue(block, inst),
   1221             .@"unreachable" => break sema.zirUnreachable(block, inst),
   1222             .panic          => break sema.zirPanic(block, inst),
   1223             .trap           => break sema.zirTrap(block, inst),
   1224             // zig fmt: on
   1225 
   1226             .extended => ext: {
   1227                 const extended = datas[@intFromEnum(inst)].extended;
   1228                 break :ext switch (extended.opcode) {
   1229                     // zig fmt: off
   1230                     .variable           => try sema.zirVarExtended(       block, extended),
   1231                     .struct_decl        => try sema.zirStructDecl(        block, extended, inst),
   1232                     .enum_decl          => try sema.zirEnumDecl(          block, extended, inst),
   1233                     .union_decl         => try sema.zirUnionDecl(         block, extended, inst),
   1234                     .opaque_decl        => try sema.zirOpaqueDecl(        block, extended, inst),
   1235                     .this               => try sema.zirThis(              block, extended),
   1236                     .ret_addr           => try sema.zirRetAddr(           block, extended),
   1237                     .builtin_src        => try sema.zirBuiltinSrc(        block, extended),
   1238                     .error_return_trace => try sema.zirErrorReturnTrace(  block),
   1239                     .frame              => try sema.zirFrame(             block, extended),
   1240                     .frame_address      => try sema.zirFrameAddress(      block, extended),
   1241                     .alloc              => try sema.zirAllocExtended(     block, extended),
   1242                     .builtin_extern     => try sema.zirBuiltinExtern(     block, extended),
   1243                     .@"asm"             => try sema.zirAsm(               block, extended, false),
   1244                     .asm_expr           => try sema.zirAsm(               block, extended, true),
   1245                     .typeof_peer        => try sema.zirTypeofPeer(        block, extended),
   1246                     .compile_log        => try sema.zirCompileLog(               extended),
   1247                     .min_multi          => try sema.zirMinMaxMulti(       block, extended, .min),
   1248                     .max_multi          => try sema.zirMinMaxMulti(       block, extended, .max),
   1249                     .add_with_overflow  => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1250                     .sub_with_overflow  => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1251                     .mul_with_overflow  => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1252                     .shl_with_overflow  => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1253                     .c_undef            => try sema.zirCUndef(            block, extended),
   1254                     .c_include          => try sema.zirCInclude(          block, extended),
   1255                     .c_define           => try sema.zirCDefine(           block, extended),
   1256                     .wasm_memory_size   => try sema.zirWasmMemorySize(    block, extended),
   1257                     .wasm_memory_grow   => try sema.zirWasmMemoryGrow(    block, extended),
   1258                     .prefetch           => try sema.zirPrefetch(          block, extended),
   1259                     .error_cast         => try sema.zirErrorCast(         block, extended),
   1260                     .await_nosuspend    => try sema.zirAwaitNosuspend(    block, extended),
   1261                     .select             => try sema.zirSelect(            block, extended),
   1262                     .int_from_error     => try sema.zirIntFromError(      block, extended),
   1263                     .error_from_int     => try sema.zirErrorFromInt(      block, extended),
   1264                     .reify              => try sema.zirReify(             block, extended, inst),
   1265                     .builtin_async_call => try sema.zirBuiltinAsyncCall(  block, extended),
   1266                     .cmpxchg            => try sema.zirCmpxchg(           block, extended),
   1267                     .c_va_arg           => try sema.zirCVaArg(            block, extended),
   1268                     .c_va_copy          => try sema.zirCVaCopy(           block, extended),
   1269                     .c_va_end           => try sema.zirCVaEnd(            block, extended),
   1270                     .c_va_start         => try sema.zirCVaStart(          block, extended),
   1271                     .ptr_cast_full      => try sema.zirPtrCastFull(       block, extended),
   1272                     .ptr_cast_no_dest   => try sema.zirPtrCastNoDest(     block, extended),
   1273                     .work_item_id       => try sema.zirWorkItem(          block, extended, extended.opcode),
   1274                     .work_group_size    => try sema.zirWorkItem(          block, extended, extended.opcode),
   1275                     .work_group_id      => try sema.zirWorkItem(          block, extended, extended.opcode),
   1276                     .in_comptime        => try sema.zirInComptime(        block),
   1277                     // zig fmt: on
   1278 
   1279                     .fence => {
   1280                         try sema.zirFence(block, extended);
   1281                         i += 1;
   1282                         continue;
   1283                     },
   1284                     .set_float_mode => {
   1285                         try sema.zirSetFloatMode(block, extended);
   1286                         i += 1;
   1287                         continue;
   1288                     },
   1289                     .set_align_stack => {
   1290                         try sema.zirSetAlignStack(block, extended);
   1291                         i += 1;
   1292                         continue;
   1293                     },
   1294                     .set_cold => {
   1295                         try sema.zirSetCold(block, extended);
   1296                         i += 1;
   1297                         continue;
   1298                     },
   1299                     .breakpoint => {
   1300                         if (!block.is_comptime) {
   1301                             _ = try block.addNoOp(.breakpoint);
   1302                         }
   1303                         i += 1;
   1304                         continue;
   1305                     },
   1306                     .value_placeholder => unreachable, // never appears in a body
   1307                 };
   1308             },
   1309 
   1310             // Instructions that we know can *never* be noreturn based solely on
   1311             // their tag. We avoid needlessly checking if they are noreturn and
   1312             // continue the loop.
   1313             // We also know that they cannot be referenced later, so we avoid
   1314             // putting them into the map.
   1315             .dbg_stmt => {
   1316                 try sema.zirDbgStmt(block, inst);
   1317                 i += 1;
   1318                 continue;
   1319             },
   1320             .dbg_var_ptr => {
   1321                 try sema.zirDbgVar(block, inst, .dbg_var_ptr);
   1322                 i += 1;
   1323                 continue;
   1324             },
   1325             .dbg_var_val => {
   1326                 try sema.zirDbgVar(block, inst, .dbg_var_val);
   1327                 i += 1;
   1328                 continue;
   1329             },
   1330             .dbg_block_begin => {
   1331                 dbg_block_begins += 1;
   1332                 try zirDbgBlockBegin(block);
   1333                 i += 1;
   1334                 continue;
   1335             },
   1336             .dbg_block_end => {
   1337                 dbg_block_begins -= 1;
   1338                 try zirDbgBlockEnd(block);
   1339                 i += 1;
   1340                 continue;
   1341             },
   1342             .ensure_err_union_payload_void => {
   1343                 try sema.zirEnsureErrUnionPayloadVoid(block, inst);
   1344                 i += 1;
   1345                 continue;
   1346             },
   1347             .ensure_result_non_error => {
   1348                 try sema.zirEnsureResultNonError(block, inst);
   1349                 i += 1;
   1350                 continue;
   1351             },
   1352             .ensure_result_used => {
   1353                 try sema.zirEnsureResultUsed(block, inst);
   1354                 i += 1;
   1355                 continue;
   1356             },
   1357             .set_eval_branch_quota => {
   1358                 try sema.zirSetEvalBranchQuota(block, inst);
   1359                 i += 1;
   1360                 continue;
   1361             },
   1362             .atomic_store => {
   1363                 try sema.zirAtomicStore(block, inst);
   1364                 i += 1;
   1365                 continue;
   1366             },
   1367             .store => {
   1368                 try sema.zirStore(block, inst);
   1369                 i += 1;
   1370                 continue;
   1371             },
   1372             .store_node => {
   1373                 try sema.zirStoreNode(block, inst);
   1374                 i += 1;
   1375                 continue;
   1376             },
   1377             .store_to_inferred_ptr => {
   1378                 try sema.zirStoreToInferredPtr(block, inst);
   1379                 i += 1;
   1380                 continue;
   1381             },
   1382             .resolve_inferred_alloc => {
   1383                 try sema.zirResolveInferredAlloc(block, inst);
   1384                 i += 1;
   1385                 continue;
   1386             },
   1387             .validate_struct_init_ty => {
   1388                 try sema.zirValidateStructInitTy(block, inst, false);
   1389                 i += 1;
   1390                 continue;
   1391             },
   1392             .validate_struct_init_result_ty => {
   1393                 try sema.zirValidateStructInitTy(block, inst, true);
   1394                 i += 1;
   1395                 continue;
   1396             },
   1397             .validate_array_init_ty => {
   1398                 try sema.zirValidateArrayInitTy(block, inst, false);
   1399                 i += 1;
   1400                 continue;
   1401             },
   1402             .validate_array_init_result_ty => {
   1403                 try sema.zirValidateArrayInitTy(block, inst, true);
   1404                 i += 1;
   1405                 continue;
   1406             },
   1407             .validate_ptr_struct_init => {
   1408                 try sema.zirValidatePtrStructInit(block, inst);
   1409                 i += 1;
   1410                 continue;
   1411             },
   1412             .validate_ptr_array_init => {
   1413                 try sema.zirValidatePtrArrayInit(block, inst);
   1414                 i += 1;
   1415                 continue;
   1416             },
   1417             .validate_deref => {
   1418                 try sema.zirValidateDeref(block, inst);
   1419                 i += 1;
   1420                 continue;
   1421             },
   1422             .validate_destructure => {
   1423                 try sema.zirValidateDestructure(block, inst);
   1424                 i += 1;
   1425                 continue;
   1426             },
   1427             .validate_ref_ty => {
   1428                 try sema.zirValidateRefTy(block, inst);
   1429                 i += 1;
   1430                 continue;
   1431             },
   1432             .@"export" => {
   1433                 try sema.zirExport(block, inst);
   1434                 i += 1;
   1435                 continue;
   1436             },
   1437             .export_value => {
   1438                 try sema.zirExportValue(block, inst);
   1439                 i += 1;
   1440                 continue;
   1441             },
   1442             .set_runtime_safety => {
   1443                 try sema.zirSetRuntimeSafety(block, inst);
   1444                 i += 1;
   1445                 continue;
   1446             },
   1447             .param => {
   1448                 try sema.zirParam(block, inst, false);
   1449                 i += 1;
   1450                 continue;
   1451             },
   1452             .param_comptime => {
   1453                 try sema.zirParam(block, inst, true);
   1454                 i += 1;
   1455                 continue;
   1456             },
   1457             .param_anytype => {
   1458                 try sema.zirParamAnytype(block, inst, false);
   1459                 i += 1;
   1460                 continue;
   1461             },
   1462             .param_anytype_comptime => {
   1463                 try sema.zirParamAnytype(block, inst, true);
   1464                 i += 1;
   1465                 continue;
   1466             },
   1467             .closure_capture => {
   1468                 try sema.zirClosureCapture(block, inst);
   1469                 i += 1;
   1470                 continue;
   1471             },
   1472             .memcpy => {
   1473                 try sema.zirMemcpy(block, inst);
   1474                 i += 1;
   1475                 continue;
   1476             },
   1477             .memset => {
   1478                 try sema.zirMemset(block, inst);
   1479                 i += 1;
   1480                 continue;
   1481             },
   1482             .check_comptime_control_flow => {
   1483                 if (!block.is_comptime) {
   1484                     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   1485                     const src = inst_data.src();
   1486                     const inline_block = inst_data.operand.toIndex().?;
   1487 
   1488                     var check_block = block;
   1489                     const target_runtime_index = while (true) {
   1490                         if (check_block.inline_block == inline_block.toOptional()) {
   1491                             break check_block.runtime_index;
   1492                         }
   1493                         check_block = check_block.parent.?;
   1494                     };
   1495 
   1496                     if (@intFromEnum(target_runtime_index) < @intFromEnum(block.runtime_index)) {
   1497                         const runtime_src = block.runtime_cond orelse block.runtime_loop.?;
   1498                         const msg = msg: {
   1499                             const msg = try sema.errMsg(block, src, "comptime control flow inside runtime block", .{});
   1500                             errdefer msg.destroy(sema.gpa);
   1501 
   1502                             try sema.errNote(block, runtime_src, msg, "runtime control flow here", .{});
   1503                             break :msg msg;
   1504                         };
   1505                         return sema.failWithOwnedErrorMsg(block, msg);
   1506                     }
   1507                 }
   1508                 i += 1;
   1509                 continue;
   1510             },
   1511             .save_err_ret_index => {
   1512                 try sema.zirSaveErrRetIndex(block, inst);
   1513                 i += 1;
   1514                 continue;
   1515             },
   1516             .restore_err_ret_index => {
   1517                 try sema.zirRestoreErrRetIndex(block, inst);
   1518                 i += 1;
   1519                 continue;
   1520             },
   1521 
   1522             // Special case instructions to handle comptime control flow.
   1523             .@"break" => {
   1524                 if (block.is_comptime) {
   1525                     break inst; // same as break_inline
   1526                 } else {
   1527                     break sema.zirBreak(block, inst);
   1528                 }
   1529             },
   1530             .break_inline => {
   1531                 if (block.is_comptime) {
   1532                     break inst;
   1533                 } else {
   1534                     sema.comptime_break_inst = inst;
   1535                     return error.ComptimeBreak;
   1536                 }
   1537             },
   1538             .repeat => {
   1539                 if (block.is_comptime) {
   1540                     // Send comptime control flow back to the beginning of this block.
   1541                     const src = LazySrcLoc.nodeOffset(datas[@intFromEnum(inst)].node);
   1542                     try sema.emitBackwardBranch(block, src);
   1543 
   1544                     // We need to construct new capture scopes for the next loop iteration so it
   1545                     // can capture values without clobbering the earlier iteration's captures.
   1546                     block.wip_capture_scope = try mod.createCaptureScope(parent_capture_scope);
   1547 
   1548                     i = 0;
   1549                     continue;
   1550                 } else {
   1551                     break always_noreturn;
   1552                 }
   1553             },
   1554             .repeat_inline => {
   1555                 // Send comptime control flow back to the beginning of this block.
   1556                 const src = LazySrcLoc.nodeOffset(datas[@intFromEnum(inst)].node);
   1557                 try sema.emitBackwardBranch(block, src);
   1558 
   1559                 // We need to construct new capture scopes for the next loop iteration so it
   1560                 // can capture values without clobbering the earlier iteration's captures.
   1561                 block.wip_capture_scope = try mod.createCaptureScope(parent_capture_scope);
   1562 
   1563                 i = 0;
   1564                 continue;
   1565             },
   1566             .loop => blk: {
   1567                 if (!block.is_comptime) break :blk try sema.zirLoop(block, inst);
   1568                 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220
   1569                 const inst_data = datas[@intFromEnum(inst)].pl_node;
   1570                 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   1571                 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1572                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1573                     break always_noreturn;
   1574                 if (inst == break_data.block_inst) {
   1575                     break :blk try sema.resolveInst(break_data.operand);
   1576                 } else {
   1577                     break break_data.inst;
   1578                 }
   1579             },
   1580             .block, .block_comptime => blk: {
   1581                 if (!block.is_comptime) {
   1582                     break :blk try sema.zirBlock(block, inst, tags[@intFromEnum(inst)] == .block_comptime);
   1583                 }
   1584                 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220
   1585                 const inst_data = datas[@intFromEnum(inst)].pl_node;
   1586                 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   1587                 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1588                 // If this block contains a function prototype, we need to reset the
   1589                 // current list of parameters and restore it later.
   1590                 // Note: this probably needs to be resolved in a more general manner.
   1591                 const prev_params = block.params;
   1592                 block.params = .{};
   1593                 defer block.params = prev_params;
   1594                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1595                     break always_noreturn;
   1596                 if (inst == break_data.block_inst) {
   1597                     break :blk try sema.resolveInst(break_data.operand);
   1598                 } else {
   1599                     break break_data.inst;
   1600                 }
   1601             },
   1602             .block_inline => blk: {
   1603                 // Directly analyze the block body without introducing a new block.
   1604                 // However, in the case of a corresponding break_inline which reaches
   1605                 // through a runtime conditional branch, we must retroactively emit
   1606                 // a block, so we remember the block index here just in case.
   1607                 const block_index = block.instructions.items.len;
   1608                 const inst_data = datas[@intFromEnum(inst)].pl_node;
   1609                 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   1610                 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1611                 const gpa = sema.gpa;
   1612 
   1613                 const opt_break_data = b: {
   1614                     // Create a temporary child block so that this inline block is properly
   1615                     // labeled for any .restore_err_ret_index instructions
   1616                     var child_block = block.makeSubBlock();
   1617 
   1618                     // If this block contains a function prototype, we need to reset the
   1619                     // current list of parameters and restore it later.
   1620                     // Note: this probably needs to be resolved in a more general manner.
   1621                     const tag_index = @intFromEnum(inline_body[inline_body.len - 1]);
   1622                     child_block.inline_block = (if (tags[tag_index] == .repeat_inline)
   1623                         inline_body[0]
   1624                     else
   1625                         inst).toOptional();
   1626 
   1627                     var label: Block.Label = .{
   1628                         .zir_block = inst,
   1629                         .merges = undefined,
   1630                     };
   1631                     child_block.label = &label;
   1632 
   1633                     // Write these instructions directly into the parent block
   1634                     child_block.instructions = block.instructions;
   1635                     defer block.instructions = child_block.instructions;
   1636 
   1637                     break :b try sema.analyzeBodyBreak(&child_block, inline_body);
   1638                 };
   1639 
   1640                 // A runtime conditional branch that needs a post-hoc block to be
   1641                 // emitted communicates this by mapping the block index into the inst map.
   1642                 if (map.get(inst)) |new_block_ref| ph: {
   1643                     // Comptime control flow populates the map, so we don't actually know
   1644                     // if this is a post-hoc runtime block until we check the
   1645                     // post_hoc_block map.
   1646                     const new_block_inst = new_block_ref.toIndex() orelse break :ph;
   1647                     const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse
   1648                         break :ph;
   1649 
   1650                     // In this case we need to move all the instructions starting at
   1651                     // block_index from the current block into this new one.
   1652 
   1653                     if (opt_break_data) |break_data| {
   1654                         // This is a comptime break which we now change to a runtime break
   1655                         // since it crosses a runtime branch.
   1656                         // It may pass through our currently being analyzed block_inline or it
   1657                         // may point directly to it. In the latter case, this modifies the
   1658                         // block that we are about to look up in the post_hoc_blocks map below.
   1659                         try sema.addRuntimeBreak(block, break_data);
   1660                     } else {
   1661                         // Here the comptime control flow ends with noreturn; however
   1662                         // we have runtime control flow continuing after this block.
   1663                         // This branch is therefore handled by the `i += 1; continue;`
   1664                         // logic below.
   1665                     }
   1666 
   1667                     try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]);
   1668                     block.instructions.items.len = block_index;
   1669 
   1670                     const block_result = try sema.analyzeBlockBody(block, inst_data.src(), &labeled_block.block, &labeled_block.label.merges);
   1671                     {
   1672                         // Destroy the ad-hoc block entry so that it does not interfere with
   1673                         // the next iteration of comptime control flow, if any.
   1674                         labeled_block.destroy(gpa);
   1675                         assert(sema.post_hoc_blocks.remove(new_block_inst));
   1676                     }
   1677                     map.putAssumeCapacity(inst, block_result);
   1678                     i += 1;
   1679                     continue;
   1680                 }
   1681 
   1682                 const break_data = opt_break_data orelse break always_noreturn;
   1683                 if (inst == break_data.block_inst) {
   1684                     break :blk try sema.resolveInst(break_data.operand);
   1685                 } else {
   1686                     break break_data.inst;
   1687                 }
   1688             },
   1689             .condbr => blk: {
   1690                 if (!block.is_comptime) break sema.zirCondbr(block, inst);
   1691                 // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220
   1692                 const inst_data = datas[@intFromEnum(inst)].pl_node;
   1693                 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
   1694                 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
   1695                 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
   1696                 const else_body = sema.code.bodySlice(
   1697                     extra.end + then_body.len,
   1698                     extra.data.else_body_len,
   1699                 );
   1700                 const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition, .{
   1701                     .needed_comptime_reason = "condition in comptime branch must be comptime-known",
   1702                     .block_comptime_reason = block.comptime_reason,
   1703                 });
   1704                 const inline_body = if (cond.val.toBool()) then_body else else_body;
   1705 
   1706                 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src);
   1707                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1708                     break always_noreturn;
   1709                 if (inst == break_data.block_inst) {
   1710                     break :blk try sema.resolveInst(break_data.operand);
   1711                 } else {
   1712                     break break_data.inst;
   1713                 }
   1714             },
   1715             .condbr_inline => blk: {
   1716                 const inst_data = datas[@intFromEnum(inst)].pl_node;
   1717                 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
   1718                 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
   1719                 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
   1720                 const else_body = sema.code.bodySlice(
   1721                     extra.end + then_body.len,
   1722                     extra.data.else_body_len,
   1723                 );
   1724                 const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition, .{
   1725                     .needed_comptime_reason = "condition in comptime branch must be comptime-known",
   1726                     .block_comptime_reason = block.comptime_reason,
   1727                 });
   1728                 const inline_body = if (cond.val.toBool()) then_body else else_body;
   1729 
   1730                 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src);
   1731                 const old_runtime_index = block.runtime_index;
   1732                 defer block.runtime_index = old_runtime_index;
   1733                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1734                     break always_noreturn;
   1735                 if (inst == break_data.block_inst) {
   1736                     break :blk try sema.resolveInst(break_data.operand);
   1737                 } else {
   1738                     break break_data.inst;
   1739                 }
   1740             },
   1741             .@"try" => blk: {
   1742                 if (!block.is_comptime) break :blk try sema.zirTry(block, inst);
   1743                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   1744                 const src = inst_data.src();
   1745                 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
   1746                 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
   1747                 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1748                 const err_union = try sema.resolveInst(extra.data.operand);
   1749                 const err_union_ty = sema.typeOf(err_union);
   1750                 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
   1751                     return sema.fail(block, operand_src, "expected error union type, found '{}'", .{
   1752                         err_union_ty.fmt(mod),
   1753                     });
   1754                 }
   1755                 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
   1756                 assert(is_non_err != .none);
   1757                 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, .{
   1758                     .needed_comptime_reason = "try operand inside comptime block must be comptime-known",
   1759                     .block_comptime_reason = block.comptime_reason,
   1760                 });
   1761                 if (is_non_err_val.toBool()) {
   1762                     break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
   1763                 }
   1764                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1765                     break always_noreturn;
   1766                 if (inst == break_data.block_inst) {
   1767                     break :blk try sema.resolveInst(break_data.operand);
   1768                 } else {
   1769                     break break_data.inst;
   1770                 }
   1771             },
   1772             .try_ptr => blk: {
   1773                 if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst);
   1774                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   1775                 const src = inst_data.src();
   1776                 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
   1777                 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
   1778                 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1779                 const operand = try sema.resolveInst(extra.data.operand);
   1780                 const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
   1781                 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
   1782                 assert(is_non_err != .none);
   1783                 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, .{
   1784                     .needed_comptime_reason = "try operand inside comptime block must be comptime-known",
   1785                     .block_comptime_reason = block.comptime_reason,
   1786                 });
   1787                 if (is_non_err_val.toBool()) {
   1788                     break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
   1789                 }
   1790                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1791                     break always_noreturn;
   1792                 if (inst == break_data.block_inst) {
   1793                     break :blk try sema.resolveInst(break_data.operand);
   1794                 } else {
   1795                     break break_data.inst;
   1796                 }
   1797             },
   1798             .@"defer" => blk: {
   1799                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"defer";
   1800                 const defer_body = sema.code.bodySlice(inst_data.index, inst_data.len);
   1801                 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
   1802                     error.ComptimeBreak => sema.comptime_break_inst,
   1803                     else => |e| return e,
   1804                 };
   1805                 if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn;
   1806                 break :blk .void_value;
   1807             },
   1808             .defer_err_code => blk: {
   1809                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].defer_err_code;
   1810                 const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data;
   1811                 const defer_body = sema.code.bodySlice(extra.index, extra.len);
   1812                 const err_code = try sema.resolveInst(inst_data.err_code);
   1813                 map.putAssumeCapacity(extra.remapped_err_code, err_code);
   1814                 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
   1815                     error.ComptimeBreak => sema.comptime_break_inst,
   1816                     else => |e| return e,
   1817                 };
   1818                 if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn;
   1819                 break :blk .void_value;
   1820             },
   1821         };
   1822         if (sema.isNoReturn(air_inst)) {
   1823             // We're going to assume that the body itself is noreturn, so let's ensure that now
   1824             assert(block.instructions.items.len > 0);
   1825             assert(sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef()));
   1826             break always_noreturn;
   1827         }
   1828         map.putAssumeCapacity(inst, air_inst);
   1829         i += 1;
   1830     };
   1831 
   1832     // balance out dbg_block_begins in case of early noreturn
   1833     if (!block.is_comptime and !block.ownerModule().strip) {
   1834         const noreturn_inst = block.instructions.popOrNull();
   1835         while (dbg_block_begins > 0) {
   1836             dbg_block_begins -= 1;
   1837             _ = try block.addInst(.{
   1838                 .tag = .dbg_block_end,
   1839                 .data = undefined,
   1840             });
   1841         }
   1842         if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some);
   1843     }
   1844 
   1845     // We may have overwritten the capture scope due to a `repeat` instruction where
   1846     // the body had a capture; restore it now.
   1847     block.wip_capture_scope = parent_capture_scope;
   1848 
   1849     return result;
   1850 }
   1851 
   1852 pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
   1853     if (zir_ref == .none) {
   1854         return .none;
   1855     } else {
   1856         return resolveInst(sema, zir_ref);
   1857     }
   1858 }
   1859 
   1860 pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
   1861     assert(zir_ref != .none);
   1862     if (zir_ref.toIndex()) |i| {
   1863         const inst = sema.inst_map.get(i).?;
   1864         if (inst == .generic_poison) return error.GenericPoison;
   1865         return inst;
   1866     }
   1867     // First section of indexes correspond to a set number of constant values.
   1868     // We intentionally map the same indexes to the same values between ZIR and AIR.
   1869     return @enumFromInt(@intFromEnum(zir_ref));
   1870 }
   1871 
   1872 fn resolveConstBool(
   1873     sema: *Sema,
   1874     block: *Block,
   1875     src: LazySrcLoc,
   1876     zir_ref: Zir.Inst.Ref,
   1877     reason: NeededComptimeReason,
   1878 ) !bool {
   1879     const air_inst = try sema.resolveInst(zir_ref);
   1880     const wanted_type = Type.bool;
   1881     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   1882     const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
   1883     return val.toBool();
   1884 }
   1885 
   1886 pub fn resolveConstString(
   1887     sema: *Sema,
   1888     block: *Block,
   1889     src: LazySrcLoc,
   1890     zir_ref: Zir.Inst.Ref,
   1891     reason: NeededComptimeReason,
   1892 ) ![]u8 {
   1893     const air_inst = try sema.resolveInst(zir_ref);
   1894     const wanted_type = Type.slice_const_u8;
   1895     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   1896     const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
   1897     return val.toAllocatedBytes(wanted_type, sema.arena, sema.mod);
   1898 }
   1899 
   1900 pub fn resolveConstStringIntern(
   1901     sema: *Sema,
   1902     block: *Block,
   1903     src: LazySrcLoc,
   1904     zir_ref: Zir.Inst.Ref,
   1905     reason: NeededComptimeReason,
   1906 ) !InternPool.NullTerminatedString {
   1907     const air_inst = try sema.resolveInst(zir_ref);
   1908     const wanted_type = Type.slice_const_u8;
   1909     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   1910     const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
   1911     return val.toIpString(wanted_type, sema.mod);
   1912 }
   1913 
   1914 pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
   1915     const air_inst = try sema.resolveInst(zir_ref);
   1916     assert(air_inst != .var_args_param_type);
   1917     const ty = try sema.analyzeAsType(block, src, air_inst);
   1918     if (ty.isGenericPoison()) return error.GenericPoison;
   1919     return ty;
   1920 }
   1921 
   1922 fn resolveDestType(
   1923     sema: *Sema,
   1924     block: *Block,
   1925     src: LazySrcLoc,
   1926     zir_ref: Zir.Inst.Ref,
   1927     strat: enum { remove_eu_opt, remove_eu, remove_opt },
   1928     builtin_name: []const u8,
   1929 ) !Type {
   1930     const mod = sema.mod;
   1931     const remove_eu = switch (strat) {
   1932         .remove_eu_opt, .remove_eu => true,
   1933         .remove_opt => false,
   1934     };
   1935     const remove_opt = switch (strat) {
   1936         .remove_eu_opt, .remove_opt => true,
   1937         .remove_eu => false,
   1938     };
   1939 
   1940     const raw_ty = sema.resolveType(block, src, zir_ref) catch |err| switch (err) {
   1941         error.GenericPoison => {
   1942             // Cast builtins use their result type as the destination type, but
   1943             // it could be an anytype argument, which we can't catch in AstGen.
   1944             const msg = msg: {
   1945                 const msg = try sema.errMsg(block, src, "{s} must have a known result type", .{builtin_name});
   1946                 errdefer msg.destroy(sema.gpa);
   1947                 switch (sema.genericPoisonReason(zir_ref)) {
   1948                     .anytype_param => |call_src| try sema.errNote(block, call_src, msg, "result type is unknown due to anytype parameter", .{}),
   1949                     .anyopaque_ptr => |ptr_src| try sema.errNote(block, ptr_src, msg, "result type is unknown due to opaque pointer type", .{}),
   1950                     .unknown => {},
   1951                 }
   1952                 try sema.errNote(block, src, msg, "use @as to provide explicit result type", .{});
   1953                 break :msg msg;
   1954             };
   1955             return sema.failWithOwnedErrorMsg(block, msg);
   1956         },
   1957         else => |e| return e,
   1958     };
   1959 
   1960     if (remove_eu and raw_ty.zigTypeTag(mod) == .ErrorUnion) {
   1961         const eu_child = raw_ty.errorUnionPayload(mod);
   1962         if (remove_opt and eu_child.zigTypeTag(mod) == .Optional) {
   1963             return eu_child.childType(mod);
   1964         }
   1965         return eu_child;
   1966     }
   1967     if (remove_opt and raw_ty.zigTypeTag(mod) == .Optional) {
   1968         return raw_ty.childType(mod);
   1969     }
   1970     return raw_ty;
   1971 }
   1972 
   1973 const GenericPoisonReason = union(enum) {
   1974     anytype_param: LazySrcLoc,
   1975     anyopaque_ptr: LazySrcLoc,
   1976     unknown,
   1977 };
   1978 
   1979 /// Backtracks through ZIR instructions to determine the reason a generic poison
   1980 /// type was created. Used for error reporting.
   1981 fn genericPoisonReason(sema: *Sema, ref: Zir.Inst.Ref) GenericPoisonReason {
   1982     var cur = ref;
   1983     while (true) {
   1984         const inst = cur.toIndex() orelse return .unknown;
   1985         switch (sema.code.instructions.items(.tag)[@intFromEnum(inst)]) {
   1986             .validate_array_init_ref_ty => {
   1987                 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   1988                 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data;
   1989                 cur = extra.ptr_ty;
   1990             },
   1991             .array_init_elem_type => {
   1992                 const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
   1993                 cur = bin.lhs;
   1994             },
   1995             .indexable_ptr_elem_type, .vector_elem_type => {
   1996                 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   1997                 cur = un_node.operand;
   1998             },
   1999             .struct_init_field_type => {
   2000                 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   2001                 const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data;
   2002                 cur = extra.container_type;
   2003             },
   2004             .elem_type => {
   2005                 // There are two cases here: the pointer type may already have been
   2006                 // generic poison, or it may have been an anyopaque pointer.
   2007                 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   2008                 const operand_ref = sema.resolveInst(un_node.operand) catch |err| switch (err) {
   2009                     error.GenericPoison => unreachable, // this is a type, not a value
   2010                 };
   2011                 const operand_val = operand_ref.toInterned() orelse return .unknown;
   2012                 if (operand_val == .generic_poison_type) {
   2013                     // The pointer was generic poison - keep looking.
   2014                     cur = un_node.operand;
   2015                 } else {
   2016                     // This must be an anyopaque pointer!
   2017                     return .{ .anyopaque_ptr = un_node.src() };
   2018                 }
   2019             },
   2020             .call, .field_call => {
   2021                 // A function call can never return generic poison, so we must be
   2022                 // evaluating an `anytype` function parameter.
   2023                 // TODO: better source location - function decl rather than call
   2024                 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   2025                 return .{ .anytype_param = pl_node.src() };
   2026             },
   2027             else => return .unknown,
   2028         }
   2029     }
   2030 }
   2031 
   2032 fn analyzeAsType(
   2033     sema: *Sema,
   2034     block: *Block,
   2035     src: LazySrcLoc,
   2036     air_inst: Air.Inst.Ref,
   2037 ) !Type {
   2038     const wanted_type = Type.type;
   2039     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   2040     const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, .{
   2041         .needed_comptime_reason = "types must be comptime-known",
   2042     });
   2043     return val.toType();
   2044 }
   2045 
   2046 pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void {
   2047     const mod = sema.mod;
   2048     const comp = mod.comp;
   2049     const gpa = sema.gpa;
   2050     const ip = &mod.intern_pool;
   2051     if (!comp.config.any_error_tracing) return;
   2052 
   2053     assert(!block.is_comptime);
   2054     var err_trace_block = block.makeSubBlock();
   2055     defer err_trace_block.instructions.deinit(gpa);
   2056 
   2057     const src: LazySrcLoc = .unneeded;
   2058 
   2059     // var addrs: [err_return_trace_addr_count]usize = undefined;
   2060     const err_return_trace_addr_count = 32;
   2061     const addr_arr_ty = try mod.arrayType(.{
   2062         .len = err_return_trace_addr_count,
   2063         .child = .usize_type,
   2064     });
   2065     const addrs_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(addr_arr_ty));
   2066 
   2067     // var st: StackTrace = undefined;
   2068     const stack_trace_ty = try sema.getBuiltinType("StackTrace");
   2069     try sema.resolveTypeFields(stack_trace_ty);
   2070     const st_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(stack_trace_ty));
   2071 
   2072     // st.instruction_addresses = &addrs;
   2073     const instruction_addresses_field_name = try ip.getOrPutString(gpa, "instruction_addresses");
   2074     const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true);
   2075     try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
   2076 
   2077     // st.index = 0;
   2078     const index_field_name = try ip.getOrPutString(gpa, "index");
   2079     const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true);
   2080     try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store);
   2081 
   2082     // @errorReturnTrace() = &st;
   2083     _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr);
   2084 
   2085     try block.instructions.insertSlice(gpa, last_arg_index, err_trace_block.instructions.items);
   2086 }
   2087 
   2088 /// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value.
   2089 /// InternPool key `variable` is considered a runtime value.
   2090 /// Generic poison causes `error.GenericPoison` to be returned.
   2091 fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
   2092     const val = (try sema.resolveValueAllowVariables(inst)) orelse return null;
   2093     if (val.isGenericPoison()) return error.GenericPoison;
   2094     if (sema.mod.intern_pool.isVariable(val.toIntern())) return null;
   2095     return val;
   2096 }
   2097 
   2098 /// Like `resolveValue`, but emits an error if the value is not comptime-known.
   2099 fn resolveConstValue(
   2100     sema: *Sema,
   2101     block: *Block,
   2102     src: LazySrcLoc,
   2103     inst: Air.Inst.Ref,
   2104     reason: NeededComptimeReason,
   2105 ) CompileError!Value {
   2106     return try sema.resolveValue(inst) orelse {
   2107         return sema.failWithNeededComptime(block, src, reason);
   2108     };
   2109 }
   2110 
   2111 /// Like `resolveValue`, but emits an error if the value is comptime-known to be undefined.
   2112 fn resolveDefinedValue(
   2113     sema: *Sema,
   2114     block: *Block,
   2115     src: LazySrcLoc,
   2116     air_ref: Air.Inst.Ref,
   2117 ) CompileError!?Value {
   2118     const mod = sema.mod;
   2119     const val = try sema.resolveValue(air_ref) orelse return null;
   2120     if (val.isUndef(mod)) {
   2121         return sema.failWithUseOfUndef(block, src);
   2122     }
   2123     return val;
   2124 }
   2125 
   2126 /// Like `resolveValue`, but emits an error if the value is not comptime-known or is undefined.
   2127 fn resolveConstDefinedValue(
   2128     sema: *Sema,
   2129     block: *Block,
   2130     src: LazySrcLoc,
   2131     air_ref: Air.Inst.Ref,
   2132     reason: NeededComptimeReason,
   2133 ) CompileError!Value {
   2134     const val = try sema.resolveConstValue(block, src, air_ref, reason);
   2135     if (val.isUndef(sema.mod)) return sema.failWithUseOfUndef(block, src);
   2136     return val;
   2137 }
   2138 
   2139 /// Like `resolveValue`, but recursively resolves lazy values before returning.
   2140 fn resolveValueResolveLazy(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
   2141     return try sema.resolveLazyValue((try sema.resolveValue(inst)) orelse return null);
   2142 }
   2143 
   2144 /// Like `resolveValue`, but any pointer value which does not correspond
   2145 /// to a comptime-known integer (e.g. a decl pointer) returns `null`.
   2146 /// Lazy values are recursively resolved.
   2147 fn resolveValueIntable(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
   2148     const val = (try sema.resolveValue(inst)) orelse return null;
   2149     if (sema.mod.intern_pool.getBackingAddrTag(val.toIntern())) |addr| switch (addr) {
   2150         .decl, .anon_decl, .mut_decl, .comptime_field => return null,
   2151         .int => {},
   2152         .eu_payload, .opt_payload, .elem, .field => unreachable,
   2153     };
   2154     return try sema.resolveLazyValue(val);
   2155 }
   2156 
   2157 /// Returns all InternPool keys representing values, including `variable`, `undef`, and `generic_poison`.
   2158 fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
   2159     assert(inst != .none);
   2160     // First section of indexes correspond to a set number of constant values.
   2161     if (@intFromEnum(inst) < InternPool.static_len) {
   2162         return Value.fromInterned(@as(InternPool.Index, @enumFromInt(@intFromEnum(inst))));
   2163     }
   2164 
   2165     const air_tags = sema.air_instructions.items(.tag);
   2166     if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| {
   2167         if (inst.toInterned()) |ip_index| {
   2168             const val = Value.fromInterned(ip_index);
   2169             if (val.getVariable(sema.mod) != null) return val;
   2170         }
   2171         return opv;
   2172     }
   2173     const ip_index = inst.toInterned() orelse {
   2174         switch (air_tags[@intFromEnum(inst.toIndex().?)]) {
   2175             .inferred_alloc => unreachable,
   2176             .inferred_alloc_comptime => unreachable,
   2177             else => return null,
   2178         }
   2179     };
   2180     const val = Value.fromInterned(ip_index);
   2181     if (val.isPtrToThreadLocal(sema.mod)) return null;
   2182     return val;
   2183 }
   2184 
   2185 /// Returns a compile error if the value has tag `variable`. See `resolveInstValue` for
   2186 /// a function that does not.
   2187 pub fn resolveInstConst(
   2188     sema: *Sema,
   2189     block: *Block,
   2190     src: LazySrcLoc,
   2191     zir_ref: Zir.Inst.Ref,
   2192     reason: NeededComptimeReason,
   2193 ) CompileError!TypedValue {
   2194     const air_ref = try sema.resolveInst(zir_ref);
   2195     const val = try sema.resolveConstDefinedValue(block, src, air_ref, reason);
   2196     return .{
   2197         .ty = sema.typeOf(air_ref),
   2198         .val = val,
   2199     };
   2200 }
   2201 
   2202 /// Value Tag may be `undef` or `variable`.
   2203 /// See `resolveInstConst` for an alternative.
   2204 pub fn resolveInstValueAllowVariables(
   2205     sema: *Sema,
   2206     block: *Block,
   2207     src: LazySrcLoc,
   2208     zir_ref: Zir.Inst.Ref,
   2209     reason: NeededComptimeReason,
   2210 ) CompileError!TypedValue {
   2211     const air_ref = try sema.resolveInst(zir_ref);
   2212     const val = try sema.resolveValueAllowVariables(air_ref) orelse {
   2213         return sema.failWithNeededComptime(block, src, reason);
   2214     };
   2215     if (val.isGenericPoison()) return error.GenericPoison;
   2216     return .{
   2217         .ty = sema.typeOf(air_ref),
   2218         .val = val,
   2219     };
   2220 }
   2221 
   2222 fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: NeededComptimeReason) CompileError {
   2223     const msg = msg: {
   2224         const msg = try sema.errMsg(block, src, "unable to resolve comptime value", .{});
   2225         errdefer msg.destroy(sema.gpa);
   2226         try sema.errNote(block, src, msg, "{s}", .{reason.needed_comptime_reason});
   2227 
   2228         if (reason.block_comptime_reason) |block_comptime_reason| {
   2229             try block_comptime_reason.explain(sema, msg);
   2230         }
   2231         break :msg msg;
   2232     };
   2233     return sema.failWithOwnedErrorMsg(block, msg);
   2234 }
   2235 
   2236 fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
   2237     return sema.fail(block, src, "use of undefined value here causes undefined behavior", .{});
   2238 }
   2239 
   2240 fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
   2241     return sema.fail(block, src, "division by zero here causes undefined behavior", .{});
   2242 }
   2243 
   2244 fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError {
   2245     return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{
   2246         lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod),
   2247     });
   2248 }
   2249 
   2250 fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, optional_ty: Type) CompileError {
   2251     return sema.fail(block, src, "expected optional type, found '{}'", .{optional_ty.fmt(sema.mod)});
   2252 }
   2253 
   2254 fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
   2255     const mod = sema.mod;
   2256     const msg = msg: {
   2257         const msg = try sema.errMsg(block, src, "type '{}' does not support array initialization syntax", .{
   2258             ty.fmt(mod),
   2259         });
   2260         errdefer msg.destroy(sema.gpa);
   2261         if (ty.isSlice(mod)) {
   2262             try sema.errNote(block, src, msg, "inferred array length is specified with an underscore: '[_]{}'", .{ty.elemType2(mod).fmt(mod)});
   2263         }
   2264         break :msg msg;
   2265     };
   2266     return sema.failWithOwnedErrorMsg(block, msg);
   2267 }
   2268 
   2269 fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
   2270     return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{
   2271         ty.fmt(sema.mod),
   2272     });
   2273 }
   2274 
   2275 fn failWithErrorSetCodeMissing(
   2276     sema: *Sema,
   2277     block: *Block,
   2278     src: LazySrcLoc,
   2279     dest_err_set_ty: Type,
   2280     src_err_set_ty: Type,
   2281 ) CompileError {
   2282     return sema.fail(block, src, "expected type '{}', found type '{}'", .{
   2283         dest_err_set_ty.fmt(sema.mod), src_err_set_ty.fmt(sema.mod),
   2284     });
   2285 }
   2286 
   2287 fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: usize) CompileError {
   2288     const mod = sema.mod;
   2289     if (int_ty.zigTypeTag(mod) == .Vector) {
   2290         const msg = msg: {
   2291             const msg = try sema.errMsg(block, src, "overflow of vector type '{}' with value '{}'", .{
   2292                 int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod),
   2293             });
   2294             errdefer msg.destroy(sema.gpa);
   2295             try sema.errNote(block, src, msg, "when computing vector element at index '{d}'", .{vector_index});
   2296             break :msg msg;
   2297         };
   2298         return sema.failWithOwnedErrorMsg(block, msg);
   2299     }
   2300     return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{
   2301         int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod),
   2302     });
   2303 }
   2304 
   2305 fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazySrcLoc, container_ty: Type, field_index: usize) CompileError {
   2306     const mod = sema.mod;
   2307     const msg = msg: {
   2308         const msg = try sema.errMsg(block, init_src, "value stored in comptime field does not match the default value of the field", .{});
   2309         errdefer msg.destroy(sema.gpa);
   2310 
   2311         const struct_type = mod.typeToStruct(container_ty) orelse break :msg msg;
   2312         const default_value_src = mod.fieldSrcLoc(struct_type.decl.unwrap().?, .{
   2313             .index = field_index,
   2314             .range = .value,
   2315         });
   2316         try mod.errNoteNonLazy(default_value_src, msg, "default value set here", .{});
   2317         break :msg msg;
   2318     };
   2319     return sema.failWithOwnedErrorMsg(block, msg);
   2320 }
   2321 
   2322 fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
   2323     const msg = msg: {
   2324         const msg = try sema.errMsg(block, src, "async has not been implemented in the self-hosted compiler yet", .{});
   2325         errdefer msg.destroy(sema.gpa);
   2326         break :msg msg;
   2327     };
   2328     return sema.failWithOwnedErrorMsg(block, msg);
   2329 }
   2330 
   2331 fn failWithInvalidFieldAccess(
   2332     sema: *Sema,
   2333     block: *Block,
   2334     src: LazySrcLoc,
   2335     object_ty: Type,
   2336     field_name: InternPool.NullTerminatedString,
   2337 ) CompileError {
   2338     const mod = sema.mod;
   2339     const inner_ty = if (object_ty.isSinglePointer(mod)) object_ty.childType(mod) else object_ty;
   2340 
   2341     if (inner_ty.zigTypeTag(mod) == .Optional) opt: {
   2342         const child_ty = inner_ty.optionalChild(mod);
   2343         if (!typeSupportsFieldAccess(mod, child_ty, field_name)) break :opt;
   2344         const msg = msg: {
   2345             const msg = try sema.errMsg(block, src, "optional type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
   2346             errdefer msg.destroy(sema.gpa);
   2347             try sema.errNote(block, src, msg, "consider using '.?', 'orelse', or 'if'", .{});
   2348             break :msg msg;
   2349         };
   2350         return sema.failWithOwnedErrorMsg(block, msg);
   2351     } else if (inner_ty.zigTypeTag(mod) == .ErrorUnion) err: {
   2352         const child_ty = inner_ty.errorUnionPayload(mod);
   2353         if (!typeSupportsFieldAccess(mod, child_ty, field_name)) break :err;
   2354         const msg = msg: {
   2355             const msg = try sema.errMsg(block, src, "error union type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
   2356             errdefer msg.destroy(sema.gpa);
   2357             try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{});
   2358             break :msg msg;
   2359         };
   2360         return sema.failWithOwnedErrorMsg(block, msg);
   2361     }
   2362     return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
   2363 }
   2364 
   2365 fn typeSupportsFieldAccess(mod: *const Module, ty: Type, field_name: InternPool.NullTerminatedString) bool {
   2366     const ip = &mod.intern_pool;
   2367     switch (ty.zigTypeTag(mod)) {
   2368         .Array => return ip.stringEqlSlice(field_name, "len"),
   2369         .Pointer => {
   2370             const ptr_info = ty.ptrInfo(mod);
   2371             if (ptr_info.flags.size == .Slice) {
   2372                 return ip.stringEqlSlice(field_name, "ptr") or ip.stringEqlSlice(field_name, "len");
   2373             } else if (Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Array) {
   2374                 return ip.stringEqlSlice(field_name, "len");
   2375             } else return false;
   2376         },
   2377         .Type, .Struct, .Union => return true,
   2378         else => return false,
   2379     }
   2380 }
   2381 
   2382 /// We don't return a pointer to the new error note because the pointer
   2383 /// becomes invalid when you add another one.
   2384 fn errNote(
   2385     sema: *Sema,
   2386     block: *Block,
   2387     src: LazySrcLoc,
   2388     parent: *Module.ErrorMsg,
   2389     comptime format: []const u8,
   2390     args: anytype,
   2391 ) error{OutOfMemory}!void {
   2392     const mod = sema.mod;
   2393     const src_decl = mod.declPtr(block.src_decl);
   2394     return mod.errNoteNonLazy(src.toSrcLoc(src_decl, mod), parent, format, args);
   2395 }
   2396 
   2397 fn addFieldErrNote(
   2398     sema: *Sema,
   2399     container_ty: Type,
   2400     field_index: usize,
   2401     parent: *Module.ErrorMsg,
   2402     comptime format: []const u8,
   2403     args: anytype,
   2404 ) !void {
   2405     @setCold(true);
   2406     const mod = sema.mod;
   2407     const decl_index = container_ty.getOwnerDecl(mod);
   2408     const decl = mod.declPtr(decl_index);
   2409 
   2410     const field_src = blk: {
   2411         const tree = decl.getFileScope(mod).getTree(sema.gpa) catch |err| {
   2412             log.err("unable to load AST to report compile error: {s}", .{@errorName(err)});
   2413             break :blk decl.srcLoc(mod);
   2414         };
   2415 
   2416         const container_node = decl.relativeToNodeIndex(0);
   2417         const node_tags = tree.nodes.items(.tag);
   2418         var buf: [2]std.zig.Ast.Node.Index = undefined;
   2419         const container_decl = tree.fullContainerDecl(&buf, container_node) orelse break :blk decl.srcLoc(mod);
   2420 
   2421         var it_index: usize = 0;
   2422         for (container_decl.ast.members) |member_node| {
   2423             switch (node_tags[member_node]) {
   2424                 .container_field_init,
   2425                 .container_field_align,
   2426                 .container_field,
   2427                 => {
   2428                     if (it_index == field_index) {
   2429                         break :blk decl.nodeOffsetSrcLoc(decl.nodeIndexToRelative(member_node), mod);
   2430                     }
   2431                     it_index += 1;
   2432                 },
   2433                 else => continue,
   2434             }
   2435         }
   2436         unreachable;
   2437     };
   2438     try mod.errNoteNonLazy(field_src, parent, format, args);
   2439 }
   2440 
   2441 fn errMsg(
   2442     sema: *Sema,
   2443     block: *Block,
   2444     src: LazySrcLoc,
   2445     comptime format: []const u8,
   2446     args: anytype,
   2447 ) error{ NeededSourceLocation, OutOfMemory }!*Module.ErrorMsg {
   2448     const mod = sema.mod;
   2449     if (src == .unneeded) return error.NeededSourceLocation;
   2450     const src_decl = mod.declPtr(block.src_decl);
   2451     return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl, mod), format, args);
   2452 }
   2453 
   2454 pub fn fail(
   2455     sema: *Sema,
   2456     block: *Block,
   2457     src: LazySrcLoc,
   2458     comptime format: []const u8,
   2459     args: anytype,
   2460 ) CompileError {
   2461     const err_msg = try sema.errMsg(block, src, format, args);
   2462     return sema.failWithOwnedErrorMsg(block, err_msg);
   2463 }
   2464 
   2465 fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Module.ErrorMsg) error{ AnalysisFail, OutOfMemory } {
   2466     @setCold(true);
   2467     const gpa = sema.gpa;
   2468     const mod = sema.mod;
   2469 
   2470     ref: {
   2471         errdefer err_msg.destroy(gpa);
   2472 
   2473         if (crash_report.is_enabled and mod.comp.debug_compile_errors) {
   2474             var wip_errors: std.zig.ErrorBundle.Wip = undefined;
   2475             wip_errors.init(gpa) catch unreachable;
   2476             Compilation.addModuleErrorMsg(mod, &wip_errors, err_msg.*) catch unreachable;
   2477             std.debug.print("compile error during Sema:\n", .{});
   2478             var error_bundle = wip_errors.toOwnedBundle("") catch unreachable;
   2479             error_bundle.renderToStdErr(.{ .ttyconf = .no_color });
   2480             crash_report.compilerPanic("unexpected compile error occurred", null, null);
   2481         }
   2482 
   2483         try mod.failed_decls.ensureUnusedCapacity(gpa, 1);
   2484         try mod.failed_files.ensureUnusedCapacity(gpa, 1);
   2485 
   2486         if (block) |start_block| {
   2487             var block_it = start_block;
   2488             while (block_it.inlining) |inlining| {
   2489                 try sema.errNote(
   2490                     inlining.call_block,
   2491                     inlining.call_src,
   2492                     err_msg,
   2493                     "called from here",
   2494                     .{},
   2495                 );
   2496                 block_it = inlining.call_block;
   2497             }
   2498 
   2499             const max_references = refs: {
   2500                 if (mod.comp.reference_trace) |num| break :refs num;
   2501                 // Do not add multiple traces without explicit request.
   2502                 if (mod.failed_decls.count() > 0) break :ref;
   2503                 break :refs default_reference_trace_len;
   2504             };
   2505 
   2506             var referenced_by = if (sema.owner_func_index != .none)
   2507                 mod.funcOwnerDeclIndex(sema.owner_func_index)
   2508             else
   2509                 sema.owner_decl_index;
   2510             var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(gpa);
   2511             defer reference_stack.deinit();
   2512 
   2513             // Avoid infinite loops.
   2514             var seen = std.AutoHashMap(InternPool.DeclIndex, void).init(gpa);
   2515             defer seen.deinit();
   2516 
   2517             while (mod.reference_table.get(referenced_by)) |ref| {
   2518                 const gop = try seen.getOrPut(ref.referencer);
   2519                 if (gop.found_existing) break;
   2520                 if (reference_stack.items.len < max_references) {
   2521                     const decl = mod.declPtr(ref.referencer);
   2522                     try reference_stack.append(.{
   2523                         .decl = decl.name,
   2524                         .src_loc = ref.src.toSrcLoc(decl, mod),
   2525                     });
   2526                 }
   2527                 referenced_by = ref.referencer;
   2528             }
   2529             err_msg.reference_trace = try reference_stack.toOwnedSlice();
   2530             err_msg.hidden_references = @intCast(seen.count() -| max_references);
   2531         }
   2532     }
   2533     const ip = &mod.intern_pool;
   2534     if (sema.owner_func_index != .none) {
   2535         ip.funcAnalysis(sema.owner_func_index).state = .sema_failure;
   2536     } else {
   2537         sema.owner_decl.analysis = .sema_failure;
   2538         sema.owner_decl.generation = mod.generation;
   2539     }
   2540     if (sema.func_index != .none) {
   2541         ip.funcAnalysis(sema.func_index).state = .sema_failure;
   2542     }
   2543     const gop = mod.failed_decls.getOrPutAssumeCapacity(sema.owner_decl_index);
   2544     if (gop.found_existing) {
   2545         // If there are multiple errors for the same Decl, prefer the first one added.
   2546         sema.err = null;
   2547         err_msg.destroy(gpa);
   2548     } else {
   2549         sema.err = err_msg;
   2550         gop.value_ptr.* = err_msg;
   2551     }
   2552     return error.AnalysisFail;
   2553 }
   2554 
   2555 /// Given an ErrorMsg, modify its message and source location to the given values, turning the
   2556 /// original message into a note. Notes on the original message are preserved as further notes.
   2557 /// Reference trace is preserved.
   2558 fn reparentOwnedErrorMsg(
   2559     sema: *Sema,
   2560     block: *Block,
   2561     src: LazySrcLoc,
   2562     msg: *Module.ErrorMsg,
   2563     comptime format: []const u8,
   2564     args: anytype,
   2565 ) !void {
   2566     const mod = sema.mod;
   2567     const src_decl = mod.declPtr(block.src_decl);
   2568     const resolved_src = src.toSrcLoc(src_decl, mod);
   2569     const msg_str = try std.fmt.allocPrint(mod.gpa, format, args);
   2570 
   2571     const orig_notes = msg.notes.len;
   2572     msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1);
   2573     std.mem.copyBackwards(Module.ErrorMsg, msg.notes[1..], msg.notes[0..orig_notes]);
   2574     msg.notes[0] = .{
   2575         .src_loc = msg.src_loc,
   2576         .msg = msg.msg,
   2577     };
   2578 
   2579     msg.src_loc = resolved_src;
   2580     msg.msg = msg_str;
   2581 }
   2582 
   2583 const align_ty = Type.u29;
   2584 
   2585 fn analyzeAsAlign(
   2586     sema: *Sema,
   2587     block: *Block,
   2588     src: LazySrcLoc,
   2589     air_ref: Air.Inst.Ref,
   2590 ) !Alignment {
   2591     const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty, .{
   2592         .needed_comptime_reason = "alignment must be comptime-known",
   2593     });
   2594     return sema.validateAlign(block, src, alignment_big);
   2595 }
   2596 
   2597 fn validateAlign(
   2598     sema: *Sema,
   2599     block: *Block,
   2600     src: LazySrcLoc,
   2601     alignment: u64,
   2602 ) !Alignment {
   2603     const result = try validateAlignAllowZero(sema, block, src, alignment);
   2604     if (result == .none) return sema.fail(block, src, "alignment must be >= 1", .{});
   2605     return result;
   2606 }
   2607 
   2608 fn validateAlignAllowZero(
   2609     sema: *Sema,
   2610     block: *Block,
   2611     src: LazySrcLoc,
   2612     alignment: u64,
   2613 ) !Alignment {
   2614     if (alignment == 0) return .none;
   2615     if (!std.math.isPowerOfTwo(alignment)) {
   2616         return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{
   2617             alignment,
   2618         });
   2619     }
   2620     return Alignment.fromNonzeroByteUnits(alignment);
   2621 }
   2622 
   2623 pub fn resolveAlign(
   2624     sema: *Sema,
   2625     block: *Block,
   2626     src: LazySrcLoc,
   2627     zir_ref: Zir.Inst.Ref,
   2628 ) !Alignment {
   2629     const air_ref = try sema.resolveInst(zir_ref);
   2630     return sema.analyzeAsAlign(block, src, air_ref);
   2631 }
   2632 
   2633 fn resolveInt(
   2634     sema: *Sema,
   2635     block: *Block,
   2636     src: LazySrcLoc,
   2637     zir_ref: Zir.Inst.Ref,
   2638     dest_ty: Type,
   2639     reason: NeededComptimeReason,
   2640 ) !u64 {
   2641     const air_ref = try sema.resolveInst(zir_ref);
   2642     return sema.analyzeAsInt(block, src, air_ref, dest_ty, reason);
   2643 }
   2644 
   2645 fn analyzeAsInt(
   2646     sema: *Sema,
   2647     block: *Block,
   2648     src: LazySrcLoc,
   2649     air_ref: Air.Inst.Ref,
   2650     dest_ty: Type,
   2651     reason: NeededComptimeReason,
   2652 ) !u64 {
   2653     const mod = sema.mod;
   2654     const coerced = try sema.coerce(block, dest_ty, air_ref, src);
   2655     const val = try sema.resolveConstDefinedValue(block, src, coerced, reason);
   2656     return (try val.getUnsignedIntAdvanced(mod, sema)).?;
   2657 }
   2658 
   2659 pub fn getStructType(
   2660     sema: *Sema,
   2661     decl: InternPool.DeclIndex,
   2662     namespace: InternPool.NamespaceIndex,
   2663     zir_index: Zir.Inst.Index,
   2664 ) !InternPool.Index {
   2665     const mod = sema.mod;
   2666     const gpa = sema.gpa;
   2667     const ip = &mod.intern_pool;
   2668     const extended = sema.code.instructions.items(.data)[@intFromEnum(zir_index)].extended;
   2669     assert(extended.opcode == .struct_decl);
   2670     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
   2671 
   2672     var extra_index: usize = extended.operand;
   2673     extra_index += @intFromBool(small.has_src_node);
   2674     const fields_len = if (small.has_fields_len) blk: {
   2675         const fields_len = sema.code.extra[extra_index];
   2676         extra_index += 1;
   2677         break :blk fields_len;
   2678     } else 0;
   2679     const decls_len = if (small.has_decls_len) blk: {
   2680         const decls_len = sema.code.extra[extra_index];
   2681         extra_index += 1;
   2682         break :blk decls_len;
   2683     } else 0;
   2684 
   2685     if (small.has_backing_int) {
   2686         const backing_int_body_len = sema.code.extra[extra_index];
   2687         extra_index += 1; // backing_int_body_len
   2688         if (backing_int_body_len == 0) {
   2689             extra_index += 1; // backing_int_ref
   2690         } else {
   2691             extra_index += backing_int_body_len; // backing_int_body_inst
   2692         }
   2693     }
   2694 
   2695     extra_index = try mod.scanNamespace(namespace, extra_index, decls_len, mod.declPtr(decl));
   2696 
   2697     const ty = try ip.getStructType(gpa, .{
   2698         .decl = decl,
   2699         .namespace = namespace.toOptional(),
   2700         .zir_index = zir_index,
   2701         .layout = small.layout,
   2702         .known_non_opv = small.known_non_opv,
   2703         .is_tuple = small.is_tuple,
   2704         .fields_len = fields_len,
   2705         .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
   2706         .any_default_inits = small.any_default_inits,
   2707         .any_comptime_fields = small.any_comptime_fields,
   2708         .inits_resolved = false,
   2709         .any_aligned_fields = small.any_aligned_fields,
   2710     });
   2711 
   2712     return ty;
   2713 }
   2714 
   2715 fn zirStructDecl(
   2716     sema: *Sema,
   2717     block: *Block,
   2718     extended: Zir.Inst.Extended.InstData,
   2719     inst: Zir.Inst.Index,
   2720 ) CompileError!Air.Inst.Ref {
   2721     const mod = sema.mod;
   2722     const ip = &mod.intern_pool;
   2723     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
   2724     const src: LazySrcLoc = if (small.has_src_node) blk: {
   2725         const node_offset: i32 = @bitCast(sema.code.extra[extended.operand]);
   2726         break :blk LazySrcLoc.nodeOffset(node_offset);
   2727     } else sema.src;
   2728 
   2729     // Because these three things each reference each other, `undefined`
   2730     // placeholders are used before being set after the struct type gains an
   2731     // InternPool index.
   2732 
   2733     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
   2734         .ty = Type.noreturn,
   2735         .val = Value.@"unreachable",
   2736     }, small.name_strategy, "struct", inst);
   2737     const new_decl = mod.declPtr(new_decl_index);
   2738     new_decl.owns_tv = true;
   2739     errdefer mod.abortAnonDecl(new_decl_index);
   2740 
   2741     const new_namespace_index = try mod.createNamespace(.{
   2742         .parent = block.namespace.toOptional(),
   2743         .ty = undefined,
   2744         .file_scope = block.getFileScope(mod),
   2745     });
   2746     const new_namespace = mod.namespacePtr(new_namespace_index);
   2747     errdefer mod.destroyNamespace(new_namespace_index);
   2748 
   2749     const struct_ty = ty: {
   2750         const ty = try sema.getStructType(new_decl_index, new_namespace_index, inst);
   2751         if (sema.builtin_type_target_index != .none) {
   2752             ip.resolveBuiltinType(sema.builtin_type_target_index, ty);
   2753             break :ty sema.builtin_type_target_index;
   2754         }
   2755         break :ty ty;
   2756     };
   2757     // TODO: figure out InternPool removals for incremental compilation
   2758     //errdefer ip.remove(struct_ty);
   2759 
   2760     new_decl.ty = Type.type;
   2761     new_decl.val = Value.fromInterned(struct_ty);
   2762     new_namespace.ty = Type.fromInterned(struct_ty);
   2763 
   2764     const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
   2765     try mod.finalizeAnonDecl(new_decl_index);
   2766     return decl_val;
   2767 }
   2768 
   2769 fn createAnonymousDeclTypeNamed(
   2770     sema: *Sema,
   2771     block: *Block,
   2772     src: LazySrcLoc,
   2773     typed_value: TypedValue,
   2774     name_strategy: Zir.Inst.NameStrategy,
   2775     anon_prefix: []const u8,
   2776     inst: ?Zir.Inst.Index,
   2777 ) !InternPool.DeclIndex {
   2778     const mod = sema.mod;
   2779     const ip = &mod.intern_pool;
   2780     const gpa = sema.gpa;
   2781     const namespace = block.namespace;
   2782     const src_scope = block.wip_capture_scope;
   2783     const src_decl = mod.declPtr(block.src_decl);
   2784     const src_node = src_decl.relativeToNodeIndex(src.node_offset.x);
   2785     const new_decl_index = try mod.allocateNewDecl(namespace, src_node, src_scope);
   2786     errdefer mod.destroyDecl(new_decl_index);
   2787 
   2788     switch (name_strategy) {
   2789         .anon => {
   2790             // It would be neat to have "struct:line:column" but this name has
   2791             // to survive incremental updates, where it may have been shifted down
   2792             // or up to a different line, but unchanged, and thus not unnecessarily
   2793             // semantically analyzed.
   2794             // This name is also used as the key in the parent namespace so it cannot be
   2795             // renamed.
   2796 
   2797             const name = mod.intern_pool.getOrPutStringFmt(gpa, "{}__{s}_{d}", .{
   2798                 src_decl.name.fmt(&mod.intern_pool), anon_prefix, @intFromEnum(new_decl_index),
   2799             }) catch unreachable;
   2800             try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, typed_value, name);
   2801             return new_decl_index;
   2802         },
   2803         .parent => {
   2804             const name = mod.declPtr(block.src_decl).name;
   2805             try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, typed_value, name);
   2806             return new_decl_index;
   2807         },
   2808         .func => {
   2809             const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index));
   2810             const zir_tags = sema.code.instructions.items(.tag);
   2811 
   2812             var buf = std.ArrayList(u8).init(gpa);
   2813             defer buf.deinit();
   2814 
   2815             const writer = buf.writer();
   2816             try writer.print("{}(", .{mod.declPtr(block.src_decl).name.fmt(&mod.intern_pool)});
   2817 
   2818             var arg_i: usize = 0;
   2819             for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) {
   2820                 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
   2821                     const arg = sema.inst_map.get(zir_inst).?;
   2822                     // If this is being called in a generic function then analyzeCall will
   2823                     // have already resolved the args and this will work.
   2824                     // If not then this is a struct type being returned from a non-generic
   2825                     // function and the name doesn't matter since it will later
   2826                     // result in a compile error.
   2827                     const arg_val = sema.resolveConstValue(block, .unneeded, arg, undefined) catch
   2828                         return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null);
   2829 
   2830                     if (arg_i != 0) try writer.writeByte(',');
   2831                     try writer.print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)});
   2832 
   2833                     arg_i += 1;
   2834                     continue;
   2835                 },
   2836                 else => continue,
   2837             };
   2838 
   2839             try writer.writeByte(')');
   2840             const name = try mod.intern_pool.getOrPutString(gpa, buf.items);
   2841             try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, typed_value, name);
   2842             return new_decl_index;
   2843         },
   2844         .dbg_var => {
   2845             const ref = inst.?.toRef();
   2846             const zir_tags = sema.code.instructions.items(.tag);
   2847             const zir_data = sema.code.instructions.items(.data);
   2848             for (@intFromEnum(inst.?)..zir_tags.len) |i| switch (zir_tags[i]) {
   2849                 .dbg_var_ptr, .dbg_var_val => {
   2850                     if (zir_data[i].str_op.operand != ref) continue;
   2851 
   2852                     const name = try mod.intern_pool.getOrPutStringFmt(gpa, "{}.{s}", .{
   2853                         src_decl.name.fmt(&mod.intern_pool), zir_data[i].str_op.getStr(sema.code),
   2854                     });
   2855 
   2856                     try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, typed_value, name);
   2857                     return new_decl_index;
   2858                 },
   2859                 else => {},
   2860             };
   2861             return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null);
   2862         },
   2863     }
   2864 }
   2865 
   2866 fn zirEnumDecl(
   2867     sema: *Sema,
   2868     block: *Block,
   2869     extended: Zir.Inst.Extended.InstData,
   2870     inst: Zir.Inst.Index,
   2871 ) CompileError!Air.Inst.Ref {
   2872     const tracy = trace(@src());
   2873     defer tracy.end();
   2874 
   2875     const mod = sema.mod;
   2876     const gpa = sema.gpa;
   2877     const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
   2878     var extra_index: usize = extended.operand;
   2879 
   2880     const src: LazySrcLoc = if (small.has_src_node) blk: {
   2881         const node_offset: i32 = @bitCast(sema.code.extra[extra_index]);
   2882         extra_index += 1;
   2883         break :blk LazySrcLoc.nodeOffset(node_offset);
   2884     } else sema.src;
   2885     const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x };
   2886 
   2887     const tag_type_ref = if (small.has_tag_type) blk: {
   2888         const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   2889         extra_index += 1;
   2890         break :blk tag_type_ref;
   2891     } else .none;
   2892 
   2893     const body_len = if (small.has_body_len) blk: {
   2894         const body_len = sema.code.extra[extra_index];
   2895         extra_index += 1;
   2896         break :blk body_len;
   2897     } else 0;
   2898 
   2899     const fields_len = if (small.has_fields_len) blk: {
   2900         const fields_len = sema.code.extra[extra_index];
   2901         extra_index += 1;
   2902         break :blk fields_len;
   2903     } else 0;
   2904 
   2905     const decls_len = if (small.has_decls_len) blk: {
   2906         const decls_len = sema.code.extra[extra_index];
   2907         extra_index += 1;
   2908         break :blk decls_len;
   2909     } else 0;
   2910 
   2911     // Because these three things each reference each other, `undefined`
   2912     // placeholders are used before being set after the enum type gains an
   2913     // InternPool index.
   2914 
   2915     var done = false;
   2916     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
   2917         .ty = Type.noreturn,
   2918         .val = Value.@"unreachable",
   2919     }, small.name_strategy, "enum", inst);
   2920     const new_decl = mod.declPtr(new_decl_index);
   2921     new_decl.owns_tv = true;
   2922     errdefer if (!done) mod.abortAnonDecl(new_decl_index);
   2923 
   2924     const new_namespace_index = try mod.createNamespace(.{
   2925         .parent = block.namespace.toOptional(),
   2926         .ty = undefined,
   2927         .file_scope = block.getFileScope(mod),
   2928     });
   2929     const new_namespace = mod.namespacePtr(new_namespace_index);
   2930     errdefer if (!done) mod.destroyNamespace(new_namespace_index);
   2931 
   2932     extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl);
   2933 
   2934     const body = sema.code.bodySlice(extra_index, body_len);
   2935     extra_index += body.len;
   2936 
   2937     const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
   2938     const body_end = extra_index;
   2939     extra_index += bit_bags_count;
   2940 
   2941     const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| {
   2942         if (bag != 0) break true;
   2943     } else false;
   2944 
   2945     const incomplete_enum = incomplete_enum: {
   2946         var incomplete_enum = try mod.intern_pool.getIncompleteEnum(gpa, .{
   2947             .decl = new_decl_index,
   2948             .namespace = new_namespace_index.toOptional(),
   2949             .fields_len = fields_len,
   2950             .has_values = any_values,
   2951             .tag_mode = if (small.nonexhaustive)
   2952                 .nonexhaustive
   2953             else if (tag_type_ref == .none)
   2954                 .auto
   2955             else
   2956                 .explicit,
   2957         });
   2958         if (sema.builtin_type_target_index != .none) {
   2959             mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, incomplete_enum.index);
   2960             incomplete_enum.index = sema.builtin_type_target_index;
   2961         }
   2962         break :incomplete_enum incomplete_enum;
   2963     };
   2964     // TODO: figure out InternPool removals for incremental compilation
   2965     //errdefer if (!done) mod.intern_pool.remove(incomplete_enum.index);
   2966 
   2967     new_decl.ty = Type.type;
   2968     new_decl.val = Value.fromInterned(incomplete_enum.index);
   2969     new_namespace.ty = Type.fromInterned(incomplete_enum.index);
   2970 
   2971     const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index);
   2972     try mod.finalizeAnonDecl(new_decl_index);
   2973     done = true;
   2974 
   2975     const int_tag_ty = ty: {
   2976         // We create a block for the field type instructions because they
   2977         // may need to reference Decls from inside the enum namespace.
   2978         // Within the field type, default value, and alignment expressions, the "owner decl"
   2979         // should be the enum itself.
   2980 
   2981         const prev_owner_decl = sema.owner_decl;
   2982         const prev_owner_decl_index = sema.owner_decl_index;
   2983         sema.owner_decl = new_decl;
   2984         sema.owner_decl_index = new_decl_index;
   2985         defer {
   2986             sema.owner_decl = prev_owner_decl;
   2987             sema.owner_decl_index = prev_owner_decl_index;
   2988         }
   2989 
   2990         const prev_owner_func_index = sema.owner_func_index;
   2991         sema.owner_func_index = .none;
   2992         defer sema.owner_func_index = prev_owner_func_index;
   2993 
   2994         const prev_func_index = sema.func_index;
   2995         sema.func_index = .none;
   2996         defer sema.func_index = prev_func_index;
   2997 
   2998         var enum_block: Block = .{
   2999             .parent = null,
   3000             .sema = sema,
   3001             .src_decl = new_decl_index,
   3002             .namespace = new_namespace_index,
   3003             .wip_capture_scope = try mod.createCaptureScope(new_decl.src_scope),
   3004             .instructions = .{},
   3005             .inlining = null,
   3006             .is_comptime = true,
   3007         };
   3008         defer enum_block.instructions.deinit(sema.gpa);
   3009 
   3010         if (body.len != 0) {
   3011             try sema.analyzeBody(&enum_block, body);
   3012         }
   3013 
   3014         if (tag_type_ref != .none) {
   3015             const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref);
   3016             if (ty.zigTypeTag(mod) != .Int and ty.zigTypeTag(mod) != .ComptimeInt) {
   3017                 return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)});
   3018             }
   3019             incomplete_enum.setTagType(&mod.intern_pool, ty.toIntern());
   3020             break :ty ty;
   3021         } else if (fields_len == 0) {
   3022             break :ty try mod.intType(.unsigned, 0);
   3023         } else {
   3024             const bits = std.math.log2_int_ceil(usize, fields_len);
   3025             break :ty try mod.intType(.unsigned, bits);
   3026         }
   3027     };
   3028 
   3029     if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
   3030         if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(mod)) {
   3031             return sema.fail(block, src, "non-exhaustive enum specifies every value", .{});
   3032         }
   3033     }
   3034 
   3035     var bit_bag_index: usize = body_end;
   3036     var cur_bit_bag: u32 = undefined;
   3037     var field_i: u32 = 0;
   3038     var last_tag_val: ?Value = null;
   3039     while (field_i < fields_len) : (field_i += 1) {
   3040         if (field_i % 32 == 0) {
   3041             cur_bit_bag = sema.code.extra[bit_bag_index];
   3042             bit_bag_index += 1;
   3043         }
   3044         const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0;
   3045         cur_bit_bag >>= 1;
   3046 
   3047         const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
   3048         extra_index += 1;
   3049 
   3050         // doc comment
   3051         extra_index += 1;
   3052 
   3053         const field_name = try mod.intern_pool.getOrPutString(gpa, field_name_zir);
   3054         if (incomplete_enum.addFieldName(&mod.intern_pool, field_name)) |other_index| {
   3055             const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy;
   3056             const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
   3057             const msg = msg: {
   3058                 const msg = try sema.errMsg(block, field_src, "duplicate enum field '{s}'", .{field_name_zir});
   3059                 errdefer msg.destroy(gpa);
   3060                 try sema.errNote(block, other_field_src, msg, "other field here", .{});
   3061                 break :msg msg;
   3062             };
   3063             return sema.failWithOwnedErrorMsg(block, msg);
   3064         }
   3065 
   3066         const tag_overflow = if (has_tag_value) overflow: {
   3067             const tag_val_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   3068             extra_index += 1;
   3069             const tag_inst = try sema.resolveInst(tag_val_ref);
   3070             last_tag_val = sema.resolveConstDefinedValue(block, .unneeded, tag_inst, undefined) catch |err| switch (err) {
   3071                 error.NeededSourceLocation => {
   3072                     const value_src = mod.fieldSrcLoc(new_decl_index, .{
   3073                         .index = field_i,
   3074                         .range = .value,
   3075                     }).lazy;
   3076                     _ = try sema.resolveConstDefinedValue(block, value_src, tag_inst, .{
   3077                         .needed_comptime_reason = "enum tag value must be comptime-known",
   3078                     });
   3079                     unreachable;
   3080                 },
   3081                 else => |e| return e,
   3082             };
   3083             if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
   3084             last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty);
   3085             if (incomplete_enum.addFieldValue(&mod.intern_pool, last_tag_val.?.toIntern())) |other_index| {
   3086                 const value_src = mod.fieldSrcLoc(new_decl_index, .{
   3087                     .index = field_i,
   3088                     .range = .value,
   3089                 }).lazy;
   3090                 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
   3091                 const msg = msg: {
   3092                     const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)});
   3093                     errdefer msg.destroy(gpa);
   3094                     try sema.errNote(block, other_field_src, msg, "other occurrence here", .{});
   3095                     break :msg msg;
   3096                 };
   3097                 return sema.failWithOwnedErrorMsg(block, msg);
   3098             }
   3099             break :overflow false;
   3100         } else if (any_values) overflow: {
   3101             var overflow: ?usize = null;
   3102             last_tag_val = if (last_tag_val) |val|
   3103                 try sema.intAdd(val, try mod.intValue(int_tag_ty, 1), int_tag_ty, &overflow)
   3104             else
   3105                 try mod.intValue(int_tag_ty, 0);
   3106             if (overflow != null) break :overflow true;
   3107             if (incomplete_enum.addFieldValue(&mod.intern_pool, last_tag_val.?.toIntern())) |other_index| {
   3108                 const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy;
   3109                 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
   3110                 const msg = msg: {
   3111                     const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)});
   3112                     errdefer msg.destroy(gpa);
   3113                     try sema.errNote(block, other_field_src, msg, "other occurrence here", .{});
   3114                     break :msg msg;
   3115                 };
   3116                 return sema.failWithOwnedErrorMsg(block, msg);
   3117             }
   3118             break :overflow false;
   3119         } else overflow: {
   3120             last_tag_val = try mod.intValue(Type.comptime_int, field_i);
   3121             if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true;
   3122             last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty);
   3123             break :overflow false;
   3124         };
   3125 
   3126         if (tag_overflow) {
   3127             const value_src = mod.fieldSrcLoc(new_decl_index, .{
   3128                 .index = field_i,
   3129                 .range = if (has_tag_value) .value else .name,
   3130             }).lazy;
   3131             const msg = try sema.errMsg(block, value_src, "enumeration value '{}' too large for type '{}'", .{
   3132                 last_tag_val.?.fmtValue(int_tag_ty, mod), int_tag_ty.fmt(mod),
   3133             });
   3134             return sema.failWithOwnedErrorMsg(block, msg);
   3135         }
   3136     }
   3137     return decl_val;
   3138 }
   3139 
   3140 fn zirUnionDecl(
   3141     sema: *Sema,
   3142     block: *Block,
   3143     extended: Zir.Inst.Extended.InstData,
   3144     inst: Zir.Inst.Index,
   3145 ) CompileError!Air.Inst.Ref {
   3146     const tracy = trace(@src());
   3147     defer tracy.end();
   3148 
   3149     const mod = sema.mod;
   3150     const gpa = sema.gpa;
   3151     const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
   3152     var extra_index: usize = extended.operand;
   3153 
   3154     const src: LazySrcLoc = if (small.has_src_node) blk: {
   3155         const node_offset: i32 = @bitCast(sema.code.extra[extra_index]);
   3156         extra_index += 1;
   3157         break :blk LazySrcLoc.nodeOffset(node_offset);
   3158     } else sema.src;
   3159 
   3160     extra_index += @intFromBool(small.has_tag_type);
   3161     extra_index += @intFromBool(small.has_body_len);
   3162     const fields_len = if (small.has_fields_len) blk: {
   3163         const fields_len = sema.code.extra[extra_index];
   3164         extra_index += 1;
   3165         break :blk fields_len;
   3166     } else 0;
   3167 
   3168     const decls_len = if (small.has_decls_len) blk: {
   3169         const decls_len = sema.code.extra[extra_index];
   3170         extra_index += 1;
   3171         break :blk decls_len;
   3172     } else 0;
   3173 
   3174     // Because these three things each reference each other, `undefined`
   3175     // placeholders are used before being set after the union type gains an
   3176     // InternPool index.
   3177 
   3178     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
   3179         .ty = Type.noreturn,
   3180         .val = Value.@"unreachable",
   3181     }, small.name_strategy, "union", inst);
   3182     const new_decl = mod.declPtr(new_decl_index);
   3183     new_decl.owns_tv = true;
   3184     errdefer mod.abortAnonDecl(new_decl_index);
   3185 
   3186     const new_namespace_index = try mod.createNamespace(.{
   3187         .parent = block.namespace.toOptional(),
   3188         .ty = undefined,
   3189         .file_scope = block.getFileScope(mod),
   3190     });
   3191     const new_namespace = mod.namespacePtr(new_namespace_index);
   3192     errdefer mod.destroyNamespace(new_namespace_index);
   3193 
   3194     const union_ty = ty: {
   3195         const ty = try mod.intern_pool.getUnionType(gpa, .{
   3196             .flags = .{
   3197                 .layout = small.layout,
   3198                 .status = .none,
   3199                 .runtime_tag = if (small.has_tag_type or small.auto_enum_tag)
   3200                     .tagged
   3201                 else if (small.layout != .Auto)
   3202                     .none
   3203                 else switch (block.wantSafety()) {
   3204                     true => .safety,
   3205                     false => .none,
   3206                 },
   3207                 .any_aligned_fields = small.any_aligned_fields,
   3208                 .requires_comptime = .unknown,
   3209                 .assumed_runtime_bits = false,
   3210                 .assumed_pointer_aligned = false,
   3211                 .alignment = .none,
   3212             },
   3213             .decl = new_decl_index,
   3214             .namespace = new_namespace_index,
   3215             .zir_index = inst,
   3216             .fields_len = fields_len,
   3217             .enum_tag_ty = .none,
   3218             .field_types = &.{},
   3219             .field_aligns = &.{},
   3220         });
   3221         if (sema.builtin_type_target_index != .none) {
   3222             mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, ty);
   3223             break :ty sema.builtin_type_target_index;
   3224         }
   3225         break :ty ty;
   3226     };
   3227     // TODO: figure out InternPool removals for incremental compilation
   3228     //errdefer mod.intern_pool.remove(union_ty);
   3229 
   3230     new_decl.ty = Type.type;
   3231     new_decl.val = Value.fromInterned(union_ty);
   3232     new_namespace.ty = Type.fromInterned(union_ty);
   3233 
   3234     _ = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl);
   3235 
   3236     const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
   3237     try mod.finalizeAnonDecl(new_decl_index);
   3238     return decl_val;
   3239 }
   3240 
   3241 fn zirOpaqueDecl(
   3242     sema: *Sema,
   3243     block: *Block,
   3244     extended: Zir.Inst.Extended.InstData,
   3245     inst: Zir.Inst.Index,
   3246 ) CompileError!Air.Inst.Ref {
   3247     const tracy = trace(@src());
   3248     defer tracy.end();
   3249 
   3250     const mod = sema.mod;
   3251     const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small);
   3252     var extra_index: usize = extended.operand;
   3253 
   3254     const src: LazySrcLoc = if (small.has_src_node) blk: {
   3255         const node_offset: i32 = @bitCast(sema.code.extra[extra_index]);
   3256         extra_index += 1;
   3257         break :blk LazySrcLoc.nodeOffset(node_offset);
   3258     } else sema.src;
   3259 
   3260     const decls_len = if (small.has_decls_len) blk: {
   3261         const decls_len = sema.code.extra[extra_index];
   3262         extra_index += 1;
   3263         break :blk decls_len;
   3264     } else 0;
   3265 
   3266     // Because these three things each reference each other, `undefined`
   3267     // placeholders are used in two places before being set after the opaque
   3268     // type gains an InternPool index.
   3269 
   3270     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
   3271         .ty = Type.noreturn,
   3272         .val = Value.@"unreachable",
   3273     }, small.name_strategy, "opaque", inst);
   3274     const new_decl = mod.declPtr(new_decl_index);
   3275     new_decl.owns_tv = true;
   3276     errdefer mod.abortAnonDecl(new_decl_index);
   3277 
   3278     const new_namespace_index = try mod.createNamespace(.{
   3279         .parent = block.namespace.toOptional(),
   3280         .ty = undefined,
   3281         .file_scope = block.getFileScope(mod),
   3282     });
   3283     const new_namespace = mod.namespacePtr(new_namespace_index);
   3284     errdefer mod.destroyNamespace(new_namespace_index);
   3285 
   3286     const opaque_ty = try mod.intern(.{ .opaque_type = .{
   3287         .decl = new_decl_index,
   3288         .namespace = new_namespace_index,
   3289     } });
   3290     // TODO: figure out InternPool removals for incremental compilation
   3291     //errdefer mod.intern_pool.remove(opaque_ty);
   3292 
   3293     new_decl.ty = Type.type;
   3294     new_decl.val = Value.fromInterned(opaque_ty);
   3295     new_namespace.ty = Type.fromInterned(opaque_ty);
   3296 
   3297     extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl);
   3298 
   3299     const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
   3300     try mod.finalizeAnonDecl(new_decl_index);
   3301     return decl_val;
   3302 }
   3303 
   3304 fn zirErrorSetDecl(
   3305     sema: *Sema,
   3306     block: *Block,
   3307     inst: Zir.Inst.Index,
   3308     name_strategy: Zir.Inst.NameStrategy,
   3309 ) CompileError!Air.Inst.Ref {
   3310     const tracy = trace(@src());
   3311     defer tracy.end();
   3312 
   3313     const mod = sema.mod;
   3314     const gpa = sema.gpa;
   3315     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   3316     const src = inst_data.src();
   3317     const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index);
   3318 
   3319     var names: InferredErrorSet.NameMap = .{};
   3320     try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len);
   3321 
   3322     var extra_index: u32 = @intCast(extra.end);
   3323     const extra_index_end = extra_index + (extra.data.fields_len * 2);
   3324     while (extra_index < extra_index_end) : (extra_index += 2) { // +2 to skip over doc_string
   3325         const str_index = sema.code.extra[extra_index];
   3326         const name = sema.code.nullTerminatedString(str_index);
   3327         const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
   3328         _ = try mod.getErrorValue(name_ip);
   3329         const result = names.getOrPutAssumeCapacity(name_ip);
   3330         assert(!result.found_existing); // verified in AstGen
   3331     }
   3332 
   3333     const error_set_ty = try mod.errorSetFromUnsortedNames(names.keys());
   3334 
   3335     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
   3336         .ty = Type.type,
   3337         .val = error_set_ty.toValue(),
   3338     }, name_strategy, "error", inst);
   3339     const new_decl = mod.declPtr(new_decl_index);
   3340     new_decl.owns_tv = true;
   3341     errdefer mod.abortAnonDecl(new_decl_index);
   3342 
   3343     const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
   3344     try mod.finalizeAnonDecl(new_decl_index);
   3345     return decl_val;
   3346 }
   3347 
   3348 fn zirRetPtr(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
   3349     const tracy = trace(@src());
   3350     defer tracy.end();
   3351 
   3352     if (block.is_comptime or try sema.typeRequiresComptime(sema.fn_ret_ty)) {
   3353         try sema.resolveTypeFields(sema.fn_ret_ty);
   3354         return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty, .none);
   3355     }
   3356 
   3357     const target = sema.mod.getTarget();
   3358     const ptr_type = try sema.ptrType(.{
   3359         .child = sema.fn_ret_ty.toIntern(),
   3360         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   3361     });
   3362 
   3363     if (block.inlining != null) {
   3364         // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr.
   3365         // TODO when functions gain result location support, the inlining struct in
   3366         // Block should contain the return pointer, and we would pass that through here.
   3367         try sema.queueFullTypeResolution(sema.fn_ret_ty);
   3368         return block.addTy(.alloc, ptr_type);
   3369     }
   3370 
   3371     return block.addTy(.ret_ptr, ptr_type);
   3372 }
   3373 
   3374 fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3375     const tracy = trace(@src());
   3376     defer tracy.end();
   3377 
   3378     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
   3379     const operand = try sema.resolveInst(inst_data.operand);
   3380     return sema.analyzeRef(block, inst_data.src(), operand);
   3381 }
   3382 
   3383 fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3384     const tracy = trace(@src());
   3385     defer tracy.end();
   3386 
   3387     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3388     const operand = try sema.resolveInst(inst_data.operand);
   3389     const src = inst_data.src();
   3390 
   3391     return sema.ensureResultUsed(block, sema.typeOf(operand), src);
   3392 }
   3393 
   3394 fn ensureResultUsed(
   3395     sema: *Sema,
   3396     block: *Block,
   3397     ty: Type,
   3398     src: LazySrcLoc,
   3399 ) CompileError!void {
   3400     const mod = sema.mod;
   3401     switch (ty.zigTypeTag(mod)) {
   3402         .Void, .NoReturn => return,
   3403         .ErrorSet, .ErrorUnion => {
   3404             const msg = msg: {
   3405                 const msg = try sema.errMsg(block, src, "error is ignored", .{});
   3406                 errdefer msg.destroy(sema.gpa);
   3407                 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{});
   3408                 break :msg msg;
   3409             };
   3410             return sema.failWithOwnedErrorMsg(block, msg);
   3411         },
   3412         else => {
   3413             const msg = msg: {
   3414                 const msg = try sema.errMsg(block, src, "value of type '{}' ignored", .{ty.fmt(sema.mod)});
   3415                 errdefer msg.destroy(sema.gpa);
   3416                 try sema.errNote(block, src, msg, "all non-void values must be used", .{});
   3417                 try sema.errNote(block, src, msg, "this error can be suppressed by assigning the value to '_'", .{});
   3418                 break :msg msg;
   3419             };
   3420             return sema.failWithOwnedErrorMsg(block, msg);
   3421         },
   3422     }
   3423 }
   3424 
   3425 fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3426     const tracy = trace(@src());
   3427     defer tracy.end();
   3428 
   3429     const mod = sema.mod;
   3430     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3431     const operand = try sema.resolveInst(inst_data.operand);
   3432     const src = inst_data.src();
   3433     const operand_ty = sema.typeOf(operand);
   3434     switch (operand_ty.zigTypeTag(mod)) {
   3435         .ErrorSet, .ErrorUnion => {
   3436             const msg = msg: {
   3437                 const msg = try sema.errMsg(block, src, "error is discarded", .{});
   3438                 errdefer msg.destroy(sema.gpa);
   3439                 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{});
   3440                 break :msg msg;
   3441             };
   3442             return sema.failWithOwnedErrorMsg(block, msg);
   3443         },
   3444         else => return,
   3445     }
   3446 }
   3447 
   3448 fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3449     const tracy = trace(@src());
   3450     defer tracy.end();
   3451 
   3452     const mod = sema.mod;
   3453     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3454     const src = inst_data.src();
   3455     const operand = try sema.resolveInst(inst_data.operand);
   3456     const operand_ty = sema.typeOf(operand);
   3457     const err_union_ty = if (operand_ty.zigTypeTag(mod) == .Pointer)
   3458         operand_ty.childType(mod)
   3459     else
   3460         operand_ty;
   3461     if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) return;
   3462     const payload_ty = err_union_ty.errorUnionPayload(mod).zigTypeTag(mod);
   3463     if (payload_ty != .Void and payload_ty != .NoReturn) {
   3464         const msg = msg: {
   3465             const msg = try sema.errMsg(block, src, "error union payload is ignored", .{});
   3466             errdefer msg.destroy(sema.gpa);
   3467             try sema.errNote(block, src, msg, "payload value can be explicitly ignored with '|_|'", .{});
   3468             break :msg msg;
   3469         };
   3470         return sema.failWithOwnedErrorMsg(block, msg);
   3471     }
   3472 }
   3473 
   3474 fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3475     const tracy = trace(@src());
   3476     defer tracy.end();
   3477 
   3478     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3479     const src = inst_data.src();
   3480     const object = try sema.resolveInst(inst_data.operand);
   3481 
   3482     return indexablePtrLen(sema, block, src, object);
   3483 }
   3484 
   3485 fn indexablePtrLen(
   3486     sema: *Sema,
   3487     block: *Block,
   3488     src: LazySrcLoc,
   3489     object: Air.Inst.Ref,
   3490 ) CompileError!Air.Inst.Ref {
   3491     const mod = sema.mod;
   3492     const object_ty = sema.typeOf(object);
   3493     const is_pointer_to = object_ty.isSinglePointer(mod);
   3494     const indexable_ty = if (is_pointer_to) object_ty.childType(mod) else object_ty;
   3495     try checkIndexable(sema, block, src, indexable_ty);
   3496     const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len");
   3497     return sema.fieldVal(block, src, object, field_name, src);
   3498 }
   3499 
   3500 fn indexablePtrLenOrNone(
   3501     sema: *Sema,
   3502     block: *Block,
   3503     src: LazySrcLoc,
   3504     operand: Air.Inst.Ref,
   3505 ) CompileError!Air.Inst.Ref {
   3506     const mod = sema.mod;
   3507     const operand_ty = sema.typeOf(operand);
   3508     try checkMemOperand(sema, block, src, operand_ty);
   3509     if (operand_ty.ptrSize(mod) == .Many) return .none;
   3510     const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len");
   3511     return sema.fieldVal(block, src, operand, field_name, src);
   3512 }
   3513 
   3514 fn zirAllocExtended(
   3515     sema: *Sema,
   3516     block: *Block,
   3517     extended: Zir.Inst.Extended.InstData,
   3518 ) CompileError!Air.Inst.Ref {
   3519     const gpa = sema.gpa;
   3520     const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
   3521     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = extra.data.src_node };
   3522     const align_src: LazySrcLoc = .{ .node_offset_var_decl_align = extra.data.src_node };
   3523     const small: Zir.Inst.AllocExtended.Small = @bitCast(extended.small);
   3524 
   3525     var extra_index: usize = extra.end;
   3526 
   3527     const var_ty: Type = if (small.has_type) blk: {
   3528         const type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   3529         extra_index += 1;
   3530         break :blk try sema.resolveType(block, ty_src, type_ref);
   3531     } else undefined;
   3532 
   3533     const alignment = if (small.has_align) blk: {
   3534         const align_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   3535         extra_index += 1;
   3536         const alignment = try sema.resolveAlign(block, align_src, align_ref);
   3537         break :blk alignment;
   3538     } else .none;
   3539 
   3540     if (block.is_comptime or small.is_comptime) {
   3541         if (small.has_type) {
   3542             return sema.analyzeComptimeAlloc(block, var_ty, alignment);
   3543         } else {
   3544             try sema.air_instructions.append(gpa, .{
   3545                 .tag = .inferred_alloc_comptime,
   3546                 .data = .{ .inferred_alloc_comptime = .{
   3547                     .decl_index = undefined,
   3548                     .alignment = alignment,
   3549                     .is_const = small.is_const,
   3550                 } },
   3551             });
   3552             return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef();
   3553         }
   3554     }
   3555 
   3556     if (small.has_type) {
   3557         if (!small.is_const) {
   3558             try sema.validateVarType(block, ty_src, var_ty, false);
   3559         }
   3560         const target = sema.mod.getTarget();
   3561         try sema.resolveTypeLayout(var_ty);
   3562         const ptr_type = try sema.ptrType(.{
   3563             .child = var_ty.toIntern(),
   3564             .flags = .{
   3565                 .alignment = alignment,
   3566                 .address_space = target_util.defaultAddressSpace(target, .local),
   3567             },
   3568         });
   3569         const ptr = try block.addTy(.alloc, ptr_type);
   3570         if (small.is_const) {
   3571             const ptr_inst = ptr.toIndex().?;
   3572             try sema.maybe_comptime_allocs.put(gpa, ptr_inst, .{ .runtime_index = block.runtime_index });
   3573             try sema.base_allocs.put(gpa, ptr_inst, ptr_inst);
   3574         }
   3575         return ptr;
   3576     }
   3577 
   3578     const result_index = try block.addInstAsIndex(.{
   3579         .tag = .inferred_alloc,
   3580         .data = .{ .inferred_alloc = .{
   3581             .alignment = alignment,
   3582             .is_const = small.is_const,
   3583         } },
   3584     });
   3585     try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{});
   3586     if (small.is_const) {
   3587         try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index });
   3588         try sema.base_allocs.put(gpa, result_index, result_index);
   3589     }
   3590     return result_index.toRef();
   3591 }
   3592 
   3593 fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3594     const tracy = trace(@src());
   3595     defer tracy.end();
   3596 
   3597     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3598     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
   3599     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   3600     return sema.analyzeComptimeAlloc(block, var_ty, .none);
   3601 }
   3602 
   3603 fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3604     const mod = sema.mod;
   3605     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3606     const alloc = try sema.resolveInst(inst_data.operand);
   3607     const alloc_ty = sema.typeOf(alloc);
   3608     const ptr_info = alloc_ty.ptrInfo(mod);
   3609     const elem_ty = Type.fromInterned(ptr_info.child);
   3610 
   3611     if (try sema.resolveComptimeKnownAllocValue(block, alloc, null)) |val| {
   3612         const new_mut_ptr = Air.internedToRef((try mod.intern(.{ .ptr = .{
   3613             .ty = alloc_ty.toIntern(),
   3614             .addr = .{ .anon_decl = .{
   3615                 .val = val,
   3616                 .orig_ty = alloc_ty.toIntern(),
   3617             } },
   3618         } })));
   3619         return sema.makePtrConst(block, new_mut_ptr);
   3620     }
   3621 
   3622     // If this is already a comptime-known allocation, we don't want to emit an error - the stores
   3623     // were already performed at comptime! Just make the pointer constant as normal.
   3624     implicit_ct: {
   3625         const ptr_val = try sema.resolveValue(alloc) orelse break :implicit_ct;
   3626         if (!ptr_val.isComptimeMutablePtr(mod)) {
   3627             // It could still be a constant pointer to a decl.
   3628             switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) {
   3629                 .anon_decl => |anon_decl| {
   3630                     if (mod.intern_pool.isVariable(anon_decl.val))
   3631                         break :implicit_ct;
   3632                 },
   3633                 else => {
   3634                     const decl_index = ptr_val.pointerDecl(mod) orelse break :implicit_ct;
   3635                     const decl_val = mod.declPtr(decl_index).val.toIntern();
   3636                     if (mod.intern_pool.isVariable(decl_val)) break :implicit_ct;
   3637                 },
   3638             }
   3639         }
   3640         return sema.makePtrConst(block, alloc);
   3641     }
   3642 
   3643     if (try sema.typeRequiresComptime(elem_ty)) {
   3644         // The value was initialized through RLS, so we didn't detect the runtime condition earlier.
   3645         // TODO: source location of runtime control flow
   3646         const init_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
   3647         return sema.fail(block, init_src, "value with comptime-only type '{}' depends on runtime control flow", .{elem_ty.fmt(mod)});
   3648     }
   3649 
   3650     // This is a runtime value.
   3651     return sema.makePtrConst(block, alloc);
   3652 }
   3653 
   3654 /// If `alloc` is an inferred allocation, `resolved_inferred_ty` is taken to be its resolved
   3655 /// type. Otherwise, it may be `null`, and the type will be inferred from `alloc`.
   3656 fn resolveComptimeKnownAllocValue(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, resolved_alloc_ty: ?Type) CompileError!?InternPool.Index {
   3657     const mod = sema.mod;
   3658 
   3659     const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc);
   3660     const ptr_info = alloc_ty.ptrInfo(mod);
   3661     const elem_ty = Type.fromInterned(ptr_info.child);
   3662 
   3663     const alloc_inst = alloc.toIndex() orelse return null;
   3664     const comptime_info = sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return null;
   3665     const stores = comptime_info.value.stores.items;
   3666 
   3667     // Since the entry existed in `maybe_comptime_allocs`, the allocation is comptime-known.
   3668     // We will resolve and return its value.
   3669 
   3670     // We expect to have emitted at least one store, unless the elem type is OPV.
   3671     if (stores.len == 0) {
   3672         const val = (try sema.typeHasOnePossibleValue(elem_ty)).?.toIntern();
   3673         return sema.finishResolveComptimeKnownAllocValue(val, alloc_inst, comptime_info.value);
   3674     }
   3675 
   3676     // In general, we want to create a comptime alloc of the correct type and
   3677     // apply the stores to that alloc in order. However, before going to all
   3678     // that effort, let's optimize for the common case of a single store.
   3679 
   3680     simple: {
   3681         if (stores.len != 1) break :simple;
   3682         const store_inst = stores[0];
   3683         const store_data = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op;
   3684         if (store_data.lhs != alloc) break :simple;
   3685 
   3686         const val = store_data.rhs.toInterned().?;
   3687         assert(mod.intern_pool.typeOf(val) == elem_ty.toIntern());
   3688         return sema.finishResolveComptimeKnownAllocValue(val, alloc_inst, comptime_info.value);
   3689     }
   3690 
   3691     // The simple strategy failed: we must create a mutable comptime alloc and
   3692     // perform all of the runtime store operations at comptime.
   3693 
   3694     var anon_decl = try block.startAnonDecl(); // TODO: comptime value mutation without Decl
   3695     defer anon_decl.deinit();
   3696     const decl_index = try anon_decl.finish(elem_ty, try mod.undefValue(elem_ty), ptr_info.flags.alignment);
   3697 
   3698     const decl_ptr = try mod.intern(.{ .ptr = .{
   3699         .ty = alloc_ty.toIntern(),
   3700         .addr = .{ .mut_decl = .{
   3701             .decl = decl_index,
   3702             .runtime_index = block.runtime_index,
   3703         } },
   3704     } });
   3705 
   3706     // Maps from pointers into the runtime allocs, to comptime-mutable pointers into the mut decl.
   3707     var ptr_mapping = std.AutoHashMap(Air.Inst.Index, InternPool.Index).init(sema.arena);
   3708     try ptr_mapping.ensureTotalCapacity(@intCast(stores.len));
   3709     ptr_mapping.putAssumeCapacity(alloc_inst, decl_ptr);
   3710 
   3711     var to_map = try std.ArrayList(Air.Inst.Index).initCapacity(sema.arena, stores.len);
   3712     for (stores) |store_inst| {
   3713         const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op;
   3714         to_map.appendAssumeCapacity(bin_op.lhs.toIndex().?);
   3715     }
   3716 
   3717     const tmp_air = sema.getTmpAir();
   3718 
   3719     while (to_map.popOrNull()) |air_ptr| {
   3720         if (ptr_mapping.contains(air_ptr)) continue;
   3721         const PointerMethod = union(enum) {
   3722             same_addr,
   3723             opt_payload,
   3724             eu_payload,
   3725             field: u32,
   3726             elem: u64,
   3727         };
   3728         const inst_tag = tmp_air.instructions.items(.tag)[@intFromEnum(air_ptr)];
   3729         const air_parent_ptr: Air.Inst.Ref, const method: PointerMethod = switch (inst_tag) {
   3730             .struct_field_ptr => blk: {
   3731                 const data = tmp_air.extraData(
   3732                     Air.StructField,
   3733                     tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload,
   3734                 ).data;
   3735                 break :blk .{
   3736                     data.struct_operand,
   3737                     .{ .field = data.field_index },
   3738                 };
   3739             },
   3740             .struct_field_ptr_index_0,
   3741             .struct_field_ptr_index_1,
   3742             .struct_field_ptr_index_2,
   3743             .struct_field_ptr_index_3,
   3744             => .{
   3745                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   3746                 .{ .field = switch (inst_tag) {
   3747                     .struct_field_ptr_index_0 => 0,
   3748                     .struct_field_ptr_index_1 => 1,
   3749                     .struct_field_ptr_index_2 => 2,
   3750                     .struct_field_ptr_index_3 => 3,
   3751                     else => unreachable,
   3752                 } },
   3753             },
   3754             .ptr_slice_ptr_ptr => .{
   3755                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   3756                 .{ .field = Value.slice_ptr_index },
   3757             },
   3758             .ptr_slice_len_ptr => .{
   3759                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   3760                 .{ .field = Value.slice_len_index },
   3761             },
   3762             .ptr_elem_ptr => blk: {
   3763                 const data = tmp_air.extraData(
   3764                     Air.Bin,
   3765                     tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload,
   3766                 ).data;
   3767                 const idx_val = (try sema.resolveValue(data.rhs)).?;
   3768                 break :blk .{
   3769                     data.lhs,
   3770                     .{ .elem = idx_val.toUnsignedInt(mod) },
   3771                 };
   3772             },
   3773             .bitcast => .{
   3774                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   3775                 .same_addr,
   3776             },
   3777             .optional_payload_ptr_set => .{
   3778                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   3779                 .opt_payload,
   3780             },
   3781             .errunion_payload_ptr_set => .{
   3782                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   3783                 .eu_payload,
   3784             },
   3785             else => unreachable,
   3786         };
   3787 
   3788         const decl_parent_ptr = ptr_mapping.get(air_parent_ptr.toIndex().?) orelse {
   3789             // Resolve the parent pointer first.
   3790             // Note that we add in what seems like the wrong order, because we're popping from the end of this array.
   3791             try to_map.appendSlice(&.{ air_ptr, air_parent_ptr.toIndex().? });
   3792             continue;
   3793         };
   3794         const new_ptr_ty = tmp_air.typeOfIndex(air_ptr, &mod.intern_pool).toIntern();
   3795         const new_ptr = switch (method) {
   3796             .same_addr => try mod.intern_pool.getCoerced(sema.gpa, decl_parent_ptr, new_ptr_ty),
   3797             .opt_payload => try mod.intern(.{ .ptr = .{
   3798                 .ty = new_ptr_ty,
   3799                 .addr = .{ .opt_payload = decl_parent_ptr },
   3800             } }),
   3801             .eu_payload => try mod.intern(.{ .ptr = .{
   3802                 .ty = new_ptr_ty,
   3803                 .addr = .{ .eu_payload = decl_parent_ptr },
   3804             } }),
   3805             .field => |field_idx| try mod.intern(.{ .ptr = .{
   3806                 .ty = new_ptr_ty,
   3807                 .addr = .{ .field = .{
   3808                     .base = decl_parent_ptr,
   3809                     .index = field_idx,
   3810                 } },
   3811             } }),
   3812             .elem => |elem_idx| (try Value.fromInterned(decl_parent_ptr).elemPtr(Type.fromInterned(new_ptr_ty), @intCast(elem_idx), mod)).toIntern(),
   3813         };
   3814         try ptr_mapping.put(air_ptr, new_ptr);
   3815     }
   3816 
   3817     // We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime.
   3818 
   3819     for (stores) |store_inst| {
   3820         switch (sema.air_instructions.items(.tag)[@intFromEnum(store_inst)]) {
   3821             .set_union_tag => {
   3822                 // If this tag has an OPV payload, there won't be a corresponding
   3823                 // store instruction, so we must set the union payload now.
   3824                 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op;
   3825                 const air_ptr_inst = bin_op.lhs.toIndex().?;
   3826                 const tag_val = (try sema.resolveValue(bin_op.rhs)).?;
   3827                 const union_ty = sema.typeOf(bin_op.lhs).childType(mod);
   3828                 const payload_ty = union_ty.unionFieldType(tag_val, mod).?;
   3829                 if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_val| {
   3830                     const new_ptr = ptr_mapping.get(air_ptr_inst).?;
   3831                     const store_val = try mod.unionValue(union_ty, tag_val, payload_val);
   3832                     try sema.storePtrVal(block, .unneeded, Value.fromInterned(new_ptr), store_val, union_ty);
   3833                 }
   3834             },
   3835             .store, .store_safe => {
   3836                 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op;
   3837                 const air_ptr_inst = bin_op.lhs.toIndex().?;
   3838                 const store_val = (try sema.resolveValue(bin_op.rhs)).?;
   3839                 const new_ptr = ptr_mapping.get(air_ptr_inst).?;
   3840                 try sema.storePtrVal(block, .unneeded, Value.fromInterned(new_ptr), store_val, Type.fromInterned(mod.intern_pool.typeOf(store_val.toIntern())));
   3841             },
   3842             else => unreachable,
   3843         }
   3844     }
   3845 
   3846     // The value is finalized - load it!
   3847     const val = (try sema.pointerDeref(block, .unneeded, Value.fromInterned(decl_ptr), alloc_ty)).?.toIntern();
   3848     return sema.finishResolveComptimeKnownAllocValue(val, alloc_inst, comptime_info.value);
   3849 }
   3850 
   3851 /// Given the resolved comptime-known value, rewrites the dead AIR to not
   3852 /// create a runtime stack allocation.
   3853 /// Same return type as `resolveComptimeKnownAllocValue` so we can tail call.
   3854 fn finishResolveComptimeKnownAllocValue(sema: *Sema, result_val: InternPool.Index, alloc_inst: Air.Inst.Index, comptime_info: MaybeComptimeAlloc) CompileError!?InternPool.Index {
   3855     // We're almost done - we have the resolved comptime value. We just need to
   3856     // eliminate the now-dead runtime instructions.
   3857 
   3858     // We will rewrite the AIR to eliminate the alloc and all stores to it.
   3859     // This will cause instructions deriving field pointers etc of the alloc to
   3860     // become invalid, however, since we are removing all stores to those pointers,
   3861     // they will be eliminated by Liveness before they reach codegen.
   3862 
   3863     // The specifics of this instruction aren't really important: we just want
   3864     // Liveness to elide it.
   3865     const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{ .ty = .u8_type, .operand = .zero_u8 } } };
   3866 
   3867     sema.air_instructions.set(@intFromEnum(alloc_inst), nop_inst);
   3868     for (comptime_info.stores.items) |store_inst| {
   3869         sema.air_instructions.set(@intFromEnum(store_inst), nop_inst);
   3870     }
   3871     for (comptime_info.non_elideable_pointers.items) |ptr_inst| {
   3872         sema.air_instructions.set(@intFromEnum(ptr_inst), nop_inst);
   3873     }
   3874 
   3875     return result_val;
   3876 }
   3877 
   3878 fn makePtrTyConst(sema: *Sema, ptr_ty: Type) CompileError!Type {
   3879     var ptr_info = ptr_ty.ptrInfo(sema.mod);
   3880     ptr_info.flags.is_const = true;
   3881     return sema.ptrType(ptr_info);
   3882 }
   3883 
   3884 fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref {
   3885     const alloc_ty = sema.typeOf(alloc);
   3886     const const_ptr_ty = try sema.makePtrTyConst(alloc_ty);
   3887 
   3888     // Detect if a comptime value simply needs to have its type changed.
   3889     if (try sema.resolveValue(alloc)) |val| {
   3890         return Air.internedToRef((try sema.mod.getCoerced(val, const_ptr_ty)).toIntern());
   3891     }
   3892 
   3893     return block.addBitCast(const_ptr_ty, alloc);
   3894 }
   3895 
   3896 fn zirAllocInferredComptime(
   3897     sema: *Sema,
   3898     inst: Zir.Inst.Index,
   3899     is_const: bool,
   3900 ) CompileError!Air.Inst.Ref {
   3901     const gpa = sema.gpa;
   3902     const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node;
   3903     const src = LazySrcLoc.nodeOffset(src_node);
   3904     sema.src = src;
   3905 
   3906     try sema.air_instructions.append(gpa, .{
   3907         .tag = .inferred_alloc_comptime,
   3908         .data = .{ .inferred_alloc_comptime = .{
   3909             .decl_index = undefined,
   3910             .alignment = .none,
   3911             .is_const = is_const,
   3912         } },
   3913     });
   3914     return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef();
   3915 }
   3916 
   3917 fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3918     const tracy = trace(@src());
   3919     defer tracy.end();
   3920 
   3921     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3922     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
   3923     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   3924     if (block.is_comptime) {
   3925         return sema.analyzeComptimeAlloc(block, var_ty, .none);
   3926     }
   3927     const target = sema.mod.getTarget();
   3928     const ptr_type = try sema.ptrType(.{
   3929         .child = var_ty.toIntern(),
   3930         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   3931     });
   3932     try sema.queueFullTypeResolution(var_ty);
   3933     const ptr = try block.addTy(.alloc, ptr_type);
   3934     const ptr_inst = ptr.toIndex().?;
   3935     try sema.maybe_comptime_allocs.put(sema.gpa, ptr_inst, .{ .runtime_index = block.runtime_index });
   3936     try sema.base_allocs.put(sema.gpa, ptr_inst, ptr_inst);
   3937     return ptr;
   3938 }
   3939 
   3940 fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3941     const tracy = trace(@src());
   3942     defer tracy.end();
   3943 
   3944     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3945     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
   3946     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   3947     if (block.is_comptime) {
   3948         return sema.analyzeComptimeAlloc(block, var_ty, .none);
   3949     }
   3950     try sema.validateVarType(block, ty_src, var_ty, false);
   3951     const target = sema.mod.getTarget();
   3952     const ptr_type = try sema.ptrType(.{
   3953         .child = var_ty.toIntern(),
   3954         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   3955     });
   3956     try sema.queueFullTypeResolution(var_ty);
   3957     return block.addTy(.alloc, ptr_type);
   3958 }
   3959 
   3960 fn zirAllocInferred(
   3961     sema: *Sema,
   3962     block: *Block,
   3963     inst: Zir.Inst.Index,
   3964     is_const: bool,
   3965 ) CompileError!Air.Inst.Ref {
   3966     const tracy = trace(@src());
   3967     defer tracy.end();
   3968 
   3969     const gpa = sema.gpa;
   3970     const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node;
   3971     const src = LazySrcLoc.nodeOffset(src_node);
   3972     sema.src = src;
   3973 
   3974     if (block.is_comptime) {
   3975         try sema.air_instructions.append(gpa, .{
   3976             .tag = .inferred_alloc_comptime,
   3977             .data = .{ .inferred_alloc_comptime = .{
   3978                 .decl_index = undefined,
   3979                 .alignment = .none,
   3980                 .is_const = is_const,
   3981             } },
   3982         });
   3983         return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef();
   3984     }
   3985 
   3986     const result_index = try block.addInstAsIndex(.{
   3987         .tag = .inferred_alloc,
   3988         .data = .{ .inferred_alloc = .{
   3989             .alignment = .none,
   3990             .is_const = is_const,
   3991         } },
   3992     });
   3993     try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{});
   3994     try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index });
   3995     try sema.base_allocs.put(sema.gpa, result_index, result_index);
   3996     return result_index.toRef();
   3997 }
   3998 
   3999 fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   4000     const tracy = trace(@src());
   4001     defer tracy.end();
   4002 
   4003     const mod = sema.mod;
   4004     const gpa = sema.gpa;
   4005     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4006     const src = inst_data.src();
   4007     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
   4008     const ptr = try sema.resolveInst(inst_data.operand);
   4009     const ptr_inst = ptr.toIndex().?;
   4010     const target = mod.getTarget();
   4011 
   4012     switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) {
   4013         .inferred_alloc_comptime => {
   4014             const iac = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc_comptime;
   4015             const decl_index = iac.decl_index;
   4016 
   4017             const decl = mod.declPtr(decl_index);
   4018             if (iac.is_const) _ = try decl.internValue(mod);
   4019             const final_elem_ty = decl.ty;
   4020             const final_ptr_ty = try sema.ptrType(.{
   4021                 .child = final_elem_ty.toIntern(),
   4022                 .flags = .{
   4023                     .is_const = false,
   4024                     .alignment = iac.alignment,
   4025                     .address_space = target_util.defaultAddressSpace(target, .local),
   4026                 },
   4027             });
   4028 
   4029             if (std.debug.runtime_safety) {
   4030                 // The inferred_alloc_comptime should never be referenced again
   4031                 sema.air_instructions.set(@intFromEnum(ptr_inst), .{ .tag = undefined, .data = undefined });
   4032             }
   4033 
   4034             try sema.maybeQueueFuncBodyAnalysis(decl_index);
   4035 
   4036             const interned = try mod.intern(.{ .ptr = .{
   4037                 .ty = final_ptr_ty.toIntern(),
   4038                 .addr = if (!iac.is_const) .{ .mut_decl = .{
   4039                     .decl = decl_index,
   4040                     .runtime_index = block.runtime_index,
   4041                 } } else .{ .decl = decl_index },
   4042             } });
   4043 
   4044             // Remap the ZIR operand to the resolved pointer value
   4045             sema.inst_map.putAssumeCapacity(inst_data.operand.toIndex().?, Air.internedToRef(interned));
   4046         },
   4047         .inferred_alloc => {
   4048             const ia1 = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc;
   4049             const ia2 = sema.unresolved_inferred_allocs.fetchSwapRemove(ptr_inst).?.value;
   4050             const peer_vals = try sema.arena.alloc(Air.Inst.Ref, ia2.prongs.items.len);
   4051             for (peer_vals, ia2.prongs.items) |*peer_val, store_inst| {
   4052                 assert(sema.air_instructions.items(.tag)[@intFromEnum(store_inst)] == .store);
   4053                 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op;
   4054                 peer_val.* = bin_op.rhs;
   4055             }
   4056             const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_vals, .none);
   4057 
   4058             const final_ptr_ty = try sema.ptrType(.{
   4059                 .child = final_elem_ty.toIntern(),
   4060                 .flags = .{
   4061                     .alignment = ia1.alignment,
   4062                     .address_space = target_util.defaultAddressSpace(target, .local),
   4063                 },
   4064             });
   4065 
   4066             if (!ia1.is_const) {
   4067                 try sema.validateVarType(block, ty_src, final_elem_ty, false);
   4068             } else if (try sema.resolveComptimeKnownAllocValue(block, ptr, final_ptr_ty)) |val| {
   4069                 const const_ptr_ty = (try sema.makePtrTyConst(final_ptr_ty)).toIntern();
   4070                 const new_const_ptr = try mod.intern(.{ .ptr = .{
   4071                     .ty = const_ptr_ty,
   4072                     .addr = .{ .anon_decl = .{
   4073                         .val = val,
   4074                         .orig_ty = const_ptr_ty,
   4075                     } },
   4076                 } });
   4077 
   4078                 // Remap the ZIR oeprand to the resolved pointer value
   4079                 sema.inst_map.putAssumeCapacity(inst_data.operand.toIndex().?, Air.internedToRef(new_const_ptr));
   4080 
   4081                 // Unless the block is comptime, `alloc_inferred` always produces
   4082                 // a runtime constant. The final inferred type needs to be
   4083                 // fully resolved so it can be lowered in codegen.
   4084                 try sema.resolveTypeFully(final_elem_ty);
   4085 
   4086                 return;
   4087             }
   4088 
   4089             try sema.queueFullTypeResolution(final_elem_ty);
   4090 
   4091             // Change it to a normal alloc.
   4092             sema.air_instructions.set(@intFromEnum(ptr_inst), .{
   4093                 .tag = .alloc,
   4094                 .data = .{ .ty = final_ptr_ty },
   4095             });
   4096 
   4097             // Now we need to go back over all the store instructions, and do the logic as if
   4098             // the new result ptr type was available.
   4099 
   4100             for (ia2.prongs.items) |placeholder_inst| {
   4101                 var replacement_block = block.makeSubBlock();
   4102                 defer replacement_block.instructions.deinit(gpa);
   4103 
   4104                 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .store);
   4105                 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].bin_op;
   4106                 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .store);
   4107 
   4108                 // If only one instruction is produced then we can replace the store
   4109                 // placeholder instruction with this instruction; no need for an entire block.
   4110                 if (replacement_block.instructions.items.len == 1) {
   4111                     const only_inst = replacement_block.instructions.items[0];
   4112                     sema.air_instructions.set(@intFromEnum(placeholder_inst), sema.air_instructions.get(@intFromEnum(only_inst)));
   4113                     continue;
   4114                 }
   4115 
   4116                 // Here we replace the placeholder store instruction with a block
   4117                 // that does the actual store logic.
   4118                 _ = try replacement_block.addBr(placeholder_inst, .void_value);
   4119                 try sema.air_extra.ensureUnusedCapacity(
   4120                     gpa,
   4121                     @typeInfo(Air.Block).Struct.fields.len + replacement_block.instructions.items.len,
   4122                 );
   4123                 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{
   4124                     .tag = .block,
   4125                     .data = .{ .ty_pl = .{
   4126                         .ty = .void_type,
   4127                         .payload = sema.addExtraAssumeCapacity(Air.Block{
   4128                             .body_len = @intCast(replacement_block.instructions.items.len),
   4129                         }),
   4130                     } },
   4131                 });
   4132                 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items));
   4133             }
   4134         },
   4135         else => unreachable,
   4136     }
   4137 }
   4138 
   4139 fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4140     const mod = sema.mod;
   4141     const gpa = sema.gpa;
   4142     const ip = &mod.intern_pool;
   4143     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4144     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
   4145     const args = sema.code.refSlice(extra.end, extra.data.operands_len);
   4146     const src = inst_data.src();
   4147 
   4148     var len: Air.Inst.Ref = .none;
   4149     var len_val: ?Value = null;
   4150     var len_idx: u32 = undefined;
   4151     var any_runtime = false;
   4152 
   4153     const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, args.len);
   4154     defer gpa.free(runtime_arg_lens);
   4155 
   4156     // First pass to look for comptime values.
   4157     for (args, 0..) |zir_arg, i_usize| {
   4158         const i: u32 = @intCast(i_usize);
   4159         runtime_arg_lens[i] = .none;
   4160         if (zir_arg == .none) continue;
   4161         const object = try sema.resolveInst(zir_arg);
   4162         const object_ty = sema.typeOf(object);
   4163         // Each arg could be an indexable, or a range, in which case the length
   4164         // is passed directly as an integer.
   4165         const is_int = switch (object_ty.zigTypeTag(mod)) {
   4166             .Int, .ComptimeInt => true,
   4167             else => false,
   4168         };
   4169         const arg_src: LazySrcLoc = .{ .for_input = .{
   4170             .for_node_offset = inst_data.src_node,
   4171             .input_index = i,
   4172         } };
   4173         const arg_len_uncoerced = if (is_int) object else l: {
   4174             if (!object_ty.isIndexable(mod)) {
   4175                 // Instead of using checkIndexable we customize this error.
   4176                 const msg = msg: {
   4177                     const msg = try sema.errMsg(block, arg_src, "type '{}' is not indexable and not a range", .{object_ty.fmt(sema.mod)});
   4178                     errdefer msg.destroy(sema.gpa);
   4179                     try sema.errNote(block, arg_src, msg, "for loop operand must be a range, array, slice, tuple, or vector", .{});
   4180                     break :msg msg;
   4181                 };
   4182                 return sema.failWithOwnedErrorMsg(block, msg);
   4183             }
   4184             if (!object_ty.indexableHasLen(mod)) continue;
   4185 
   4186             break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, "len"), arg_src);
   4187         };
   4188         const arg_len = try sema.coerce(block, Type.usize, arg_len_uncoerced, arg_src);
   4189         if (len == .none) {
   4190             len = arg_len;
   4191             len_idx = i;
   4192         }
   4193         if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| {
   4194             if (len_val) |v| {
   4195                 if (!(try sema.valuesEqual(arg_val, v, Type.usize))) {
   4196                     const msg = msg: {
   4197                         const msg = try sema.errMsg(block, src, "non-matching for loop lengths", .{});
   4198                         errdefer msg.destroy(gpa);
   4199                         const a_src: LazySrcLoc = .{ .for_input = .{
   4200                             .for_node_offset = inst_data.src_node,
   4201                             .input_index = len_idx,
   4202                         } };
   4203                         try sema.errNote(block, a_src, msg, "length {} here", .{
   4204                             v.fmtValue(Type.usize, sema.mod),
   4205                         });
   4206                         try sema.errNote(block, arg_src, msg, "length {} here", .{
   4207                             arg_val.fmtValue(Type.usize, sema.mod),
   4208                         });
   4209                         break :msg msg;
   4210                     };
   4211                     return sema.failWithOwnedErrorMsg(block, msg);
   4212                 }
   4213             } else {
   4214                 len = arg_len;
   4215                 len_val = arg_val;
   4216                 len_idx = i;
   4217             }
   4218             continue;
   4219         }
   4220         runtime_arg_lens[i] = arg_len;
   4221         any_runtime = true;
   4222     }
   4223 
   4224     if (len == .none) {
   4225         const msg = msg: {
   4226             const msg = try sema.errMsg(block, src, "unbounded for loop", .{});
   4227             errdefer msg.destroy(gpa);
   4228             for (args, 0..) |zir_arg, i_usize| {
   4229                 const i: u32 = @intCast(i_usize);
   4230                 if (zir_arg == .none) continue;
   4231                 const object = try sema.resolveInst(zir_arg);
   4232                 const object_ty = sema.typeOf(object);
   4233                 // Each arg could be an indexable, or a range, in which case the length
   4234                 // is passed directly as an integer.
   4235                 switch (object_ty.zigTypeTag(mod)) {
   4236                     .Int, .ComptimeInt => continue,
   4237                     else => {},
   4238                 }
   4239                 const arg_src: LazySrcLoc = .{ .for_input = .{
   4240                     .for_node_offset = inst_data.src_node,
   4241                     .input_index = i,
   4242                 } };
   4243                 try sema.errNote(block, arg_src, msg, "type '{}' has no upper bound", .{
   4244                     object_ty.fmt(sema.mod),
   4245                 });
   4246             }
   4247             break :msg msg;
   4248         };
   4249         return sema.failWithOwnedErrorMsg(block, msg);
   4250     }
   4251 
   4252     // Now for the runtime checks.
   4253     if (any_runtime and block.wantSafety()) {
   4254         for (runtime_arg_lens, 0..) |arg_len, i| {
   4255             if (arg_len == .none) continue;
   4256             if (i == len_idx) continue;
   4257             const ok = try block.addBinOp(.cmp_eq, len, arg_len);
   4258             try sema.addSafetyCheck(block, src, ok, .for_len_mismatch);
   4259         }
   4260     }
   4261 
   4262     return len;
   4263 }
   4264 
   4265 /// Given any single pointer, retrieve a pointer to the payload of any optional
   4266 /// or error union pointed to, initializing these pointers along the way.
   4267 /// Given a `*E!?T`, returns a (valid) `*T`.
   4268 /// May invalidate already-stored payload data.
   4269 fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcLoc) CompileError!Air.Inst.Ref {
   4270     const mod = sema.mod;
   4271     var base_ptr = ptr;
   4272     while (true) switch (sema.typeOf(base_ptr).childType(mod).zigTypeTag(mod)) {
   4273         .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true),
   4274         .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true),
   4275         else => break,
   4276     };
   4277     try sema.checkKnownAllocPtr(ptr, base_ptr);
   4278     return base_ptr;
   4279 }
   4280 
   4281 fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4282     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4283     const ptr = try sema.resolveInst(un_node.operand);
   4284     return sema.optEuBasePtrInit(block, ptr, un_node.src());
   4285 }
   4286 
   4287 fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4288     const mod = sema.mod;
   4289     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4290     const src = pl_node.src();
   4291     const extra = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data;
   4292     const uncoerced_val = try sema.resolveInst(extra.rhs);
   4293     const maybe_wrapped_ptr_ty = sema.resolveType(block, .unneeded, extra.lhs) catch |err| switch (err) {
   4294         error.GenericPoison => return uncoerced_val,
   4295         else => |e| return e,
   4296     };
   4297     const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(mod);
   4298     assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction
   4299     const elem_ty = ptr_ty.childType(mod);
   4300     switch (ptr_ty.ptrSize(mod)) {
   4301         .One => {
   4302             const uncoerced_ty = sema.typeOf(uncoerced_val);
   4303             if (elem_ty.zigTypeTag(mod) == .Array and elem_ty.childType(mod).toIntern() == uncoerced_ty.toIntern()) {
   4304                 // We're trying to initialize a *[1]T with a reference to a T - don't perform any coercion.
   4305                 return uncoerced_val;
   4306             }
   4307             // If the destination type is anyopaque, don't coerce - the pointer will coerce instead.
   4308             if (elem_ty.toIntern() == .anyopaque_type) {
   4309                 return uncoerced_val;
   4310             } else {
   4311                 return sema.coerce(block, elem_ty, uncoerced_val, src);
   4312             }
   4313         },
   4314         .Slice, .Many => {
   4315             // Our goal is to coerce `uncoerced_val` to an array of `elem_ty`.
   4316             const val_ty = sema.typeOf(uncoerced_val);
   4317             switch (val_ty.zigTypeTag(mod)) {
   4318                 .Array, .Vector => {},
   4319                 else => if (!val_ty.isTuple(mod)) {
   4320                     return sema.fail(block, src, "expected array of '{}', found '{}'", .{ elem_ty.fmt(mod), val_ty.fmt(mod) });
   4321                 },
   4322             }
   4323             const want_ty = try mod.arrayType(.{
   4324                 .len = val_ty.arrayLen(mod),
   4325                 .child = elem_ty.toIntern(),
   4326                 .sentinel = if (ptr_ty.sentinel(mod)) |s| s.toIntern() else .none,
   4327             });
   4328             return sema.coerce(block, want_ty, uncoerced_val, src);
   4329         },
   4330         .C => {
   4331             // There's nothing meaningful to do here, because we don't know if this is meant to be a
   4332             // single-pointer or a many-pointer.
   4333             return uncoerced_val;
   4334         },
   4335     }
   4336 }
   4337 
   4338 fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   4339     const mod = sema.mod;
   4340     const un_tok = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
   4341     const src = un_tok.src();
   4342     // In case of GenericPoison, we don't actually have a type, so this will be
   4343     // treated as an untyped address-of operator.
   4344     if (un_tok.operand == .var_args_param_type) return;
   4345     const operand_air_inst = sema.resolveInst(un_tok.operand) catch |err| switch (err) {
   4346         error.GenericPoison => return,
   4347         else => |e| return e,
   4348     };
   4349     if (operand_air_inst == .var_args_param_type) return;
   4350     const ty_operand = sema.analyzeAsType(block, src, operand_air_inst) catch |err| switch (err) {
   4351         error.GenericPoison => return,
   4352         else => |e| return e,
   4353     };
   4354     if (ty_operand.isGenericPoison()) return;
   4355     if (ty_operand.optEuBaseType(mod).zigTypeTag(mod) != .Pointer) {
   4356         return sema.failWithOwnedErrorMsg(block, msg: {
   4357             const msg = try sema.errMsg(block, src, "expected type '{}', found pointer", .{ty_operand.fmt(mod)});
   4358             errdefer msg.destroy(sema.gpa);
   4359             try sema.errNote(block, src, msg, "address-of operator always returns a pointer", .{});
   4360             break :msg msg;
   4361         });
   4362     }
   4363 }
   4364 
   4365 fn zirValidateArrayInitRefTy(
   4366     sema: *Sema,
   4367     block: *Block,
   4368     inst: Zir.Inst.Index,
   4369 ) CompileError!Air.Inst.Ref {
   4370     const mod = sema.mod;
   4371     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4372     const src = pl_node.src();
   4373     const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data;
   4374     const maybe_wrapped_ptr_ty = sema.resolveType(block, .unneeded, extra.ptr_ty) catch |err| switch (err) {
   4375         error.GenericPoison => return .generic_poison_type,
   4376         else => |e| return e,
   4377     };
   4378     const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(mod);
   4379     assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction
   4380     if (ptr_ty.isSlice(mod)) {
   4381         // Use array of correct length
   4382         const arr_ty = try mod.arrayType(.{
   4383             .len = extra.elem_count,
   4384             .child = ptr_ty.childType(mod).toIntern(),
   4385             .sentinel = if (ptr_ty.sentinel(mod)) |s| s.toIntern() else .none,
   4386         });
   4387         return Air.internedToRef(arr_ty.toIntern());
   4388     }
   4389     // Otherwise, we just want the pointer child type
   4390     const ret_ty = ptr_ty.childType(mod);
   4391     if (ret_ty.toIntern() == .anyopaque_type) {
   4392         // The actual array type is unknown, which we represent with a generic poison.
   4393         return .generic_poison_type;
   4394     }
   4395     const arr_ty = ret_ty.optEuBaseType(mod);
   4396     try sema.validateArrayInitTy(block, src, src, extra.elem_count, arr_ty);
   4397     return Air.internedToRef(ret_ty.toIntern());
   4398 }
   4399 
   4400 fn zirValidateArrayInitTy(
   4401     sema: *Sema,
   4402     block: *Block,
   4403     inst: Zir.Inst.Index,
   4404     is_result_ty: bool,
   4405 ) CompileError!void {
   4406     const mod = sema.mod;
   4407     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4408     const src = inst_data.src();
   4409     const ty_src: LazySrcLoc = if (is_result_ty) src else .{ .node_offset_init_ty = inst_data.src_node };
   4410     const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
   4411     const ty = sema.resolveType(block, ty_src, extra.ty) catch |err| switch (err) {
   4412         // It's okay for the type to be unknown: this will result in an anonymous array init.
   4413         error.GenericPoison => return,
   4414         else => |e| return e,
   4415     };
   4416     const arr_ty = if (is_result_ty) ty.optEuBaseType(mod) else ty;
   4417     return sema.validateArrayInitTy(block, src, ty_src, extra.init_count, arr_ty);
   4418 }
   4419 
   4420 fn validateArrayInitTy(
   4421     sema: *Sema,
   4422     block: *Block,
   4423     src: LazySrcLoc,
   4424     ty_src: LazySrcLoc,
   4425     init_count: u32,
   4426     ty: Type,
   4427 ) CompileError!void {
   4428     const mod = sema.mod;
   4429     switch (ty.zigTypeTag(mod)) {
   4430         .Array => {
   4431             const array_len = ty.arrayLen(mod);
   4432             if (init_count != array_len) {
   4433                 return sema.fail(block, src, "expected {d} array elements; found {d}", .{
   4434                     array_len, init_count,
   4435                 });
   4436             }
   4437             return;
   4438         },
   4439         .Vector => {
   4440             const array_len = ty.arrayLen(mod);
   4441             if (init_count != array_len) {
   4442                 return sema.fail(block, src, "expected {d} vector elements; found {d}", .{
   4443                     array_len, init_count,
   4444                 });
   4445             }
   4446             return;
   4447         },
   4448         .Struct => if (ty.isTuple(mod)) {
   4449             try sema.resolveTypeFields(ty);
   4450             const array_len = ty.arrayLen(mod);
   4451             if (init_count > array_len) {
   4452                 return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{
   4453                     array_len, init_count,
   4454                 });
   4455             }
   4456             return;
   4457         },
   4458         else => {},
   4459     }
   4460     return sema.failWithArrayInitNotSupported(block, ty_src, ty);
   4461 }
   4462 
   4463 fn zirValidateStructInitTy(
   4464     sema: *Sema,
   4465     block: *Block,
   4466     inst: Zir.Inst.Index,
   4467     is_result_ty: bool,
   4468 ) CompileError!void {
   4469     const mod = sema.mod;
   4470     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4471     const src = inst_data.src();
   4472     const ty = sema.resolveType(block, src, inst_data.operand) catch |err| switch (err) {
   4473         // It's okay for the type to be unknown: this will result in an anonymous struct init.
   4474         error.GenericPoison => return,
   4475         else => |e| return e,
   4476     };
   4477     const struct_ty = if (is_result_ty) ty.optEuBaseType(mod) else ty;
   4478 
   4479     switch (struct_ty.zigTypeTag(mod)) {
   4480         .Struct, .Union => return,
   4481         else => {},
   4482     }
   4483     return sema.failWithStructInitNotSupported(block, src, struct_ty);
   4484 }
   4485 
   4486 fn zirValidatePtrStructInit(
   4487     sema: *Sema,
   4488     block: *Block,
   4489     inst: Zir.Inst.Index,
   4490 ) CompileError!void {
   4491     const tracy = trace(@src());
   4492     defer tracy.end();
   4493 
   4494     const mod = sema.mod;
   4495     const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4496     const init_src = validate_inst.src();
   4497     const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
   4498     const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len);
   4499     const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node;
   4500     const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
   4501     const object_ptr = try sema.resolveInst(field_ptr_extra.lhs);
   4502     const agg_ty = sema.typeOf(object_ptr).childType(mod).optEuBaseType(mod);
   4503     switch (agg_ty.zigTypeTag(mod)) {
   4504         .Struct => return sema.validateStructInit(
   4505             block,
   4506             agg_ty,
   4507             init_src,
   4508             instrs,
   4509         ),
   4510         .Union => return sema.validateUnionInit(
   4511             block,
   4512             agg_ty,
   4513             init_src,
   4514             instrs,
   4515             object_ptr,
   4516         ),
   4517         else => unreachable,
   4518     }
   4519 }
   4520 
   4521 fn validateUnionInit(
   4522     sema: *Sema,
   4523     block: *Block,
   4524     union_ty: Type,
   4525     init_src: LazySrcLoc,
   4526     instrs: []const Zir.Inst.Index,
   4527     union_ptr: Air.Inst.Ref,
   4528 ) CompileError!void {
   4529     const mod = sema.mod;
   4530     const gpa = sema.gpa;
   4531 
   4532     if (instrs.len != 1) {
   4533         const msg = msg: {
   4534             const msg = try sema.errMsg(
   4535                 block,
   4536                 init_src,
   4537                 "cannot initialize multiple union fields at once; unions can only have one active field",
   4538                 .{},
   4539             );
   4540             errdefer msg.destroy(gpa);
   4541 
   4542             for (instrs[1..]) |inst| {
   4543                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4544                 const inst_src: LazySrcLoc = .{ .node_offset_initializer = inst_data.src_node };
   4545                 try sema.errNote(block, inst_src, msg, "additional initializer here", .{});
   4546             }
   4547             try sema.addDeclaredHereNote(msg, union_ty);
   4548             break :msg msg;
   4549         };
   4550         return sema.failWithOwnedErrorMsg(block, msg);
   4551     }
   4552 
   4553     if (block.is_comptime and
   4554         (try sema.resolveDefinedValue(block, init_src, union_ptr)) != null)
   4555     {
   4556         // In this case, comptime machinery already did everything. No work to do here.
   4557         return;
   4558     }
   4559 
   4560     const field_ptr = instrs[0];
   4561     const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
   4562     const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node };
   4563     const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
   4564     const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_ptr_extra.field_name_start));
   4565     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
   4566     const air_tags = sema.air_instructions.items(.tag);
   4567     const air_datas = sema.air_instructions.items(.data);
   4568     const field_ptr_ref = sema.inst_map.get(field_ptr).?;
   4569 
   4570     // Our task here is to determine if the union is comptime-known. In such case,
   4571     // we erase the runtime AIR instructions for initializing the union, and replace
   4572     // the mapping with the comptime value. Either way, we will need to populate the tag.
   4573 
   4574     // We expect to see something like this in the current block AIR:
   4575     //   %a = alloc(*const U)
   4576     //   %b = bitcast(*U, %a)
   4577     //   %c = field_ptr(..., %b)
   4578     //   %e!= store(%c!, %d!)
   4579     // If %d is a comptime operand, the union is comptime.
   4580     // If the union is comptime, we want `first_block_index`
   4581     // to point at %c so that the bitcast becomes the last instruction in the block.
   4582     //
   4583     // In the case of a comptime-known pointer to a union, the
   4584     // the field_ptr instruction is missing, so we have to pattern-match
   4585     // based only on the store instructions.
   4586     // `first_block_index` needs to point to the `field_ptr` if it exists;
   4587     // the `store` otherwise.
   4588     var first_block_index = block.instructions.items.len;
   4589     var block_index = block.instructions.items.len - 1;
   4590     var init_val: ?Value = null;
   4591     while (block_index > 0) : (block_index -= 1) {
   4592         const store_inst = block.instructions.items[block_index];
   4593         if (store_inst.toRef() == field_ptr_ref) break;
   4594         switch (air_tags[@intFromEnum(store_inst)]) {
   4595             .store, .store_safe => {},
   4596             else => continue,
   4597         }
   4598         const bin_op = air_datas[@intFromEnum(store_inst)].bin_op;
   4599         var ptr_ref = bin_op.lhs;
   4600         if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
   4601             ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
   4602         };
   4603         if (ptr_ref != field_ptr_ref) continue;
   4604         first_block_index = @min(if (field_ptr_ref.toIndex()) |field_ptr_inst|
   4605             std.mem.lastIndexOfScalar(
   4606                 Air.Inst.Index,
   4607                 block.instructions.items[0..block_index],
   4608                 field_ptr_inst,
   4609             ).?
   4610         else
   4611             block_index, first_block_index);
   4612         init_val = try sema.resolveValue(bin_op.rhs);
   4613         break;
   4614     }
   4615 
   4616     const tag_ty = union_ty.unionTagTypeHypothetical(mod);
   4617     const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index);
   4618 
   4619     if (init_val) |val| {
   4620         // Our task is to delete all the `field_ptr` and `store` instructions, and insert
   4621         // instead a single `store` to the result ptr with a comptime union value.
   4622         block_index = first_block_index;
   4623         for (block.instructions.items[first_block_index..]) |cur_inst| {
   4624             switch (air_tags[@intFromEnum(cur_inst)]) {
   4625                 .struct_field_ptr,
   4626                 .struct_field_ptr_index_0,
   4627                 .struct_field_ptr_index_1,
   4628                 .struct_field_ptr_index_2,
   4629                 .struct_field_ptr_index_3,
   4630                 => if (cur_inst.toRef() == field_ptr_ref) continue,
   4631                 .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == field_ptr_ref) continue,
   4632                 .store, .store_safe => {
   4633                     var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs;
   4634                     if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
   4635                         ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
   4636                     };
   4637                     if (ptr_ref == field_ptr_ref) continue;
   4638                 },
   4639                 else => {},
   4640             }
   4641             block.instructions.items[block_index] = cur_inst;
   4642             block_index += 1;
   4643         }
   4644         block.instructions.shrinkRetainingCapacity(block_index);
   4645 
   4646         const union_val = try mod.intern(.{ .un = .{
   4647             .ty = union_ty.toIntern(),
   4648             .tag = tag_val.toIntern(),
   4649             .val = val.toIntern(),
   4650         } });
   4651         const union_init = Air.internedToRef(union_val);
   4652         try sema.storePtr2(block, init_src, union_ptr, init_src, union_init, init_src, .store);
   4653         return;
   4654     } else if (try sema.typeRequiresComptime(union_ty)) {
   4655         return sema.failWithNeededComptime(block, field_ptr_data.src(), .{
   4656             .needed_comptime_reason = "initializer of comptime only union must be comptime-known",
   4657         });
   4658     }
   4659 
   4660     const new_tag = Air.internedToRef(tag_val.toIntern());
   4661     const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, new_tag);
   4662     try sema.checkComptimeKnownStore(block, set_tag_inst);
   4663 }
   4664 
   4665 fn validateStructInit(
   4666     sema: *Sema,
   4667     block: *Block,
   4668     struct_ty: Type,
   4669     init_src: LazySrcLoc,
   4670     instrs: []const Zir.Inst.Index,
   4671 ) CompileError!void {
   4672     const mod = sema.mod;
   4673     const gpa = sema.gpa;
   4674     const ip = &mod.intern_pool;
   4675 
   4676     const field_indices = try gpa.alloc(u32, instrs.len);
   4677     defer gpa.free(field_indices);
   4678 
   4679     // Maps field index to field_ptr index of where it was already initialized.
   4680     const found_fields = try gpa.alloc(Zir.Inst.OptionalIndex, struct_ty.structFieldCount(mod));
   4681     defer gpa.free(found_fields);
   4682     @memset(found_fields, .none);
   4683 
   4684     var struct_ptr_zir_ref: Zir.Inst.Ref = undefined;
   4685 
   4686     for (instrs, field_indices) |field_ptr, *field_index| {
   4687         const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
   4688         const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node };
   4689         const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
   4690         struct_ptr_zir_ref = field_ptr_extra.lhs;
   4691         const field_name = try ip.getOrPutString(
   4692             gpa,
   4693             sema.code.nullTerminatedString(field_ptr_extra.field_name_start),
   4694         );
   4695         field_index.* = if (struct_ty.isTuple(mod))
   4696             try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
   4697         else
   4698             try sema.structFieldIndex(block, struct_ty, field_name, field_src);
   4699         assert(found_fields[field_index.*] == .none);
   4700         found_fields[field_index.*] = field_ptr.toOptional();
   4701     }
   4702 
   4703     var root_msg: ?*Module.ErrorMsg = null;
   4704     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
   4705 
   4706     const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref);
   4707     if (block.is_comptime and
   4708         (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null)
   4709     {
   4710         try sema.resolveStructLayout(struct_ty);
   4711         // In this case the only thing we need to do is evaluate the implicit
   4712         // store instructions for default field values, and report any missing fields.
   4713         // Avoid the cost of the extra machinery for detecting a comptime struct init value.
   4714         for (found_fields, 0..) |field_ptr, i_usize| {
   4715             const i: u32 = @intCast(i_usize);
   4716             if (field_ptr != .none) continue;
   4717 
   4718             try sema.resolveStructFieldInits(struct_ty);
   4719             const default_val = struct_ty.structFieldDefaultValue(i, mod);
   4720             if (default_val.toIntern() == .unreachable_value) {
   4721                 const field_name = struct_ty.structFieldName(i, mod).unwrap() orelse {
   4722                     const template = "missing tuple field with index {d}";
   4723                     if (root_msg) |msg| {
   4724                         try sema.errNote(block, init_src, msg, template, .{i});
   4725                     } else {
   4726                         root_msg = try sema.errMsg(block, init_src, template, .{i});
   4727                     }
   4728                     continue;
   4729                 };
   4730                 const template = "missing struct field: {}";
   4731                 const args = .{field_name.fmt(ip)};
   4732                 if (root_msg) |msg| {
   4733                     try sema.errNote(block, init_src, msg, template, args);
   4734                 } else {
   4735                     root_msg = try sema.errMsg(block, init_src, template, args);
   4736                 }
   4737                 continue;
   4738             }
   4739 
   4740             const field_src = init_src; // TODO better source location
   4741             const default_field_ptr = if (struct_ty.isTuple(mod))
   4742                 try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true)
   4743             else
   4744                 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), field_src, struct_ty, true);
   4745             const init = Air.internedToRef(default_val.toIntern());
   4746             try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
   4747         }
   4748 
   4749         if (root_msg) |msg| {
   4750             if (mod.typeToStruct(struct_ty)) |struct_type| {
   4751                 const decl = mod.declPtr(struct_type.decl.unwrap().?);
   4752                 const fqn = try decl.getFullyQualifiedName(mod);
   4753                 try mod.errNoteNonLazy(
   4754                     decl.srcLoc(mod),
   4755                     msg,
   4756                     "struct '{}' declared here",
   4757                     .{fqn.fmt(ip)},
   4758                 );
   4759             }
   4760             root_msg = null;
   4761             return sema.failWithOwnedErrorMsg(block, msg);
   4762         }
   4763 
   4764         return;
   4765     }
   4766 
   4767     var struct_is_comptime = true;
   4768     var first_block_index = block.instructions.items.len;
   4769 
   4770     const require_comptime = try sema.typeRequiresComptime(struct_ty);
   4771     const air_tags = sema.air_instructions.items(.tag);
   4772     const air_datas = sema.air_instructions.items(.data);
   4773 
   4774     try sema.resolveStructFieldInits(struct_ty);
   4775 
   4776     // We collect the comptime field values in case the struct initialization
   4777     // ends up being comptime-known.
   4778     const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(mod));
   4779 
   4780     field: for (found_fields, 0..) |opt_field_ptr, i_usize| {
   4781         const i: u32 = @intCast(i_usize);
   4782         if (opt_field_ptr.unwrap()) |field_ptr| {
   4783             // Determine whether the value stored to this pointer is comptime-known.
   4784             const field_ty = struct_ty.structFieldType(i, mod);
   4785             if (try sema.typeHasOnePossibleValue(field_ty)) |opv| {
   4786                 field_values[i] = opv.toIntern();
   4787                 continue;
   4788             }
   4789 
   4790             const field_ptr_ref = sema.inst_map.get(field_ptr).?;
   4791 
   4792             //std.debug.print("validateStructInit (field_ptr_ref=%{d}):\n", .{field_ptr_ref});
   4793             //for (block.instructions.items) |item| {
   4794             //    std.debug.print("  %{d} = {s}\n", .{item, @tagName(air_tags[@intFromEnum(item)])});
   4795             //}
   4796 
   4797             // We expect to see something like this in the current block AIR:
   4798             //   %a = field_ptr(...)
   4799             //   store(%a, %b)
   4800             // With an optional bitcast between the store and the field_ptr.
   4801             // If %b is a comptime operand, this field is comptime.
   4802             //
   4803             // However, in the case of a comptime-known pointer to a struct, the
   4804             // the field_ptr instruction is missing, so we have to pattern-match
   4805             // based only on the store instructions.
   4806             // `first_block_index` needs to point to the `field_ptr` if it exists;
   4807             // the `store` otherwise.
   4808 
   4809             // Possible performance enhancement: save the `block_index` between iterations
   4810             // of the for loop.
   4811             var block_index = block.instructions.items.len;
   4812             while (block_index > 0) {
   4813                 block_index -= 1;
   4814                 const store_inst = block.instructions.items[block_index];
   4815                 if (store_inst.toRef() == field_ptr_ref) {
   4816                     struct_is_comptime = false;
   4817                     continue :field;
   4818                 }
   4819                 switch (air_tags[@intFromEnum(store_inst)]) {
   4820                     .store, .store_safe => {},
   4821                     else => continue,
   4822                 }
   4823                 const bin_op = air_datas[@intFromEnum(store_inst)].bin_op;
   4824                 var ptr_ref = bin_op.lhs;
   4825                 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
   4826                     ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
   4827                 };
   4828                 if (ptr_ref != field_ptr_ref) continue;
   4829                 first_block_index = @min(if (field_ptr_ref.toIndex()) |field_ptr_inst|
   4830                     std.mem.lastIndexOfScalar(
   4831                         Air.Inst.Index,
   4832                         block.instructions.items[0..block_index],
   4833                         field_ptr_inst,
   4834                     ).?
   4835                 else
   4836                     block_index, first_block_index);
   4837                 if (try sema.resolveValue(bin_op.rhs)) |val| {
   4838                     field_values[i] = val.toIntern();
   4839                 } else if (require_comptime) {
   4840                     const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
   4841                     return sema.failWithNeededComptime(block, field_ptr_data.src(), .{
   4842                         .needed_comptime_reason = "initializer of comptime only struct must be comptime-known",
   4843                     });
   4844                 } else {
   4845                     struct_is_comptime = false;
   4846                 }
   4847                 continue :field;
   4848             }
   4849             struct_is_comptime = false;
   4850             continue :field;
   4851         }
   4852 
   4853         const default_val = struct_ty.structFieldDefaultValue(i, mod);
   4854         if (default_val.toIntern() == .unreachable_value) {
   4855             const field_name = struct_ty.structFieldName(i, mod).unwrap() orelse {
   4856                 const template = "missing tuple field with index {d}";
   4857                 if (root_msg) |msg| {
   4858                     try sema.errNote(block, init_src, msg, template, .{i});
   4859                 } else {
   4860                     root_msg = try sema.errMsg(block, init_src, template, .{i});
   4861                 }
   4862                 continue;
   4863             };
   4864             const template = "missing struct field: {}";
   4865             const args = .{field_name.fmt(ip)};
   4866             if (root_msg) |msg| {
   4867                 try sema.errNote(block, init_src, msg, template, args);
   4868             } else {
   4869                 root_msg = try sema.errMsg(block, init_src, template, args);
   4870             }
   4871             continue;
   4872         }
   4873         field_values[i] = default_val.toIntern();
   4874     }
   4875 
   4876     if (root_msg) |msg| {
   4877         if (mod.typeToStruct(struct_ty)) |struct_type| {
   4878             const decl = mod.declPtr(struct_type.decl.unwrap().?);
   4879             const fqn = try decl.getFullyQualifiedName(mod);
   4880             try mod.errNoteNonLazy(
   4881                 decl.srcLoc(mod),
   4882                 msg,
   4883                 "struct '{}' declared here",
   4884                 .{fqn.fmt(ip)},
   4885             );
   4886         }
   4887         root_msg = null;
   4888         return sema.failWithOwnedErrorMsg(block, msg);
   4889     }
   4890 
   4891     if (struct_is_comptime) {
   4892         // Our task is to delete all the `field_ptr` and `store` instructions, and insert
   4893         // instead a single `store` to the struct_ptr with a comptime struct value.
   4894         var init_index: usize = 0;
   4895         var field_ptr_ref = Air.Inst.Ref.none;
   4896         var block_index = first_block_index;
   4897         for (block.instructions.items[first_block_index..]) |cur_inst| {
   4898             while (field_ptr_ref == .none and init_index < instrs.len) : (init_index += 1) {
   4899                 const field_ty = struct_ty.structFieldType(field_indices[init_index], mod);
   4900                 if (try field_ty.onePossibleValue(mod)) |_| continue;
   4901                 field_ptr_ref = sema.inst_map.get(instrs[init_index]).?;
   4902             }
   4903             switch (air_tags[@intFromEnum(cur_inst)]) {
   4904                 .struct_field_ptr,
   4905                 .struct_field_ptr_index_0,
   4906                 .struct_field_ptr_index_1,
   4907                 .struct_field_ptr_index_2,
   4908                 .struct_field_ptr_index_3,
   4909                 => if (cur_inst.toRef() == field_ptr_ref) continue,
   4910                 .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == field_ptr_ref) continue,
   4911                 .store, .store_safe => {
   4912                     var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs;
   4913                     if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
   4914                         ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
   4915                     };
   4916                     if (ptr_ref == field_ptr_ref) {
   4917                         field_ptr_ref = .none;
   4918                         continue;
   4919                     }
   4920                 },
   4921                 else => {},
   4922             }
   4923             block.instructions.items[block_index] = cur_inst;
   4924             block_index += 1;
   4925         }
   4926         block.instructions.shrinkRetainingCapacity(block_index);
   4927 
   4928         const struct_val = try mod.intern(.{ .aggregate = .{
   4929             .ty = struct_ty.toIntern(),
   4930             .storage = .{ .elems = field_values },
   4931         } });
   4932         const struct_init = Air.internedToRef(struct_val);
   4933         try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store);
   4934         return;
   4935     }
   4936     try sema.resolveStructLayout(struct_ty);
   4937 
   4938     // Our task is to insert `store` instructions for all the default field values.
   4939     for (found_fields, 0..) |field_ptr, i| {
   4940         if (field_ptr != .none) continue;
   4941 
   4942         const field_src = init_src; // TODO better source location
   4943         const default_field_ptr = if (struct_ty.isTuple(mod))
   4944             try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true)
   4945         else
   4946             try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), field_src, struct_ty, true);
   4947         try sema.checkKnownAllocPtr(struct_ptr, default_field_ptr);
   4948         const init = Air.internedToRef(field_values[i]);
   4949         try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
   4950     }
   4951 }
   4952 
   4953 fn zirValidatePtrArrayInit(
   4954     sema: *Sema,
   4955     block: *Block,
   4956     inst: Zir.Inst.Index,
   4957 ) CompileError!void {
   4958     const mod = sema.mod;
   4959     const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4960     const init_src = validate_inst.src();
   4961     const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
   4962     const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len);
   4963     const first_elem_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node;
   4964     const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data;
   4965     const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr);
   4966     const array_ty = sema.typeOf(array_ptr).childType(mod).optEuBaseType(mod);
   4967     const array_len = array_ty.arrayLen(mod);
   4968 
   4969     if (instrs.len != array_len) switch (array_ty.zigTypeTag(mod)) {
   4970         .Struct => {
   4971             var root_msg: ?*Module.ErrorMsg = null;
   4972             errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
   4973 
   4974             var i = instrs.len;
   4975             while (i < array_len) : (i += 1) {
   4976                 const default_val = array_ty.structFieldDefaultValue(i, mod);
   4977                 if (default_val.toIntern() == .unreachable_value) {
   4978                     const template = "missing tuple field with index {d}";
   4979                     if (root_msg) |msg| {
   4980                         try sema.errNote(block, init_src, msg, template, .{i});
   4981                     } else {
   4982                         root_msg = try sema.errMsg(block, init_src, template, .{i});
   4983                     }
   4984                 }
   4985             }
   4986 
   4987             if (root_msg) |msg| {
   4988                 root_msg = null;
   4989                 return sema.failWithOwnedErrorMsg(block, msg);
   4990             }
   4991         },
   4992         .Array => {
   4993             return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
   4994                 array_len, instrs.len,
   4995             });
   4996         },
   4997         .Vector => {
   4998             return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
   4999                 array_len, instrs.len,
   5000             });
   5001         },
   5002         else => unreachable,
   5003     };
   5004 
   5005     if (block.is_comptime and
   5006         (try sema.resolveDefinedValue(block, init_src, array_ptr)) != null)
   5007     {
   5008         // In this case the comptime machinery will have evaluated the store instructions
   5009         // at comptime so we have almost nothing to do here. However, in case of a
   5010         // sentinel-terminated array, the sentinel will not have been populated by
   5011         // any ZIR instructions at comptime; we need to do that here.
   5012         if (array_ty.sentinel(mod)) |sentinel_val| {
   5013             const array_len_ref = try mod.intRef(Type.usize, array_len);
   5014             const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true);
   5015             const sentinel = Air.internedToRef(sentinel_val.toIntern());
   5016             try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store);
   5017         }
   5018         return;
   5019     }
   5020 
   5021     // If the array has one possible value, the value is always comptime-known.
   5022     if (try sema.typeHasOnePossibleValue(array_ty)) |array_opv| {
   5023         const array_init = Air.internedToRef(array_opv.toIntern());
   5024         try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
   5025         return;
   5026     }
   5027 
   5028     var array_is_comptime = true;
   5029     var first_block_index = block.instructions.items.len;
   5030 
   5031     // Collect the comptime element values in case the array literal ends up
   5032     // being comptime-known.
   5033     const element_vals = try sema.arena.alloc(
   5034         InternPool.Index,
   5035         try sema.usizeCast(block, init_src, array_len),
   5036     );
   5037     const air_tags = sema.air_instructions.items(.tag);
   5038     const air_datas = sema.air_instructions.items(.data);
   5039 
   5040     outer: for (instrs, 0..) |elem_ptr, i| {
   5041         // Determine whether the value stored to this pointer is comptime-known.
   5042 
   5043         if (array_ty.isTuple(mod)) {
   5044             if (array_ty.structFieldIsComptime(i, mod))
   5045                 try sema.resolveStructFieldInits(array_ty);
   5046             if (try array_ty.structFieldValueComptime(mod, i)) |opv| {
   5047                 element_vals[i] = opv.toIntern();
   5048                 continue;
   5049             }
   5050         }
   5051 
   5052         const elem_ptr_ref = sema.inst_map.get(elem_ptr).?;
   5053 
   5054         // We expect to see something like this in the current block AIR:
   5055         //   %a = elem_ptr(...)
   5056         //   store(%a, %b)
   5057         // With an optional bitcast between the store and the elem_ptr.
   5058         // If %b is a comptime operand, this element is comptime.
   5059         //
   5060         // However, in the case of a comptime-known pointer to an array, the
   5061         // the elem_ptr instruction is missing, so we have to pattern-match
   5062         // based only on the store instructions.
   5063         // `first_block_index` needs to point to the `elem_ptr` if it exists;
   5064         // the `store` otherwise.
   5065         //
   5066         // This is nearly identical to similar logic in `validateStructInit`.
   5067 
   5068         // Possible performance enhancement: save the `block_index` between iterations
   5069         // of the for loop.
   5070         var block_index = block.instructions.items.len;
   5071         while (block_index > 0) {
   5072             block_index -= 1;
   5073             const store_inst = block.instructions.items[block_index];
   5074             if (store_inst.toRef() == elem_ptr_ref) {
   5075                 array_is_comptime = false;
   5076                 continue :outer;
   5077             }
   5078             switch (air_tags[@intFromEnum(store_inst)]) {
   5079                 .store, .store_safe => {},
   5080                 else => continue,
   5081             }
   5082             const bin_op = air_datas[@intFromEnum(store_inst)].bin_op;
   5083             var ptr_ref = bin_op.lhs;
   5084             if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
   5085                 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
   5086             };
   5087             if (ptr_ref != elem_ptr_ref) continue;
   5088             first_block_index = @min(if (elem_ptr_ref.toIndex()) |elem_ptr_inst|
   5089                 std.mem.lastIndexOfScalar(
   5090                     Air.Inst.Index,
   5091                     block.instructions.items[0..block_index],
   5092                     elem_ptr_inst,
   5093                 ).?
   5094             else
   5095                 block_index, first_block_index);
   5096             if (try sema.resolveValue(bin_op.rhs)) |val| {
   5097                 element_vals[i] = val.toIntern();
   5098             } else {
   5099                 array_is_comptime = false;
   5100             }
   5101             continue :outer;
   5102         }
   5103         array_is_comptime = false;
   5104         continue :outer;
   5105     }
   5106 
   5107     if (array_is_comptime) {
   5108         if (try sema.resolveDefinedValue(block, init_src, array_ptr)) |ptr_val| {
   5109             switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) {
   5110                 .ptr => |ptr| switch (ptr.addr) {
   5111                     .comptime_field => return, // This store was validated by the individual elem ptrs.
   5112                     else => {},
   5113                 },
   5114                 else => {},
   5115             }
   5116         }
   5117 
   5118         // Our task is to delete all the `elem_ptr` and `store` instructions, and insert
   5119         // instead a single `store` to the array_ptr with a comptime struct value.
   5120         var elem_index: usize = 0;
   5121         var elem_ptr_ref = Air.Inst.Ref.none;
   5122         var block_index = first_block_index;
   5123         for (block.instructions.items[first_block_index..]) |cur_inst| {
   5124             while (elem_ptr_ref == .none and elem_index < instrs.len) : (elem_index += 1) {
   5125                 if (array_ty.isTuple(mod) and array_ty.structFieldIsComptime(elem_index, mod)) continue;
   5126                 elem_ptr_ref = sema.inst_map.get(instrs[elem_index]).?;
   5127             }
   5128             switch (air_tags[@intFromEnum(cur_inst)]) {
   5129                 .ptr_elem_ptr => if (cur_inst.toRef() == elem_ptr_ref) continue,
   5130                 .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == elem_ptr_ref) continue,
   5131                 .store, .store_safe => {
   5132                     var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs;
   5133                     if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
   5134                         ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
   5135                     };
   5136                     if (ptr_ref == elem_ptr_ref) {
   5137                         elem_ptr_ref = .none;
   5138                         continue;
   5139                     }
   5140                 },
   5141                 else => {},
   5142             }
   5143             block.instructions.items[block_index] = cur_inst;
   5144             block_index += 1;
   5145         }
   5146         block.instructions.shrinkRetainingCapacity(block_index);
   5147 
   5148         const array_val = try mod.intern(.{ .aggregate = .{
   5149             .ty = array_ty.toIntern(),
   5150             .storage = .{ .elems = element_vals },
   5151         } });
   5152         const array_init = Air.internedToRef(array_val);
   5153         try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
   5154     }
   5155 }
   5156 
   5157 fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5158     const mod = sema.mod;
   5159     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5160     const src = inst_data.src();
   5161     const operand = try sema.resolveInst(inst_data.operand);
   5162     const operand_ty = sema.typeOf(operand);
   5163 
   5164     if (operand_ty.zigTypeTag(mod) != .Pointer) {
   5165         return sema.fail(block, src, "cannot dereference non-pointer type '{}'", .{operand_ty.fmt(mod)});
   5166     } else switch (operand_ty.ptrSize(mod)) {
   5167         .One, .C => {},
   5168         .Many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{}'", .{operand_ty.fmt(mod)}),
   5169         .Slice => return sema.fail(block, src, "index syntax required for slice type '{}'", .{operand_ty.fmt(mod)}),
   5170     }
   5171 
   5172     if ((try sema.typeHasOnePossibleValue(operand_ty.childType(mod))) != null) {
   5173         // No need to validate the actual pointer value, we don't need it!
   5174         return;
   5175     }
   5176 
   5177     const elem_ty = operand_ty.elemType2(mod);
   5178     if (try sema.resolveValue(operand)) |val| {
   5179         if (val.isUndef(mod)) {
   5180             return sema.fail(block, src, "cannot dereference undefined value", .{});
   5181         }
   5182     } else if (try sema.typeRequiresComptime(elem_ty)) {
   5183         const msg = msg: {
   5184             const msg = try sema.errMsg(
   5185                 block,
   5186                 src,
   5187                 "values of type '{}' must be comptime-known, but operand value is runtime-known",
   5188                 .{elem_ty.fmt(mod)},
   5189             );
   5190             errdefer msg.destroy(sema.gpa);
   5191 
   5192             const src_decl = mod.declPtr(block.src_decl);
   5193             try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), elem_ty);
   5194             break :msg msg;
   5195         };
   5196         return sema.failWithOwnedErrorMsg(block, msg);
   5197     }
   5198 }
   5199 
   5200 fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5201     const mod = sema.mod;
   5202     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5203     const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data;
   5204     const src = inst_data.src();
   5205     const destructure_src = LazySrcLoc.nodeOffset(extra.destructure_node);
   5206     const operand = try sema.resolveInst(extra.operand);
   5207     const operand_ty = sema.typeOf(operand);
   5208 
   5209     const can_destructure = switch (operand_ty.zigTypeTag(mod)) {
   5210         .Array, .Vector => true,
   5211         .Struct => operand_ty.isTuple(mod),
   5212         else => false,
   5213     };
   5214 
   5215     if (!can_destructure) {
   5216         return sema.failWithOwnedErrorMsg(block, msg: {
   5217             const msg = try sema.errMsg(block, src, "type '{}' cannot be destructured", .{operand_ty.fmt(mod)});
   5218             errdefer msg.destroy(sema.gpa);
   5219             try sema.errNote(block, destructure_src, msg, "result destructured here", .{});
   5220             break :msg msg;
   5221         });
   5222     }
   5223 
   5224     if (operand_ty.arrayLen(mod) != extra.expect_len) {
   5225         return sema.failWithOwnedErrorMsg(block, msg: {
   5226             const msg = try sema.errMsg(block, src, "expected {} elements for destructure, found {}", .{
   5227                 extra.expect_len,
   5228                 operand_ty.arrayLen(mod),
   5229             });
   5230             errdefer msg.destroy(sema.gpa);
   5231             try sema.errNote(block, destructure_src, msg, "result destructured here", .{});
   5232             break :msg msg;
   5233         });
   5234     }
   5235 }
   5236 
   5237 fn failWithBadMemberAccess(
   5238     sema: *Sema,
   5239     block: *Block,
   5240     agg_ty: Type,
   5241     field_src: LazySrcLoc,
   5242     field_name: InternPool.NullTerminatedString,
   5243 ) CompileError {
   5244     const mod = sema.mod;
   5245     const kw_name = switch (agg_ty.zigTypeTag(mod)) {
   5246         .Union => "union",
   5247         .Struct => "struct",
   5248         .Opaque => "opaque",
   5249         .Enum => "enum",
   5250         else => unreachable,
   5251     };
   5252     const msg = msg: {
   5253         const msg = blk: {
   5254             if (agg_ty.getOwnerDeclOrNull(mod)) |some| if (mod.declIsRoot(some)) {
   5255                 break :blk try sema.errMsg(block, field_src, "root struct of file '{}' has no member named '{}'", .{
   5256                     agg_ty.fmt(mod), field_name.fmt(&mod.intern_pool),
   5257                 });
   5258             };
   5259 
   5260             break :blk try sema.errMsg(block, field_src, "{s} '{}' has no member named '{}'", .{
   5261                 kw_name, agg_ty.fmt(mod), field_name.fmt(&mod.intern_pool),
   5262             });
   5263         };
   5264 
   5265         errdefer msg.destroy(sema.gpa);
   5266         try sema.addDeclaredHereNote(msg, agg_ty);
   5267         break :msg msg;
   5268     };
   5269     return sema.failWithOwnedErrorMsg(block, msg);
   5270 }
   5271 
   5272 fn failWithBadStructFieldAccess(
   5273     sema: *Sema,
   5274     block: *Block,
   5275     struct_type: InternPool.Key.StructType,
   5276     field_src: LazySrcLoc,
   5277     field_name: InternPool.NullTerminatedString,
   5278 ) CompileError {
   5279     const mod = sema.mod;
   5280     const gpa = sema.gpa;
   5281     const decl = mod.declPtr(struct_type.decl.unwrap().?);
   5282     const fqn = try decl.getFullyQualifiedName(mod);
   5283 
   5284     const msg = msg: {
   5285         const msg = try sema.errMsg(
   5286             block,
   5287             field_src,
   5288             "no field named '{}' in struct '{}'",
   5289             .{ field_name.fmt(&mod.intern_pool), fqn.fmt(&mod.intern_pool) },
   5290         );
   5291         errdefer msg.destroy(gpa);
   5292         try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "struct declared here", .{});
   5293         break :msg msg;
   5294     };
   5295     return sema.failWithOwnedErrorMsg(block, msg);
   5296 }
   5297 
   5298 fn failWithBadUnionFieldAccess(
   5299     sema: *Sema,
   5300     block: *Block,
   5301     union_obj: InternPool.UnionType,
   5302     field_src: LazySrcLoc,
   5303     field_name: InternPool.NullTerminatedString,
   5304 ) CompileError {
   5305     const mod = sema.mod;
   5306     const gpa = sema.gpa;
   5307 
   5308     const decl = mod.declPtr(union_obj.decl);
   5309     const fqn = try decl.getFullyQualifiedName(mod);
   5310 
   5311     const msg = msg: {
   5312         const msg = try sema.errMsg(
   5313             block,
   5314             field_src,
   5315             "no field named '{}' in union '{}'",
   5316             .{ field_name.fmt(&mod.intern_pool), fqn.fmt(&mod.intern_pool) },
   5317         );
   5318         errdefer msg.destroy(gpa);
   5319         try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "union declared here", .{});
   5320         break :msg msg;
   5321     };
   5322     return sema.failWithOwnedErrorMsg(block, msg);
   5323 }
   5324 
   5325 fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !void {
   5326     const mod = sema.mod;
   5327     const src_loc = decl_ty.declSrcLocOrNull(mod) orelse return;
   5328     const category = switch (decl_ty.zigTypeTag(mod)) {
   5329         .Union => "union",
   5330         .Struct => "struct",
   5331         .Enum => "enum",
   5332         .Opaque => "opaque",
   5333         .ErrorSet => "error set",
   5334         else => unreachable,
   5335     };
   5336     try mod.errNoteNonLazy(src_loc, parent, "{s} declared here", .{category});
   5337 }
   5338 
   5339 fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5340     const tracy = trace(@src());
   5341     defer tracy.end();
   5342 
   5343     const src: LazySrcLoc = sema.src;
   5344     const bin_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
   5345     const ptr = try sema.resolveInst(bin_inst.lhs);
   5346     const operand = try sema.resolveInst(bin_inst.rhs);
   5347     const ptr_inst = ptr.toIndex().?;
   5348     const air_datas = sema.air_instructions.items(.data);
   5349 
   5350     switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) {
   5351         .inferred_alloc_comptime => {
   5352             const iac = &air_datas[@intFromEnum(ptr_inst)].inferred_alloc_comptime;
   5353             return sema.storeToInferredAllocComptime(block, src, operand, iac);
   5354         },
   5355         .inferred_alloc => {
   5356             const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?;
   5357             return sema.storeToInferredAlloc(block, ptr, operand, ia);
   5358         },
   5359         else => unreachable,
   5360     }
   5361 }
   5362 
   5363 fn storeToInferredAlloc(
   5364     sema: *Sema,
   5365     block: *Block,
   5366     ptr: Air.Inst.Ref,
   5367     operand: Air.Inst.Ref,
   5368     inferred_alloc: *InferredAlloc,
   5369 ) CompileError!void {
   5370     // Create a store instruction as a placeholder.  This will be replaced by a
   5371     // proper store sequence once we know the stored type.
   5372     const dummy_store = try block.addBinOp(.store, ptr, operand);
   5373     try sema.checkComptimeKnownStore(block, dummy_store);
   5374     // Add the stored instruction to the set we will use to resolve peer types
   5375     // for the inferred allocation.
   5376     try inferred_alloc.prongs.append(sema.arena, dummy_store.toIndex().?);
   5377 }
   5378 
   5379 fn storeToInferredAllocComptime(
   5380     sema: *Sema,
   5381     block: *Block,
   5382     src: LazySrcLoc,
   5383     operand: Air.Inst.Ref,
   5384     iac: *Air.Inst.Data.InferredAllocComptime,
   5385 ) CompileError!void {
   5386     const operand_ty = sema.typeOf(operand);
   5387     // There will be only one store_to_inferred_ptr because we are running at comptime.
   5388     // The alloc will turn into a Decl.
   5389     if (try sema.resolveValue(operand)) |operand_val| {
   5390         var anon_decl = try block.startAnonDecl(); // TODO: comptime value mutation without Decl
   5391         defer anon_decl.deinit();
   5392         iac.decl_index = try anon_decl.finish(operand_ty, operand_val, iac.alignment);
   5393         try sema.comptime_mutable_decls.append(iac.decl_index);
   5394         return;
   5395     }
   5396 
   5397     return sema.failWithNeededComptime(block, src, .{
   5398         .needed_comptime_reason = "value being stored to a comptime variable must be comptime-known",
   5399     });
   5400 }
   5401 
   5402 fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5403     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5404     const src = inst_data.src();
   5405     const quota: u32 = @intCast(try sema.resolveInt(block, src, inst_data.operand, Type.u32, .{
   5406         .needed_comptime_reason = "eval branch quota must be comptime-known",
   5407     }));
   5408     sema.branch_quota = @max(sema.branch_quota, quota);
   5409 }
   5410 
   5411 fn zirStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5412     const tracy = trace(@src());
   5413     defer tracy.end();
   5414 
   5415     const bin_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
   5416     const ptr = try sema.resolveInst(bin_inst.lhs);
   5417     const value = try sema.resolveInst(bin_inst.rhs);
   5418     return sema.storePtr(block, sema.src, ptr, value);
   5419 }
   5420 
   5421 fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5422     const tracy = trace(@src());
   5423     defer tracy.end();
   5424 
   5425     const mod = sema.mod;
   5426     const zir_tags = sema.code.instructions.items(.tag);
   5427     const zir_datas = sema.code.instructions.items(.data);
   5428     const inst_data = zir_datas[@intFromEnum(inst)].pl_node;
   5429     const src = inst_data.src();
   5430     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   5431     const ptr = try sema.resolveInst(extra.lhs);
   5432     const operand = try sema.resolveInst(extra.rhs);
   5433 
   5434     const is_ret = if (extra.lhs.toIndex()) |ptr_index|
   5435         zir_tags[@intFromEnum(ptr_index)] == .ret_ptr
   5436     else
   5437         false;
   5438 
   5439     // Check for the possibility of this pattern:
   5440     //   %a = ret_ptr
   5441     //   %b = store(%a, %c)
   5442     // Where %c is an error union or error set. In such case we need to add
   5443     // to the current function's inferred error set, if any.
   5444     if (is_ret and sema.fn_ret_ty_ies != null) switch (sema.typeOf(operand).zigTypeTag(mod)) {
   5445         .ErrorUnion, .ErrorSet => try sema.addToInferredErrorSet(operand),
   5446         else => {},
   5447     };
   5448 
   5449     const ptr_src: LazySrcLoc = .{ .node_offset_store_ptr = inst_data.src_node };
   5450     const operand_src: LazySrcLoc = .{ .node_offset_store_operand = inst_data.src_node };
   5451     const air_tag: Air.Inst.Tag = if (is_ret)
   5452         .ret_ptr
   5453     else if (block.wantSafety())
   5454         .store_safe
   5455     else
   5456         .store;
   5457     return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
   5458 }
   5459 
   5460 fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5461     const bytes = sema.code.instructions.items(.data)[@intFromEnum(inst)].str.get(sema.code);
   5462     return sema.addStrLitNoAlias(bytes);
   5463 }
   5464 
   5465 fn addStrLit(sema: *Sema, bytes: []const u8) CompileError!Air.Inst.Ref {
   5466     const duped_bytes = try sema.arena.dupe(u8, bytes);
   5467     return addStrLitNoAlias(sema, duped_bytes);
   5468 }
   5469 
   5470 /// Safe to call when `bytes` does not point into `InternPool`.
   5471 fn addStrLitNoAlias(sema: *Sema, bytes: []const u8) CompileError!Air.Inst.Ref {
   5472     const mod = sema.mod;
   5473     const array_ty = try mod.arrayType(.{
   5474         .len = bytes.len,
   5475         .sentinel = .zero_u8,
   5476         .child = .u8_type,
   5477     });
   5478     const val = try mod.intern(.{ .aggregate = .{
   5479         .ty = array_ty.toIntern(),
   5480         .storage = .{ .bytes = bytes },
   5481     } });
   5482     return anonDeclRef(sema, val);
   5483 }
   5484 
   5485 fn anonDeclRef(sema: *Sema, val: InternPool.Index) CompileError!Air.Inst.Ref {
   5486     return Air.internedToRef(try refValue(sema, val));
   5487 }
   5488 
   5489 fn refValue(sema: *Sema, val: InternPool.Index) CompileError!InternPool.Index {
   5490     const mod = sema.mod;
   5491     const ptr_ty = (try sema.ptrType(.{
   5492         .child = mod.intern_pool.typeOf(val),
   5493         .flags = .{
   5494             .alignment = .none,
   5495             .is_const = true,
   5496             .address_space = .generic,
   5497         },
   5498     })).toIntern();
   5499     return mod.intern(.{ .ptr = .{
   5500         .ty = ptr_ty,
   5501         .addr = .{ .anon_decl = .{
   5502             .val = val,
   5503             .orig_ty = ptr_ty,
   5504         } },
   5505     } });
   5506 }
   5507 
   5508 fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5509     _ = block;
   5510     const tracy = trace(@src());
   5511     defer tracy.end();
   5512 
   5513     const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].int;
   5514     return sema.mod.intRef(Type.comptime_int, int);
   5515 }
   5516 
   5517 fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5518     _ = block;
   5519     const tracy = trace(@src());
   5520     defer tracy.end();
   5521 
   5522     const mod = sema.mod;
   5523     const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].str;
   5524     const byte_count = int.len * @sizeOf(std.math.big.Limb);
   5525     const limb_bytes = sema.code.string_bytes[int.start..][0..byte_count];
   5526 
   5527     // TODO: this allocation and copy is only needed because the limbs may be unaligned.
   5528     // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these
   5529     // two lines can be removed.
   5530     const limbs = try sema.arena.alloc(std.math.big.Limb, int.len);
   5531     @memcpy(mem.sliceAsBytes(limbs), limb_bytes);
   5532 
   5533     return Air.internedToRef((try mod.intValue_big(Type.comptime_int, .{
   5534         .limbs = limbs,
   5535         .positive = true,
   5536     })).toIntern());
   5537 }
   5538 
   5539 fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5540     _ = block;
   5541     const number = sema.code.instructions.items(.data)[@intFromEnum(inst)].float;
   5542     return Air.internedToRef((try sema.mod.floatValue(
   5543         Type.comptime_float,
   5544         number,
   5545     )).toIntern());
   5546 }
   5547 
   5548 fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5549     _ = block;
   5550     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5551     const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data;
   5552     const number = extra.get();
   5553     return Air.internedToRef((try sema.mod.floatValue(Type.comptime_float, number)).toIntern());
   5554 }
   5555 
   5556 fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
   5557     const tracy = trace(@src());
   5558     defer tracy.end();
   5559 
   5560     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5561     const src = inst_data.src();
   5562     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   5563     const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, .{
   5564         .needed_comptime_reason = "compile error string must be comptime-known",
   5565     });
   5566     return sema.fail(block, src, "{s}", .{msg});
   5567 }
   5568 
   5569 fn zirCompileLog(
   5570     sema: *Sema,
   5571     extended: Zir.Inst.Extended.InstData,
   5572 ) CompileError!Air.Inst.Ref {
   5573     const mod = sema.mod;
   5574 
   5575     var managed = mod.compile_log_text.toManaged(sema.gpa);
   5576     defer sema.mod.compile_log_text = managed.moveToUnmanaged();
   5577     const writer = managed.writer();
   5578 
   5579     const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
   5580     const src_node = extra.data.src_node;
   5581     const args = sema.code.refSlice(extra.end, extended.small);
   5582 
   5583     for (args, 0..) |arg_ref, i| {
   5584         if (i != 0) try writer.print(", ", .{});
   5585 
   5586         const arg = try sema.resolveInst(arg_ref);
   5587         const arg_ty = sema.typeOf(arg);
   5588         if (try sema.resolveValueResolveLazy(arg)) |val| {
   5589             try writer.print("@as({}, {})", .{
   5590                 arg_ty.fmt(mod), val.fmtValue(arg_ty, mod),
   5591             });
   5592         } else {
   5593             try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(mod)});
   5594         }
   5595     }
   5596     try writer.print("\n", .{});
   5597 
   5598     const decl_index = if (sema.func_index != .none)
   5599         mod.funcOwnerDeclIndex(sema.func_index)
   5600     else
   5601         sema.owner_decl_index;
   5602     const gop = try mod.compile_log_decls.getOrPut(sema.gpa, decl_index);
   5603     if (!gop.found_existing) {
   5604         gop.value_ptr.* = src_node;
   5605     }
   5606     return .void_value;
   5607 }
   5608 
   5609 fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
   5610     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5611     const src = inst_data.src();
   5612     const msg_inst = try sema.resolveInst(inst_data.operand);
   5613 
   5614     if (block.is_comptime) {
   5615         return sema.fail(block, src, "encountered @panic at comptime", .{});
   5616     }
   5617     try sema.panicWithMsg(block, src, msg_inst, .@"@panic");
   5618     return always_noreturn;
   5619 }
   5620 
   5621 fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
   5622     const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node;
   5623     const src = LazySrcLoc.nodeOffset(src_node);
   5624     sema.src = src;
   5625     if (block.is_comptime)
   5626         return sema.fail(block, src, "encountered @trap at comptime", .{});
   5627     _ = try block.addNoOp(.trap);
   5628     return always_noreturn;
   5629 }
   5630 
   5631 fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5632     const tracy = trace(@src());
   5633     defer tracy.end();
   5634 
   5635     const mod = sema.mod;
   5636     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5637     const src = inst_data.src();
   5638     const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   5639     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
   5640     const gpa = sema.gpa;
   5641 
   5642     // AIR expects a block outside the loop block too.
   5643     // Reserve space for a Loop instruction so that generated Break instructions can
   5644     // point to it, even if it doesn't end up getting used because the code ends up being
   5645     // comptime evaluated.
   5646     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   5647     const loop_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1);
   5648     try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
   5649     sema.air_instructions.appendAssumeCapacity(.{
   5650         .tag = .block,
   5651         .data = undefined,
   5652     });
   5653     sema.air_instructions.appendAssumeCapacity(.{
   5654         .tag = .loop,
   5655         .data = .{ .ty_pl = .{
   5656             .ty = .noreturn_type,
   5657             .payload = undefined,
   5658         } },
   5659     });
   5660     var label: Block.Label = .{
   5661         .zir_block = inst,
   5662         .merges = .{
   5663             .src_locs = .{},
   5664             .results = .{},
   5665             .br_list = .{},
   5666             .block_inst = block_inst,
   5667         },
   5668     };
   5669     var child_block = parent_block.makeSubBlock();
   5670     child_block.label = &label;
   5671     child_block.runtime_cond = null;
   5672     child_block.runtime_loop = src;
   5673     child_block.runtime_index.increment();
   5674     const merges = &child_block.label.?.merges;
   5675 
   5676     defer child_block.instructions.deinit(gpa);
   5677     defer merges.deinit(gpa);
   5678 
   5679     var loop_block = child_block.makeSubBlock();
   5680     defer loop_block.instructions.deinit(gpa);
   5681 
   5682     try sema.analyzeBody(&loop_block, body);
   5683 
   5684     const loop_block_len = loop_block.instructions.items.len;
   5685     if (loop_block_len > 0 and sema.typeOf(loop_block.instructions.items[loop_block_len - 1].toRef()).isNoReturn(mod)) {
   5686         // If the loop ended with a noreturn terminator, then there is no way for it to loop,
   5687         // so we can just use the block instead.
   5688         try child_block.instructions.appendSlice(gpa, loop_block.instructions.items);
   5689     } else {
   5690         try child_block.instructions.append(gpa, loop_inst);
   5691 
   5692         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + loop_block_len);
   5693         sema.air_instructions.items(.data)[@intFromEnum(loop_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(
   5694             Air.Block{ .body_len = @intCast(loop_block_len) },
   5695         );
   5696         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(loop_block.instructions.items));
   5697     }
   5698     return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
   5699 }
   5700 
   5701 fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5702     const tracy = trace(@src());
   5703     defer tracy.end();
   5704 
   5705     const mod = sema.mod;
   5706     const comp = mod.comp;
   5707     const gpa = sema.gpa;
   5708     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5709     const src = pl_node.src();
   5710     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
   5711     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
   5712 
   5713     // we check this here to avoid undefined symbols
   5714     if (!@import("build_options").have_llvm)
   5715         return sema.fail(parent_block, src, "C import unavailable; Zig compiler built without LLVM extensions", .{});
   5716 
   5717     var c_import_buf = std.ArrayList(u8).init(gpa);
   5718     defer c_import_buf.deinit();
   5719 
   5720     var comptime_reason: Block.ComptimeReason = .{ .c_import = .{
   5721         .block = parent_block,
   5722         .src = src,
   5723     } };
   5724     var child_block: Block = .{
   5725         .parent = parent_block,
   5726         .sema = sema,
   5727         .src_decl = parent_block.src_decl,
   5728         .namespace = parent_block.namespace,
   5729         .wip_capture_scope = parent_block.wip_capture_scope,
   5730         .instructions = .{},
   5731         .inlining = parent_block.inlining,
   5732         .is_comptime = true,
   5733         .comptime_reason = &comptime_reason,
   5734         .c_import_buf = &c_import_buf,
   5735         .runtime_cond = parent_block.runtime_cond,
   5736         .runtime_loop = parent_block.runtime_loop,
   5737         .runtime_index = parent_block.runtime_index,
   5738     };
   5739     defer child_block.instructions.deinit(gpa);
   5740 
   5741     // Ignore the result, all the relevant operations have written to c_import_buf already.
   5742     _ = try sema.analyzeBodyBreak(&child_block, body);
   5743 
   5744     var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule()) catch |err|
   5745         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
   5746     defer c_import_res.deinit(gpa);
   5747 
   5748     if (c_import_res.errors.errorMessageCount() != 0) {
   5749         const msg = msg: {
   5750             const msg = try sema.errMsg(&child_block, src, "C import failed", .{});
   5751             errdefer msg.destroy(gpa);
   5752 
   5753             if (!comp.config.link_libc)
   5754                 try sema.errNote(&child_block, src, msg, "libc headers not available; compilation does not link against libc", .{});
   5755 
   5756             const gop = try mod.cimport_errors.getOrPut(gpa, sema.owner_decl_index);
   5757             if (!gop.found_existing) {
   5758                 gop.value_ptr.* = c_import_res.errors;
   5759                 c_import_res.errors = std.zig.ErrorBundle.empty;
   5760             }
   5761             break :msg msg;
   5762         };
   5763         return sema.failWithOwnedErrorMsg(&child_block, msg);
   5764     }
   5765     const parent_mod = parent_block.ownerModule();
   5766     const c_import_mod = Package.Module.create(comp.arena, .{
   5767         .global_cache_directory = comp.global_cache_directory,
   5768         .paths = .{
   5769             .root = .{
   5770                 .root_dir = Compilation.Directory.cwd(),
   5771                 .sub_path = std.fs.path.dirname(c_import_res.out_zig_path) orelse "",
   5772             },
   5773             .root_src_path = std.fs.path.basename(c_import_res.out_zig_path),
   5774         },
   5775         .fully_qualified_name = c_import_res.out_zig_path,
   5776         .cc_argv = parent_mod.cc_argv,
   5777         .inherited = .{},
   5778         .global = comp.config,
   5779         .parent = parent_mod,
   5780         .builtin_mod = parent_mod.getBuiltinDependency(),
   5781     }) catch |err| switch (err) {
   5782         // None of these are possible because we are creating a package with
   5783         // the exact same configuration as the parent package, which already
   5784         // passed these checks.
   5785         error.ValgrindUnsupportedOnTarget => unreachable,
   5786         error.TargetRequiresSingleThreaded => unreachable,
   5787         error.BackendRequiresSingleThreaded => unreachable,
   5788         error.TargetRequiresPic => unreachable,
   5789         error.PieRequiresPic => unreachable,
   5790         error.DynamicLinkingRequiresPic => unreachable,
   5791         error.TargetHasNoRedZone => unreachable,
   5792         error.StackCheckUnsupportedByTarget => unreachable,
   5793         error.StackProtectorUnsupportedByTarget => unreachable,
   5794         error.StackProtectorUnavailableWithoutLibC => unreachable,
   5795 
   5796         else => |e| return e,
   5797     };
   5798 
   5799     const result = mod.importPkg(c_import_mod) catch |err|
   5800         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
   5801 
   5802     mod.astGenFile(result.file) catch |err|
   5803         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
   5804 
   5805     try mod.semaFile(result.file);
   5806     const file_root_decl_index = result.file.root_decl.unwrap().?;
   5807     return sema.analyzeDeclVal(parent_block, src, file_root_decl_index);
   5808 }
   5809 
   5810 fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5811     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5812     const src = inst_data.src();
   5813     return sema.failWithUseOfAsync(parent_block, src);
   5814 }
   5815 
   5816 fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index, force_comptime: bool) CompileError!Air.Inst.Ref {
   5817     const tracy = trace(@src());
   5818     defer tracy.end();
   5819 
   5820     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5821     const src = pl_node.src();
   5822     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
   5823     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
   5824     const gpa = sema.gpa;
   5825 
   5826     // Reserve space for a Block instruction so that generated Break instructions can
   5827     // point to it, even if it doesn't end up getting used because the code ends up being
   5828     // comptime evaluated or is an unlabeled block.
   5829     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   5830     try sema.air_instructions.append(gpa, .{
   5831         .tag = .block,
   5832         .data = undefined,
   5833     });
   5834 
   5835     var label: Block.Label = .{
   5836         .zir_block = inst,
   5837         .merges = .{
   5838             .src_locs = .{},
   5839             .results = .{},
   5840             .br_list = .{},
   5841             .block_inst = block_inst,
   5842         },
   5843     };
   5844 
   5845     var child_block: Block = .{
   5846         .parent = parent_block,
   5847         .sema = sema,
   5848         .src_decl = parent_block.src_decl,
   5849         .namespace = parent_block.namespace,
   5850         .wip_capture_scope = parent_block.wip_capture_scope,
   5851         .instructions = .{},
   5852         .label = &label,
   5853         .inlining = parent_block.inlining,
   5854         .is_comptime = parent_block.is_comptime or force_comptime,
   5855         .comptime_reason = parent_block.comptime_reason,
   5856         .is_typeof = parent_block.is_typeof,
   5857         .want_safety = parent_block.want_safety,
   5858         .float_mode = parent_block.float_mode,
   5859         .c_import_buf = parent_block.c_import_buf,
   5860         .runtime_cond = parent_block.runtime_cond,
   5861         .runtime_loop = parent_block.runtime_loop,
   5862         .runtime_index = parent_block.runtime_index,
   5863         .error_return_trace_index = parent_block.error_return_trace_index,
   5864     };
   5865 
   5866     defer child_block.instructions.deinit(gpa);
   5867     defer label.merges.deinit(gpa);
   5868 
   5869     return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
   5870 }
   5871 
   5872 fn resolveBlockBody(
   5873     sema: *Sema,
   5874     parent_block: *Block,
   5875     src: LazySrcLoc,
   5876     child_block: *Block,
   5877     body: []const Zir.Inst.Index,
   5878     /// This is the instruction that a break instruction within `body` can
   5879     /// use to return from the body.
   5880     body_inst: Zir.Inst.Index,
   5881     merges: *Block.Merges,
   5882 ) CompileError!Air.Inst.Ref {
   5883     if (child_block.is_comptime) {
   5884         return sema.resolveBody(child_block, body, body_inst);
   5885     } else {
   5886         if (sema.analyzeBodyInner(child_block, body)) |_| {
   5887             return sema.analyzeBlockBody(parent_block, src, child_block, merges);
   5888         } else |err| switch (err) {
   5889             error.ComptimeBreak => {
   5890                 // Comptime control flow is happening, however child_block may still contain
   5891                 // runtime instructions which need to be copied to the parent block.
   5892                 try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items);
   5893 
   5894                 const break_inst = sema.comptime_break_inst;
   5895                 const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
   5896                 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
   5897                 if (extra.block_inst == body_inst) {
   5898                     return try sema.resolveInst(break_data.operand);
   5899                 } else {
   5900                     return error.ComptimeBreak;
   5901                 }
   5902             },
   5903             else => |e| return e,
   5904         }
   5905     }
   5906 }
   5907 
   5908 fn analyzeBlockBody(
   5909     sema: *Sema,
   5910     parent_block: *Block,
   5911     src: LazySrcLoc,
   5912     child_block: *Block,
   5913     merges: *Block.Merges,
   5914 ) CompileError!Air.Inst.Ref {
   5915     const tracy = trace(@src());
   5916     defer tracy.end();
   5917 
   5918     const gpa = sema.gpa;
   5919     const mod = sema.mod;
   5920 
   5921     // Blocks must terminate with noreturn instruction.
   5922     assert(child_block.instructions.items.len != 0);
   5923     assert(sema.typeOf(child_block.instructions.items[child_block.instructions.items.len - 1].toRef()).isNoReturn(mod));
   5924 
   5925     if (merges.results.items.len == 0) {
   5926         // No need for a block instruction. We can put the new instructions
   5927         // directly into the parent block.
   5928         try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);
   5929         return child_block.instructions.items[child_block.instructions.items.len - 1].toRef();
   5930     }
   5931     if (merges.results.items.len == 1) {
   5932         const last_inst_index = child_block.instructions.items.len - 1;
   5933         const last_inst = child_block.instructions.items[last_inst_index];
   5934         if (sema.getBreakBlock(last_inst)) |br_block| {
   5935             if (br_block == merges.block_inst) {
   5936                 // No need for a block instruction. We can put the new instructions directly
   5937                 // into the parent block. Here we omit the break instruction.
   5938                 const without_break = child_block.instructions.items[0..last_inst_index];
   5939                 try parent_block.instructions.appendSlice(gpa, without_break);
   5940                 return merges.results.items[0];
   5941             }
   5942         }
   5943     }
   5944     // It is impossible to have the number of results be > 1 in a comptime scope.
   5945     assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition.
   5946 
   5947     // Need to set the type and emit the Block instruction. This allows machine code generation
   5948     // to emit a jump instruction to after the block when it encounters the break.
   5949     try parent_block.instructions.append(gpa, merges.block_inst);
   5950     const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items });
   5951     // TODO add note "missing else causes void value"
   5952 
   5953     const type_src = src; // TODO: better source location
   5954     if (try sema.typeRequiresComptime(resolved_ty)) {
   5955         const msg = msg: {
   5956             const msg = try sema.errMsg(child_block, type_src, "value with comptime-only type '{}' depends on runtime control flow", .{resolved_ty.fmt(mod)});
   5957             errdefer msg.destroy(sema.gpa);
   5958 
   5959             const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?;
   5960             try sema.errNote(child_block, runtime_src, msg, "runtime control flow here", .{});
   5961 
   5962             const child_src_decl = mod.declPtr(child_block.src_decl);
   5963             try sema.explainWhyTypeIsComptime(msg, type_src.toSrcLoc(child_src_decl, mod), resolved_ty);
   5964 
   5965             break :msg msg;
   5966         };
   5967         return sema.failWithOwnedErrorMsg(child_block, msg);
   5968     }
   5969     const ty_inst = Air.internedToRef(resolved_ty.toIntern());
   5970     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
   5971         child_block.instructions.items.len);
   5972     sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   5973         .ty = ty_inst,
   5974         .payload = sema.addExtraAssumeCapacity(Air.Block{
   5975             .body_len = @intCast(child_block.instructions.items.len),
   5976         }),
   5977     } };
   5978     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
   5979     // Now that the block has its type resolved, we need to go back into all the break
   5980     // instructions, and insert type coercion on the operands.
   5981     for (merges.br_list.items) |br| {
   5982         const br_operand = sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand;
   5983         const br_operand_src = src;
   5984         const br_operand_ty = sema.typeOf(br_operand);
   5985         if (br_operand_ty.eql(resolved_ty, mod)) {
   5986             // No type coercion needed.
   5987             continue;
   5988         }
   5989         var coerce_block = parent_block.makeSubBlock();
   5990         defer coerce_block.instructions.deinit(gpa);
   5991         const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src);
   5992         // If no instructions were produced, such as in the case of a coercion of a
   5993         // constant value to a new type, we can simply point the br operand to it.
   5994         if (coerce_block.instructions.items.len == 0) {
   5995             sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand = coerced_operand;
   5996             continue;
   5997         }
   5998         assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1].toRef() == coerced_operand);
   5999 
   6000         // Convert the br instruction to a block instruction that has the coercion
   6001         // and then a new br inside that returns the coerced instruction.
   6002         const sub_block_len: u32 = @intCast(coerce_block.instructions.items.len + 1);
   6003         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
   6004             sub_block_len);
   6005         try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
   6006         const sub_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   6007 
   6008         sema.air_instructions.items(.tag)[@intFromEnum(br)] = .block;
   6009         sema.air_instructions.items(.data)[@intFromEnum(br)] = .{ .ty_pl = .{
   6010             .ty = .noreturn_type,
   6011             .payload = sema.addExtraAssumeCapacity(Air.Block{
   6012                 .body_len = sub_block_len,
   6013             }),
   6014         } };
   6015         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items));
   6016         sema.air_extra.appendAssumeCapacity(@intFromEnum(sub_br_inst));
   6017 
   6018         sema.air_instructions.appendAssumeCapacity(.{
   6019             .tag = .br,
   6020             .data = .{ .br = .{
   6021                 .block_inst = merges.block_inst,
   6022                 .operand = coerced_operand,
   6023             } },
   6024         });
   6025     }
   6026     return merges.block_inst.toRef();
   6027 }
   6028 
   6029 fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6030     const tracy = trace(@src());
   6031     defer tracy.end();
   6032 
   6033     const mod = sema.mod;
   6034     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   6035     const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
   6036     const src = inst_data.src();
   6037     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   6038     const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
   6039     const decl_name = try mod.intern_pool.getOrPutString(mod.gpa, sema.code.nullTerminatedString(extra.decl_name));
   6040     const decl_index = if (extra.namespace != .none) index_blk: {
   6041         const container_ty = try sema.resolveType(block, operand_src, extra.namespace);
   6042         const container_namespace = container_ty.getNamespaceIndex(mod).unwrap().?;
   6043 
   6044         const maybe_index = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false);
   6045         break :index_blk maybe_index orelse
   6046             return sema.failWithBadMemberAccess(block, container_ty, operand_src, decl_name);
   6047     } else try sema.lookupIdentifier(block, operand_src, decl_name);
   6048     const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) {
   6049         error.NeededSourceLocation => {
   6050             _ = try sema.resolveExportOptions(block, options_src, extra.options);
   6051             unreachable;
   6052         },
   6053         else => |e| return e,
   6054     };
   6055     {
   6056         try mod.ensureDeclAnalyzed(decl_index);
   6057         const exported_decl = mod.declPtr(decl_index);
   6058         if (exported_decl.val.getFunction(mod)) |function| {
   6059             return sema.analyzeExport(block, src, options, function.owner_decl);
   6060         }
   6061     }
   6062     try sema.analyzeExport(block, src, options, decl_index);
   6063 }
   6064 
   6065 fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6066     const tracy = trace(@src());
   6067     defer tracy.end();
   6068 
   6069     const mod = sema.mod;
   6070     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   6071     const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;
   6072     const src = inst_data.src();
   6073     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   6074     const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
   6075     const operand = try sema.resolveInstConst(block, operand_src, extra.operand, .{
   6076         .needed_comptime_reason = "export target must be comptime-known",
   6077     });
   6078     const options = try sema.resolveExportOptions(block, options_src, extra.options);
   6079     if (options.linkage == .Internal)
   6080         return;
   6081     if (operand.val.getFunction(mod)) |function| {
   6082         const decl_index = function.owner_decl;
   6083         return sema.analyzeExport(block, src, options, decl_index);
   6084     }
   6085 
   6086     try addExport(mod, .{
   6087         .opts = options,
   6088         .src = src,
   6089         .owner_decl = sema.owner_decl_index,
   6090         .src_decl = block.src_decl,
   6091         .exported = .{ .value = operand.val.toIntern() },
   6092         .status = .in_progress,
   6093     });
   6094 }
   6095 
   6096 pub fn analyzeExport(
   6097     sema: *Sema,
   6098     block: *Block,
   6099     src: LazySrcLoc,
   6100     options: Module.Export.Options,
   6101     exported_decl_index: InternPool.DeclIndex,
   6102 ) !void {
   6103     const gpa = sema.gpa;
   6104     const mod = sema.mod;
   6105 
   6106     if (options.linkage == .Internal)
   6107         return;
   6108 
   6109     try mod.ensureDeclAnalyzed(exported_decl_index);
   6110     const exported_decl = mod.declPtr(exported_decl_index);
   6111 
   6112     if (!try sema.validateExternType(exported_decl.ty, .other)) {
   6113         const msg = msg: {
   6114             const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(mod)});
   6115             errdefer msg.destroy(gpa);
   6116 
   6117             const src_decl = mod.declPtr(block.src_decl);
   6118             try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), exported_decl.ty, .other);
   6119 
   6120             try sema.addDeclaredHereNote(msg, exported_decl.ty);
   6121             break :msg msg;
   6122         };
   6123         return sema.failWithOwnedErrorMsg(block, msg);
   6124     }
   6125 
   6126     // TODO: some backends might support re-exporting extern decls
   6127     if (exported_decl.isExtern(mod)) {
   6128         return sema.fail(block, src, "export target cannot be extern", .{});
   6129     }
   6130 
   6131     // This decl is alive no matter what, since it's being exported
   6132     try mod.markDeclAlive(exported_decl);
   6133     try sema.maybeQueueFuncBodyAnalysis(exported_decl_index);
   6134 
   6135     try addExport(mod, .{
   6136         .opts = options,
   6137         .src = src,
   6138         .owner_decl = sema.owner_decl_index,
   6139         .src_decl = block.src_decl,
   6140         .exported = .{ .decl_index = exported_decl_index },
   6141         .status = .in_progress,
   6142     });
   6143 }
   6144 
   6145 fn addExport(mod: *Module, export_init: Module.Export) error{OutOfMemory}!void {
   6146     const gpa = mod.gpa;
   6147 
   6148     try mod.decl_exports.ensureUnusedCapacity(gpa, 1);
   6149     try mod.value_exports.ensureUnusedCapacity(gpa, 1);
   6150     try mod.export_owners.ensureUnusedCapacity(gpa, 1);
   6151 
   6152     const new_export = try gpa.create(Module.Export);
   6153     errdefer gpa.destroy(new_export);
   6154 
   6155     new_export.* = export_init;
   6156 
   6157     const eo_gop = mod.export_owners.getOrPutAssumeCapacity(export_init.owner_decl);
   6158     if (!eo_gop.found_existing) eo_gop.value_ptr.* = .{};
   6159     try eo_gop.value_ptr.append(gpa, new_export);
   6160     errdefer _ = eo_gop.value_ptr.pop();
   6161 
   6162     switch (export_init.exported) {
   6163         .decl_index => |decl_index| {
   6164             const de_gop = mod.decl_exports.getOrPutAssumeCapacity(decl_index);
   6165             if (!de_gop.found_existing) de_gop.value_ptr.* = .{};
   6166             try de_gop.value_ptr.append(gpa, new_export);
   6167         },
   6168         .value => |value| {
   6169             const ve_gop = mod.value_exports.getOrPutAssumeCapacity(value);
   6170             if (!ve_gop.found_existing) ve_gop.value_ptr.* = .{};
   6171             try ve_gop.value_ptr.append(gpa, new_export);
   6172         },
   6173     }
   6174 }
   6175 
   6176 fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
   6177     const mod = sema.mod;
   6178     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   6179     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   6180     const src = LazySrcLoc.nodeOffset(extra.node);
   6181     const alignment = try sema.resolveAlign(block, operand_src, extra.operand);
   6182     if (alignment.order(Alignment.fromNonzeroByteUnits(256)).compare(.gt)) {
   6183         return sema.fail(block, src, "attempt to @setAlignStack({d}); maximum is 256", .{
   6184             alignment.toByteUnitsOptional().?,
   6185         });
   6186     }
   6187 
   6188     const fn_owner_decl = mod.funcOwnerDeclPtr(sema.func_index);
   6189     switch (fn_owner_decl.ty.fnCallingConvention(mod)) {
   6190         .Naked => return sema.fail(block, src, "@setAlignStack in naked function", .{}),
   6191         .Inline => return sema.fail(block, src, "@setAlignStack in inline function", .{}),
   6192         else => if (block.inlining != null) {
   6193             return sema.fail(block, src, "@setAlignStack in inline call", .{});
   6194         },
   6195     }
   6196 
   6197     if (sema.prev_stack_alignment_src) |prev_src| {
   6198         const msg = msg: {
   6199             const msg = try sema.errMsg(block, src, "multiple @setAlignStack in the same function body", .{});
   6200             errdefer msg.destroy(sema.gpa);
   6201             try sema.errNote(block, prev_src, msg, "other instance here", .{});
   6202             break :msg msg;
   6203         };
   6204         return sema.failWithOwnedErrorMsg(block, msg);
   6205     }
   6206     sema.prev_stack_alignment_src = src;
   6207 
   6208     const ip = &mod.intern_pool;
   6209     const a = ip.funcAnalysis(sema.func_index);
   6210     if (a.stack_alignment != .none) {
   6211         a.stack_alignment = @enumFromInt(@max(
   6212             @intFromEnum(alignment),
   6213             @intFromEnum(a.stack_alignment),
   6214         ));
   6215     }
   6216 }
   6217 
   6218 fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
   6219     const mod = sema.mod;
   6220     const ip = &mod.intern_pool;
   6221     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   6222     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   6223     const is_cold = try sema.resolveConstBool(block, operand_src, extra.operand, .{
   6224         .needed_comptime_reason = "operand to @setCold must be comptime-known",
   6225     });
   6226     if (sema.func_index == .none) return; // does nothing outside a function
   6227     ip.funcAnalysis(sema.func_index).is_cold = is_cold;
   6228 }
   6229 
   6230 fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
   6231     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   6232     const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   6233     block.float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, "FloatMode", .{
   6234         .needed_comptime_reason = "operand to @setFloatMode must be comptime-known",
   6235     });
   6236 }
   6237 
   6238 fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6239     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   6240     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   6241     block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, .{
   6242         .needed_comptime_reason = "operand to @setRuntimeSafety must be comptime-known",
   6243     });
   6244 }
   6245 
   6246 fn zirFence(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
   6247     if (block.is_comptime) return;
   6248 
   6249     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   6250     const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   6251     const order = try sema.resolveAtomicOrder(block, order_src, extra.operand, .{
   6252         .needed_comptime_reason = "atomic order of @fence must be comptime-known",
   6253     });
   6254 
   6255     if (@intFromEnum(order) < @intFromEnum(std.builtin.AtomicOrder.Acquire)) {
   6256         return sema.fail(block, order_src, "atomic ordering must be Acquire or stricter", .{});
   6257     }
   6258 
   6259     _ = try block.addInst(.{
   6260         .tag = .fence,
   6261         .data = .{ .fence = order },
   6262     });
   6263 }
   6264 
   6265 fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
   6266     const tracy = trace(@src());
   6267     defer tracy.end();
   6268 
   6269     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break";
   6270     const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data;
   6271     const operand = try sema.resolveInst(inst_data.operand);
   6272     const zir_block = extra.block_inst;
   6273 
   6274     var block = start_block;
   6275     while (true) {
   6276         if (block.label) |label| {
   6277             if (label.zir_block == zir_block) {
   6278                 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
   6279                 const src_loc = if (extra.operand_src_node != Zir.Inst.Break.no_src_node)
   6280                     LazySrcLoc.nodeOffset(extra.operand_src_node)
   6281                 else
   6282                     null;
   6283                 try label.merges.src_locs.append(sema.gpa, src_loc);
   6284                 try label.merges.results.append(sema.gpa, operand);
   6285                 try label.merges.br_list.append(sema.gpa, br_ref.toIndex().?);
   6286                 block.runtime_index.increment();
   6287                 if (block.runtime_cond == null and block.runtime_loop == null) {
   6288                     block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop;
   6289                     block.runtime_loop = start_block.runtime_loop;
   6290                 }
   6291                 return inst;
   6292             }
   6293         }
   6294         block = block.parent.?;
   6295     }
   6296 }
   6297 
   6298 fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6299     // We do not set sema.src here because dbg_stmt instructions are only emitted for
   6300     // ZIR code that possibly will need to generate runtime code. So error messages
   6301     // and other source locations must not rely on sema.src being set from dbg_stmt
   6302     // instructions.
   6303     if (block.is_comptime or block.ownerModule().strip) return;
   6304 
   6305     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
   6306 
   6307     if (block.instructions.items.len != 0) {
   6308         const idx = block.instructions.items[block.instructions.items.len - 1];
   6309         if (sema.air_instructions.items(.tag)[@intFromEnum(idx)] == .dbg_stmt) {
   6310             // The previous dbg_stmt didn't correspond to any actual code, so replace it.
   6311             sema.air_instructions.items(.data)[@intFromEnum(idx)].dbg_stmt = .{
   6312                 .line = inst_data.line,
   6313                 .column = inst_data.column,
   6314             };
   6315             return;
   6316         }
   6317     }
   6318 
   6319     _ = try block.addInst(.{
   6320         .tag = .dbg_stmt,
   6321         .data = .{ .dbg_stmt = .{
   6322             .line = inst_data.line,
   6323             .column = inst_data.column,
   6324         } },
   6325     });
   6326 }
   6327 
   6328 fn zirDbgBlockBegin(block: *Block) CompileError!void {
   6329     if (block.is_comptime or block.ownerModule().strip) return;
   6330 
   6331     _ = try block.addInst(.{
   6332         .tag = .dbg_block_begin,
   6333         .data = undefined,
   6334     });
   6335 }
   6336 
   6337 fn zirDbgBlockEnd(block: *Block) CompileError!void {
   6338     if (block.is_comptime or block.ownerModule().strip) return;
   6339 
   6340     _ = try block.addInst(.{
   6341         .tag = .dbg_block_end,
   6342         .data = undefined,
   6343     });
   6344 }
   6345 
   6346 fn zirDbgVar(
   6347     sema: *Sema,
   6348     block: *Block,
   6349     inst: Zir.Inst.Index,
   6350     air_tag: Air.Inst.Tag,
   6351 ) CompileError!void {
   6352     if (block.is_comptime or block.ownerModule().strip) return;
   6353 
   6354     const str_op = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_op;
   6355     const operand = try sema.resolveInst(str_op.operand);
   6356     const name = str_op.getStr(sema.code);
   6357     try sema.addDbgVar(block, operand, air_tag, name);
   6358 }
   6359 
   6360 fn addDbgVar(
   6361     sema: *Sema,
   6362     block: *Block,
   6363     operand: Air.Inst.Ref,
   6364     air_tag: Air.Inst.Tag,
   6365     name: []const u8,
   6366 ) CompileError!void {
   6367     const mod = sema.mod;
   6368     const operand_ty = sema.typeOf(operand);
   6369     const val_ty = switch (air_tag) {
   6370         .dbg_var_ptr => operand_ty.childType(mod),
   6371         .dbg_var_val => operand_ty,
   6372         else => unreachable,
   6373     };
   6374     if (try sema.typeRequiresComptime(val_ty)) return;
   6375     if (!(try sema.typeHasRuntimeBits(val_ty))) return;
   6376 
   6377     try sema.queueFullTypeResolution(operand_ty);
   6378 
   6379     // Add the name to the AIR.
   6380     const name_extra_index: u32 = @intCast(sema.air_extra.items.len);
   6381     const elements_used = name.len / 4 + 1;
   6382     try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements_used);
   6383     const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
   6384     @memcpy(buffer[0..name.len], name);
   6385     buffer[name.len] = 0;
   6386     sema.air_extra.items.len += elements_used;
   6387 
   6388     _ = try block.addInst(.{
   6389         .tag = air_tag,
   6390         .data = .{ .pl_op = .{
   6391             .payload = name_extra_index,
   6392             .operand = operand,
   6393         } },
   6394     });
   6395 }
   6396 
   6397 fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   6398     const mod = sema.mod;
   6399     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   6400     const src = inst_data.src();
   6401     const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
   6402     const decl_index = try sema.lookupIdentifier(block, src, decl_name);
   6403     try sema.addReferencedBy(block, src, decl_index);
   6404     return sema.analyzeDeclRef(decl_index);
   6405 }
   6406 
   6407 fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   6408     const mod = sema.mod;
   6409     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   6410     const src = inst_data.src();
   6411     const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
   6412     const decl = try sema.lookupIdentifier(block, src, decl_name);
   6413     return sema.analyzeDeclVal(block, src, decl);
   6414 }
   6415 
   6416 fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPool.NullTerminatedString) !InternPool.DeclIndex {
   6417     const mod = sema.mod;
   6418     var namespace = block.namespace;
   6419     while (true) {
   6420         if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl_index| {
   6421             return decl_index;
   6422         }
   6423         namespace = mod.namespacePtr(namespace).parent.unwrap() orelse break;
   6424     }
   6425     unreachable; // AstGen detects use of undeclared identifiers.
   6426 }
   6427 
   6428 /// This looks up a member of a specific namespace. It is affected by `usingnamespace` but
   6429 /// only for ones in the specified namespace.
   6430 fn lookupInNamespace(
   6431     sema: *Sema,
   6432     block: *Block,
   6433     src: LazySrcLoc,
   6434     namespace_index: InternPool.NamespaceIndex,
   6435     ident_name: InternPool.NullTerminatedString,
   6436     observe_usingnamespace: bool,
   6437 ) CompileError!?InternPool.DeclIndex {
   6438     const mod = sema.mod;
   6439 
   6440     const namespace = mod.namespacePtr(namespace_index);
   6441     const namespace_decl_index = namespace.getDeclIndex(mod);
   6442     const namespace_decl = mod.declPtr(namespace_decl_index);
   6443     if (namespace_decl.analysis == .file_failure) {
   6444         return error.AnalysisFail;
   6445     }
   6446 
   6447     if (observe_usingnamespace and namespace.usingnamespace_set.count() != 0) {
   6448         const src_file = mod.namespacePtr(block.namespace).file_scope;
   6449 
   6450         const gpa = sema.gpa;
   6451         var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, bool) = .{};
   6452         defer checked_namespaces.deinit(gpa);
   6453 
   6454         // Keep track of name conflicts for error notes.
   6455         var candidates: std.ArrayListUnmanaged(InternPool.DeclIndex) = .{};
   6456         defer candidates.deinit(gpa);
   6457 
   6458         try checked_namespaces.put(gpa, namespace, namespace.file_scope == src_file);
   6459         var check_i: usize = 0;
   6460 
   6461         while (check_i < checked_namespaces.count()) : (check_i += 1) {
   6462             const check_ns = checked_namespaces.keys()[check_i];
   6463             if (check_ns.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| {
   6464                 // Skip decls which are not marked pub, which are in a different
   6465                 // file than the `a.b`/`@hasDecl` syntax.
   6466                 const decl = mod.declPtr(decl_index);
   6467                 if (decl.is_pub or (src_file == decl.getFileScope(mod) and checked_namespaces.values()[check_i])) {
   6468                     try candidates.append(gpa, decl_index);
   6469                 }
   6470             }
   6471             var it = check_ns.usingnamespace_set.iterator();
   6472             while (it.next()) |entry| {
   6473                 const sub_usingnamespace_decl_index = entry.key_ptr.*;
   6474                 // Skip the decl we're currently analysing.
   6475                 if (sub_usingnamespace_decl_index == sema.owner_decl_index) continue;
   6476                 const sub_usingnamespace_decl = mod.declPtr(sub_usingnamespace_decl_index);
   6477                 const sub_is_pub = entry.value_ptr.*;
   6478                 if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope(mod)) {
   6479                     // Skip usingnamespace decls which are not marked pub, which are in
   6480                     // a different file than the `a.b`/`@hasDecl` syntax.
   6481                     continue;
   6482                 }
   6483                 try sema.ensureDeclAnalyzed(sub_usingnamespace_decl_index);
   6484                 const ns_ty = sub_usingnamespace_decl.val.toType();
   6485                 const sub_ns = ns_ty.getNamespace(mod).?;
   6486                 try checked_namespaces.put(gpa, sub_ns, src_file == sub_usingnamespace_decl.getFileScope(mod));
   6487             }
   6488         }
   6489 
   6490         {
   6491             var i: usize = 0;
   6492             while (i < candidates.items.len) {
   6493                 if (candidates.items[i] == sema.owner_decl_index) {
   6494                     _ = candidates.orderedRemove(i);
   6495                 } else {
   6496                     i += 1;
   6497                 }
   6498             }
   6499         }
   6500 
   6501         switch (candidates.items.len) {
   6502             0 => {},
   6503             1 => {
   6504                 const decl_index = candidates.items[0];
   6505                 return decl_index;
   6506             },
   6507             else => {
   6508                 const msg = msg: {
   6509                     const msg = try sema.errMsg(block, src, "ambiguous reference", .{});
   6510                     errdefer msg.destroy(gpa);
   6511                     for (candidates.items) |candidate_index| {
   6512                         const candidate = mod.declPtr(candidate_index);
   6513                         const src_loc = candidate.srcLoc(mod);
   6514                         try mod.errNoteNonLazy(src_loc, msg, "declared here", .{});
   6515                     }
   6516                     break :msg msg;
   6517                 };
   6518                 return sema.failWithOwnedErrorMsg(block, msg);
   6519             },
   6520         }
   6521     } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| {
   6522         return decl_index;
   6523     }
   6524 
   6525     return null;
   6526 }
   6527 
   6528 fn funcDeclSrc(sema: *Sema, func_inst: Air.Inst.Ref) !?*Decl {
   6529     const mod = sema.mod;
   6530     const func_val = (try sema.resolveValue(func_inst)) orelse return null;
   6531     if (func_val.isUndef(mod)) return null;
   6532     const owner_decl_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) {
   6533         .extern_func => |extern_func| extern_func.decl,
   6534         .func => |func| func.owner_decl,
   6535         .ptr => |ptr| switch (ptr.addr) {
   6536             .decl => |decl| mod.declPtr(decl).val.getFunction(mod).?.owner_decl,
   6537             else => return null,
   6538         },
   6539         else => return null,
   6540     };
   6541     return mod.declPtr(owner_decl_index);
   6542 }
   6543 
   6544 pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref {
   6545     const mod = sema.mod;
   6546     const gpa = sema.gpa;
   6547     const src = sema.src;
   6548 
   6549     if (!block.ownerModule().error_tracing) return .none;
   6550 
   6551     if (block.is_comptime)
   6552         return .none;
   6553 
   6554     const stack_trace_ty = sema.getBuiltinType("StackTrace") catch |err| switch (err) {
   6555         error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable,
   6556         else => |e| return e,
   6557     };
   6558     sema.resolveTypeFields(stack_trace_ty) catch |err| switch (err) {
   6559         error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable,
   6560         else => |e| return e,
   6561     };
   6562     const field_name = try mod.intern_pool.getOrPutString(gpa, "index");
   6563     const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, src) catch |err| switch (err) {
   6564         error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable,
   6565         else => |e| return e,
   6566     };
   6567 
   6568     return try block.addInst(.{
   6569         .tag = .save_err_return_trace_index,
   6570         .data = .{ .ty_pl = .{
   6571             .ty = Air.internedToRef(stack_trace_ty.toIntern()),
   6572             .payload = @intCast(field_index),
   6573         } },
   6574     });
   6575 }
   6576 
   6577 /// Add instructions to block to "pop" the error return trace.
   6578 /// If `operand` is provided, only pops if operand is non-error.
   6579 fn popErrorReturnTrace(
   6580     sema: *Sema,
   6581     block: *Block,
   6582     src: LazySrcLoc,
   6583     operand: Air.Inst.Ref,
   6584     saved_error_trace_index: Air.Inst.Ref,
   6585 ) CompileError!void {
   6586     const mod = sema.mod;
   6587     const gpa = sema.gpa;
   6588     var is_non_error: ?bool = null;
   6589     var is_non_error_inst: Air.Inst.Ref = undefined;
   6590     if (operand != .none) {
   6591         is_non_error_inst = try sema.analyzeIsNonErr(block, src, operand);
   6592         if (try sema.resolveDefinedValue(block, src, is_non_error_inst)) |cond_val|
   6593             is_non_error = cond_val.toBool();
   6594     } else is_non_error = true; // no operand means pop unconditionally
   6595 
   6596     if (is_non_error == true) {
   6597         // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or
   6598         // the result is comptime-known to be a non-error. Either way, pop unconditionally.
   6599 
   6600         const stack_trace_ty = try sema.getBuiltinType("StackTrace");
   6601         try sema.resolveTypeFields(stack_trace_ty);
   6602         const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty);
   6603         const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
   6604         const field_name = try mod.intern_pool.getOrPutString(gpa, "index");
   6605         const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true);
   6606         try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store);
   6607     } else if (is_non_error == null) {
   6608         // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need
   6609         // to pop any error trace that may have been propagated from our arguments.
   6610 
   6611         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len);
   6612         const cond_block_inst = try block.addInstAsIndex(.{
   6613             .tag = .block,
   6614             .data = .{
   6615                 .ty_pl = .{
   6616                     .ty = .void_type,
   6617                     .payload = undefined, // updated below
   6618                 },
   6619             },
   6620         });
   6621 
   6622         var then_block = block.makeSubBlock();
   6623         defer then_block.instructions.deinit(gpa);
   6624 
   6625         // If non-error, then pop the error return trace by restoring the index.
   6626         const stack_trace_ty = try sema.getBuiltinType("StackTrace");
   6627         try sema.resolveTypeFields(stack_trace_ty);
   6628         const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty);
   6629         const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty);
   6630         const field_name = try mod.intern_pool.getOrPutString(gpa, "index");
   6631         const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true);
   6632         try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store);
   6633         _ = try then_block.addBr(cond_block_inst, .void_value);
   6634 
   6635         // Otherwise, do nothing
   6636         var else_block = block.makeSubBlock();
   6637         defer else_block.instructions.deinit(gpa);
   6638         _ = try else_block.addBr(cond_block_inst, .void_value);
   6639 
   6640         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
   6641             then_block.instructions.items.len + else_block.instructions.items.len +
   6642             @typeInfo(Air.Block).Struct.fields.len + 1); // +1 for the sole .cond_br instruction in the .block
   6643 
   6644         const cond_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   6645         try sema.air_instructions.append(gpa, .{ .tag = .cond_br, .data = .{ .pl_op = .{
   6646             .operand = is_non_error_inst,
   6647             .payload = sema.addExtraAssumeCapacity(Air.CondBr{
   6648                 .then_body_len = @intCast(then_block.instructions.items.len),
   6649                 .else_body_len = @intCast(else_block.instructions.items.len),
   6650             }),
   6651         } } });
   6652         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
   6653         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
   6654 
   6655         sema.air_instructions.items(.data)[@intFromEnum(cond_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 });
   6656         sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst));
   6657     }
   6658 }
   6659 
   6660 fn zirCall(
   6661     sema: *Sema,
   6662     block: *Block,
   6663     inst: Zir.Inst.Index,
   6664     comptime kind: enum { direct, field },
   6665 ) CompileError!Air.Inst.Ref {
   6666     const tracy = trace(@src());
   6667     defer tracy.end();
   6668 
   6669     const mod = sema.mod;
   6670     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   6671     const callee_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
   6672     const call_src = inst_data.src();
   6673     const ExtraType = switch (kind) {
   6674         .direct => Zir.Inst.Call,
   6675         .field => Zir.Inst.FieldCall,
   6676     };
   6677     const extra = sema.code.extraData(ExtraType, inst_data.payload_index);
   6678     const args_len = extra.data.flags.args_len;
   6679 
   6680     const modifier: std.builtin.CallModifier = @enumFromInt(extra.data.flags.packed_modifier);
   6681     const ensure_result_used = extra.data.flags.ensure_result_used;
   6682     const pop_error_return_trace = extra.data.flags.pop_error_return_trace;
   6683 
   6684     const callee: ResolvedFieldCallee = switch (kind) {
   6685         .direct => .{ .direct = try sema.resolveInst(extra.data.callee) },
   6686         .field => blk: {
   6687             const object_ptr = try sema.resolveInst(extra.data.obj_ptr);
   6688             const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.data.field_name_start));
   6689             const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
   6690             break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src);
   6691         },
   6692     };
   6693     const func: Air.Inst.Ref = switch (callee) {
   6694         .direct => |func_inst| func_inst,
   6695         .method => |method| method.func_inst,
   6696     };
   6697 
   6698     const callee_ty = sema.typeOf(func);
   6699     const total_args = args_len + @intFromBool(callee == .method);
   6700     const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, callee == .method);
   6701 
   6702     // The block index before the call, so we can potentially insert an error trace save here later.
   6703     const block_index: Air.Inst.Index = @enumFromInt(block.instructions.items.len);
   6704 
   6705     // This will be set by `analyzeCall` to indicate whether any parameter was an error (making the
   6706     // error trace potentially dirty).
   6707     var input_is_error = false;
   6708 
   6709     const args_info: CallArgsInfo = .{ .zir_call = .{
   6710         .bound_arg = switch (callee) {
   6711             .direct => .none,
   6712             .method => |method| method.arg0_inst,
   6713         },
   6714         .bound_arg_src = callee_src,
   6715         .call_inst = inst,
   6716         .call_node_offset = inst_data.src_node,
   6717         .num_args = args_len,
   6718         .args_body = @ptrCast(sema.code.extra[extra.end..]),
   6719         .any_arg_is_error = &input_is_error,
   6720     } };
   6721 
   6722     // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction.
   6723     const call_dbg_node: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
   6724     const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
   6725 
   6726     if (sema.owner_func_index == .none or
   6727         !mod.intern_pool.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn)
   6728     {
   6729         // No errorable fn actually called; we have no error return trace
   6730         input_is_error = false;
   6731     }
   6732 
   6733     if (block.ownerModule().error_tracing and
   6734         !block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace))
   6735     {
   6736         const return_ty = sema.typeOf(call_inst);
   6737         if (modifier != .always_tail and return_ty.isNoReturn(mod))
   6738             return call_inst; // call to "fn (...) noreturn", don't pop
   6739 
   6740         // TODO: we don't fix up the error trace for always_tail correctly, we should be doing it
   6741         // *before* the recursive call. This will be a bit tricky to do and probably requires
   6742         // moving this logic into analyzeCall. But that's probably a good idea anyway.
   6743         if (modifier == .always_tail)
   6744             return call_inst;
   6745 
   6746         // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only
   6747         // need to clean-up our own trace if we were passed to a non-error-handling expression.
   6748         if (input_is_error or (pop_error_return_trace and return_ty.isError(mod))) {
   6749             const stack_trace_ty = try sema.getBuiltinType("StackTrace");
   6750             try sema.resolveTypeFields(stack_trace_ty);
   6751             const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "index");
   6752             const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src);
   6753 
   6754             // Insert a save instruction before the arg resolution + call instructions we just generated
   6755             const save_inst = try block.insertInst(block_index, .{
   6756                 .tag = .save_err_return_trace_index,
   6757                 .data = .{ .ty_pl = .{
   6758                     .ty = Air.internedToRef(stack_trace_ty.toIntern()),
   6759                     .payload = @intCast(field_index),
   6760                 } },
   6761             });
   6762 
   6763             // Pop the error return trace, testing the result for non-error if necessary
   6764             const operand = if (pop_error_return_trace or modifier == .always_tail) .none else call_inst;
   6765             try sema.popErrorReturnTrace(block, call_src, operand, save_inst);
   6766         }
   6767 
   6768         return call_inst;
   6769     } else {
   6770         return call_inst;
   6771     }
   6772 }
   6773 
   6774 fn checkCallArgumentCount(
   6775     sema: *Sema,
   6776     block: *Block,
   6777     func: Air.Inst.Ref,
   6778     func_src: LazySrcLoc,
   6779     callee_ty: Type,
   6780     total_args: usize,
   6781     member_fn: bool,
   6782 ) !Type {
   6783     const mod = sema.mod;
   6784     const func_ty = func_ty: {
   6785         switch (callee_ty.zigTypeTag(mod)) {
   6786             .Fn => break :func_ty callee_ty,
   6787             .Pointer => {
   6788                 const ptr_info = callee_ty.ptrInfo(mod);
   6789                 if (ptr_info.flags.size == .One and Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Fn) {
   6790                     break :func_ty Type.fromInterned(ptr_info.child);
   6791                 }
   6792             },
   6793             .Optional => {
   6794                 const opt_child = callee_ty.optionalChild(mod);
   6795                 if (opt_child.zigTypeTag(mod) == .Fn or (opt_child.isSinglePointer(mod) and
   6796                     opt_child.childType(mod).zigTypeTag(mod) == .Fn))
   6797                 {
   6798                     const msg = msg: {
   6799                         const msg = try sema.errMsg(block, func_src, "cannot call optional type '{}'", .{
   6800                             callee_ty.fmt(mod),
   6801                         });
   6802                         errdefer msg.destroy(sema.gpa);
   6803                         try sema.errNote(block, func_src, msg, "consider using '.?', 'orelse' or 'if'", .{});
   6804                         break :msg msg;
   6805                     };
   6806                     return sema.failWithOwnedErrorMsg(block, msg);
   6807                 }
   6808             },
   6809             else => {},
   6810         }
   6811         return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(mod)});
   6812     };
   6813 
   6814     const func_ty_info = mod.typeToFunc(func_ty).?;
   6815     const fn_params_len = func_ty_info.param_types.len;
   6816     const args_len = total_args - @intFromBool(member_fn);
   6817     if (func_ty_info.is_var_args) {
   6818         assert(callConvSupportsVarArgs(func_ty_info.cc));
   6819         if (total_args >= fn_params_len) return func_ty;
   6820     } else if (fn_params_len == total_args) {
   6821         return func_ty;
   6822     }
   6823 
   6824     const maybe_decl = try sema.funcDeclSrc(func);
   6825     const member_str = if (member_fn) "member function " else "";
   6826     const variadic_str = if (func_ty_info.is_var_args) "at least " else "";
   6827     const msg = msg: {
   6828         const msg = try sema.errMsg(
   6829             block,
   6830             func_src,
   6831             "{s}expected {s}{d} argument(s), found {d}",
   6832             .{
   6833                 member_str,
   6834                 variadic_str,
   6835                 fn_params_len - @intFromBool(member_fn),
   6836                 args_len,
   6837             },
   6838         );
   6839         errdefer msg.destroy(sema.gpa);
   6840 
   6841         if (maybe_decl) |fn_decl| try mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{});
   6842         break :msg msg;
   6843     };
   6844     return sema.failWithOwnedErrorMsg(block, msg);
   6845 }
   6846 
   6847 fn callBuiltin(
   6848     sema: *Sema,
   6849     block: *Block,
   6850     call_src: LazySrcLoc,
   6851     builtin_fn: Air.Inst.Ref,
   6852     modifier: std.builtin.CallModifier,
   6853     args: []const Air.Inst.Ref,
   6854     operation: CallOperation,
   6855 ) !void {
   6856     const mod = sema.mod;
   6857     const callee_ty = sema.typeOf(builtin_fn);
   6858     const func_ty = func_ty: {
   6859         switch (callee_ty.zigTypeTag(mod)) {
   6860             .Fn => break :func_ty callee_ty,
   6861             .Pointer => {
   6862                 const ptr_info = callee_ty.ptrInfo(mod);
   6863                 if (ptr_info.flags.size == .One and Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Fn) {
   6864                     break :func_ty Type.fromInterned(ptr_info.child);
   6865                 }
   6866             },
   6867             else => {},
   6868         }
   6869         std.debug.panic("type '{}' is not a function calling builtin fn", .{callee_ty.fmt(mod)});
   6870     };
   6871 
   6872     const func_ty_info = mod.typeToFunc(func_ty).?;
   6873     const fn_params_len = func_ty_info.param_types.len;
   6874     if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) {
   6875         std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len });
   6876     }
   6877 
   6878     _ = try sema.analyzeCall(
   6879         block,
   6880         builtin_fn,
   6881         func_ty,
   6882         call_src,
   6883         call_src,
   6884         modifier,
   6885         false,
   6886         .{ .resolved = .{ .src = call_src, .args = args } },
   6887         null,
   6888         operation,
   6889     );
   6890 }
   6891 
   6892 const CallOperation = enum {
   6893     call,
   6894     @"@call",
   6895     @"@panic",
   6896     @"safety check",
   6897     @"error return",
   6898 };
   6899 
   6900 const CallArgsInfo = union(enum) {
   6901     /// The full list of resolved (but uncoerced) arguments is known ahead of time.
   6902     resolved: struct {
   6903         src: LazySrcLoc,
   6904         args: []const Air.Inst.Ref,
   6905     },
   6906 
   6907     /// The list of resolved (but uncoerced) arguments is known ahead of time, but
   6908     /// originated from a usage of the @call builtin at the given node offset.
   6909     call_builtin: struct {
   6910         call_node_offset: i32,
   6911         args: []const Air.Inst.Ref,
   6912     },
   6913 
   6914     /// This call corresponds to a ZIR call instruction. The arguments have not yet been
   6915     /// resolved. They must be resolved by `analyzeCall` so that argument resolution and
   6916     /// generic instantiation may be interleaved. This is required for RLS to work on
   6917     /// generic parameters.
   6918     zir_call: struct {
   6919         /// This may be `none`, in which case it is ignored. Otherwise, it is the
   6920         /// already-resolved value of the first argument, from method call syntax.
   6921         bound_arg: Air.Inst.Ref,
   6922         /// The source location of `bound_arg` if it is not `null`. Otherwise `undefined`.
   6923         bound_arg_src: LazySrcLoc,
   6924         /// The ZIR call instruction. The parameter type is placed at this index while
   6925         /// analyzing arguments.
   6926         call_inst: Zir.Inst.Index,
   6927         /// The node offset of `call_inst`.
   6928         call_node_offset: i32,
   6929         /// The number of arguments to this call, not including `bound_arg`.
   6930         num_args: u32,
   6931         /// The ZIR corresponding to all function arguments (other than `bound_arg`, if it
   6932         /// is not `none`). Format is precisely the same as trailing data of ZIR `call`.
   6933         args_body: []const Zir.Inst.Index,
   6934         /// This bool will be set to true if any argument evaluated turns out to have an error set or error union type.
   6935         /// This is used by the caller to restore the error return trace when necessary.
   6936         any_arg_is_error: *bool,
   6937     },
   6938 
   6939     fn count(cai: CallArgsInfo) usize {
   6940         return switch (cai) {
   6941             inline .resolved, .call_builtin => |resolved| resolved.args.len,
   6942             .zir_call => |zir_call| zir_call.num_args + @intFromBool(zir_call.bound_arg != .none),
   6943         };
   6944     }
   6945 
   6946     fn argSrc(cai: CallArgsInfo, block: *Block, arg_index: usize) LazySrcLoc {
   6947         return switch (cai) {
   6948             .resolved => |resolved| resolved.src,
   6949             .call_builtin => |call_builtin| .{ .call_arg = .{
   6950                 .decl = block.src_decl,
   6951                 .call_node_offset = call_builtin.call_node_offset,
   6952                 .arg_index = @intCast(arg_index),
   6953             } },
   6954             .zir_call => |zir_call| if (arg_index == 0 and zir_call.bound_arg != .none) {
   6955                 return zir_call.bound_arg_src;
   6956             } else .{ .call_arg = .{
   6957                 .decl = block.src_decl,
   6958                 .call_node_offset = zir_call.call_node_offset,
   6959                 .arg_index = @intCast(arg_index - @intFromBool(zir_call.bound_arg != .none)),
   6960             } },
   6961         };
   6962     }
   6963 
   6964     /// Analyzes the arg at `arg_index` and coerces it to `param_ty`.
   6965     /// `param_ty` may be `generic_poison` or `var_args_param`.
   6966     /// `func_ty_info` may be the type before instantiation, even if a generic
   6967     /// instantiation has been partially completed.
   6968     fn analyzeArg(
   6969         cai: CallArgsInfo,
   6970         sema: *Sema,
   6971         block: *Block,
   6972         arg_index: usize,
   6973         param_ty: Type,
   6974         func_ty_info: InternPool.Key.FuncType,
   6975         func_inst: Air.Inst.Ref,
   6976     ) CompileError!Air.Inst.Ref {
   6977         const mod = sema.mod;
   6978         const param_count = func_ty_info.param_types.len;
   6979         switch (param_ty.toIntern()) {
   6980             .generic_poison_type, .var_args_param_type => {},
   6981             else => try sema.queueFullTypeResolution(param_ty),
   6982         }
   6983         const uncoerced_arg: Air.Inst.Ref = switch (cai) {
   6984             inline .resolved, .call_builtin => |resolved| resolved.args[arg_index],
   6985             .zir_call => |zir_call| arg_val: {
   6986                 const has_bound_arg = zir_call.bound_arg != .none;
   6987                 if (arg_index == 0 and has_bound_arg) {
   6988                     break :arg_val zir_call.bound_arg;
   6989                 }
   6990                 const real_arg_idx = arg_index - @intFromBool(has_bound_arg);
   6991 
   6992                 const arg_body = if (real_arg_idx == 0) blk: {
   6993                     const start = zir_call.num_args;
   6994                     const end = @intFromEnum(zir_call.args_body[0]);
   6995                     break :blk zir_call.args_body[start..end];
   6996                 } else blk: {
   6997                     const start = @intFromEnum(zir_call.args_body[real_arg_idx - 1]);
   6998                     const end = @intFromEnum(zir_call.args_body[real_arg_idx]);
   6999                     break :blk zir_call.args_body[start..end];
   7000                 };
   7001 
   7002                 // Generate args to comptime params in comptime block
   7003                 const parent_comptime = block.is_comptime;
   7004                 defer block.is_comptime = parent_comptime;
   7005                 // Note that we are indexing into parameters, not arguments, so use `arg_index` instead of `real_arg_idx`
   7006                 if (arg_index < @min(param_count, 32) and func_ty_info.paramIsComptime(@intCast(arg_index))) {
   7007                     block.is_comptime = true;
   7008                     // TODO set comptime_reason
   7009                 }
   7010                 // Give the arg its result type
   7011                 sema.inst_map.putAssumeCapacity(zir_call.call_inst, Air.internedToRef(param_ty.toIntern()));
   7012                 // Resolve the arg!
   7013                 const uncoerced_arg = try sema.resolveBody(block, arg_body, zir_call.call_inst);
   7014 
   7015                 if (sema.typeOf(uncoerced_arg).zigTypeTag(mod) == .NoReturn) {
   7016                     // This terminates resolution of arguments. The caller should
   7017                     // propagate this.
   7018                     return uncoerced_arg;
   7019                 }
   7020 
   7021                 if (sema.typeOf(uncoerced_arg).isError(mod)) {
   7022                     zir_call.any_arg_is_error.* = true;
   7023                 }
   7024 
   7025                 break :arg_val uncoerced_arg;
   7026             },
   7027         };
   7028         switch (param_ty.toIntern()) {
   7029             .generic_poison_type => return uncoerced_arg,
   7030             .var_args_param_type => return sema.coerceVarArgParam(block, uncoerced_arg, cai.argSrc(block, arg_index)),
   7031             else => return sema.coerceExtra(
   7032                 block,
   7033                 param_ty,
   7034                 uncoerced_arg,
   7035                 cai.argSrc(block, arg_index),
   7036                 .{ .param_src = .{
   7037                     .func_inst = func_inst,
   7038                     .param_i = @intCast(arg_index),
   7039                 } },
   7040             ) catch |err| switch (err) {
   7041                 error.NotCoercible => unreachable,
   7042                 else => |e| return e,
   7043             },
   7044         }
   7045     }
   7046 };
   7047 
   7048 /// While performing an inline call, we need to switch between two Sema states a few times: the
   7049 /// state for the caller (with the callee's `code`, `fn_ret_ty`, etc), and the state for the callee.
   7050 /// These cannot be two separate Sema instances as they must share AIR.
   7051 /// Therefore, this struct acts as a helper to switch between the two.
   7052 /// This switching is required during argument evaluation, where function argument analysis must be
   7053 /// interleaved with resolving generic parameter types.
   7054 const InlineCallSema = struct {
   7055     sema: *Sema,
   7056     cur: enum {
   7057         caller,
   7058         callee,
   7059     },
   7060 
   7061     other_code: Zir,
   7062     other_func_index: InternPool.Index,
   7063     other_fn_ret_ty: Type,
   7064     other_fn_ret_ty_ies: ?*InferredErrorSet,
   7065     other_inst_map: InstMap,
   7066     other_error_return_trace_index_on_fn_entry: Air.Inst.Ref,
   7067     other_generic_owner: InternPool.Index,
   7068     other_generic_call_src: LazySrcLoc,
   7069     other_generic_call_decl: InternPool.OptionalDeclIndex,
   7070 
   7071     /// Sema should currently be set up for the caller (i.e. unchanged yet). This init will not
   7072     /// change that. The other parameters contain data for the callee Sema. The other modified
   7073     /// Sema fields are all initialized to default values for the callee.
   7074     /// Must call deinit on the result.
   7075     fn init(
   7076         sema: *Sema,
   7077         callee_code: Zir,
   7078         callee_func_index: InternPool.Index,
   7079         callee_error_return_trace_index_on_fn_entry: Air.Inst.Ref,
   7080     ) InlineCallSema {
   7081         return .{
   7082             .sema = sema,
   7083             .cur = .caller,
   7084             .other_code = callee_code,
   7085             .other_func_index = callee_func_index,
   7086             .other_fn_ret_ty = Type.void,
   7087             .other_fn_ret_ty_ies = null,
   7088             .other_inst_map = .{},
   7089             .other_error_return_trace_index_on_fn_entry = callee_error_return_trace_index_on_fn_entry,
   7090             .other_generic_owner = .none,
   7091             .other_generic_call_src = .unneeded,
   7092             .other_generic_call_decl = .none,
   7093         };
   7094     }
   7095 
   7096     /// Switch back to the caller Sema if necessary and free all temporary state of the callee Sema.
   7097     fn deinit(ics: *InlineCallSema) void {
   7098         switch (ics.cur) {
   7099             .caller => {},
   7100             .callee => ics.swap(),
   7101         }
   7102         // Callee Sema owns the inst_map memory
   7103         ics.other_inst_map.deinit(ics.sema.gpa);
   7104         ics.* = undefined;
   7105     }
   7106 
   7107     /// Returns a Sema instance suitable for usage from the caller context.
   7108     fn caller(ics: *InlineCallSema) *Sema {
   7109         switch (ics.cur) {
   7110             .caller => {},
   7111             .callee => ics.swap(),
   7112         }
   7113         return ics.sema;
   7114     }
   7115 
   7116     /// Returns a Sema instance suitable for usage from the callee context.
   7117     fn callee(ics: *InlineCallSema) *Sema {
   7118         switch (ics.cur) {
   7119             .caller => ics.swap(),
   7120             .callee => {},
   7121         }
   7122         return ics.sema;
   7123     }
   7124 
   7125     /// Internal use only. Swaps to the other Sema state.
   7126     fn swap(ics: *InlineCallSema) void {
   7127         ics.cur = switch (ics.cur) {
   7128             .caller => .callee,
   7129             .callee => .caller,
   7130         };
   7131         // zig fmt: off
   7132         std.mem.swap(Zir,                &ics.sema.code,              &ics.other_code);
   7133         std.mem.swap(InternPool.Index,   &ics.sema.func_index,        &ics.other_func_index);
   7134         std.mem.swap(Type,               &ics.sema.fn_ret_ty,         &ics.other_fn_ret_ty);
   7135         std.mem.swap(?*InferredErrorSet, &ics.sema.fn_ret_ty_ies,     &ics.other_fn_ret_ty_ies);
   7136         std.mem.swap(InstMap,            &ics.sema.inst_map,          &ics.other_inst_map);
   7137         std.mem.swap(InternPool.Index,   &ics.sema.generic_owner,     &ics.other_generic_owner);
   7138         std.mem.swap(LazySrcLoc,         &ics.sema.generic_call_src,  &ics.other_generic_call_src);
   7139         std.mem.swap(InternPool.OptionalDeclIndex, &ics.sema.generic_call_decl, &ics.other_generic_call_decl);
   7140         std.mem.swap(Air.Inst.Ref,       &ics.sema.error_return_trace_index_on_fn_entry, &ics.other_error_return_trace_index_on_fn_entry);
   7141         // zig fmt: on
   7142     }
   7143 };
   7144 
   7145 fn analyzeCall(
   7146     sema: *Sema,
   7147     block: *Block,
   7148     func: Air.Inst.Ref,
   7149     func_ty: Type,
   7150     func_src: LazySrcLoc,
   7151     call_src: LazySrcLoc,
   7152     modifier: std.builtin.CallModifier,
   7153     ensure_result_used: bool,
   7154     args_info: CallArgsInfo,
   7155     call_dbg_node: ?Zir.Inst.Index,
   7156     operation: CallOperation,
   7157 ) CompileError!Air.Inst.Ref {
   7158     const mod = sema.mod;
   7159     const ip = &mod.intern_pool;
   7160 
   7161     const callee_ty = sema.typeOf(func);
   7162     const func_ty_info = mod.typeToFunc(func_ty).?;
   7163     const cc = func_ty_info.cc;
   7164     if (try sema.resolveValue(func)) |func_val|
   7165         if (func_val.isUndef(mod))
   7166             return sema.failWithUseOfUndef(block, call_src);
   7167     if (cc == .Naked) {
   7168         const maybe_decl = try sema.funcDeclSrc(func);
   7169         const msg = msg: {
   7170             const msg = try sema.errMsg(
   7171                 block,
   7172                 func_src,
   7173                 "unable to call function with naked calling convention",
   7174                 .{},
   7175             );
   7176             errdefer msg.destroy(sema.gpa);
   7177 
   7178             if (maybe_decl) |fn_decl| try mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{});
   7179             break :msg msg;
   7180         };
   7181         return sema.failWithOwnedErrorMsg(block, msg);
   7182     }
   7183 
   7184     const call_tag: Air.Inst.Tag = switch (modifier) {
   7185         .auto,
   7186         .always_inline,
   7187         .compile_time,
   7188         .no_async,
   7189         => Air.Inst.Tag.call,
   7190 
   7191         .never_tail => Air.Inst.Tag.call_never_tail,
   7192         .never_inline => Air.Inst.Tag.call_never_inline,
   7193         .always_tail => Air.Inst.Tag.call_always_tail,
   7194 
   7195         .async_kw => return sema.failWithUseOfAsync(block, call_src),
   7196     };
   7197 
   7198     if (modifier == .never_inline and func_ty_info.cc == .Inline) {
   7199         return sema.fail(block, call_src, "'never_inline' call of inline function", .{});
   7200     }
   7201     if (modifier == .always_inline and func_ty_info.is_noinline) {
   7202         return sema.fail(block, call_src, "'always_inline' call of noinline function", .{});
   7203     }
   7204 
   7205     const gpa = sema.gpa;
   7206 
   7207     var is_generic_call = func_ty_info.is_generic;
   7208     var is_comptime_call = block.is_comptime or modifier == .compile_time;
   7209     var comptime_reason: ?*const Block.ComptimeReason = null;
   7210     if (!is_comptime_call) {
   7211         if (sema.typeRequiresComptime(Type.fromInterned(func_ty_info.return_type))) |ct| {
   7212             is_comptime_call = ct;
   7213             if (ct) {
   7214                 comptime_reason = &.{ .comptime_ret_ty = .{
   7215                     .block = block,
   7216                     .func = func,
   7217                     .func_src = func_src,
   7218                     .return_ty = Type.fromInterned(func_ty_info.return_type),
   7219                 } };
   7220             }
   7221         } else |err| switch (err) {
   7222             error.GenericPoison => is_generic_call = true,
   7223             else => |e| return e,
   7224         }
   7225     }
   7226     var is_inline_call = is_comptime_call or modifier == .always_inline or
   7227         func_ty_info.cc == .Inline;
   7228 
   7229     if (sema.func_is_naked and !is_inline_call and !is_comptime_call) {
   7230         const msg = msg: {
   7231             const msg = try sema.errMsg(block, call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)});
   7232             errdefer msg.destroy(sema.gpa);
   7233 
   7234             switch (operation) {
   7235                 .call, .@"@call", .@"@panic", .@"error return" => {},
   7236                 .@"safety check" => try sema.errNote(block, call_src, msg, "use @setRuntimeSafety to disable runtime safety", .{}),
   7237             }
   7238             break :msg msg;
   7239         };
   7240         return sema.failWithOwnedErrorMsg(block, msg);
   7241     }
   7242 
   7243     if (!is_inline_call and is_generic_call) {
   7244         if (sema.instantiateGenericCall(
   7245             block,
   7246             func,
   7247             func_src,
   7248             call_src,
   7249             ensure_result_used,
   7250             args_info,
   7251             call_tag,
   7252             call_dbg_node,
   7253         )) |some| {
   7254             return some;
   7255         } else |err| switch (err) {
   7256             error.GenericPoison => {
   7257                 is_inline_call = true;
   7258             },
   7259             error.ComptimeReturn => {
   7260                 is_inline_call = true;
   7261                 is_comptime_call = true;
   7262                 comptime_reason = &.{ .comptime_ret_ty = .{
   7263                     .block = block,
   7264                     .func = func,
   7265                     .func_src = func_src,
   7266                     .return_ty = Type.fromInterned(func_ty_info.return_type),
   7267                 } };
   7268             },
   7269             else => |e| return e,
   7270         }
   7271     }
   7272 
   7273     if (is_comptime_call and modifier == .never_inline) {
   7274         return sema.fail(block, call_src, "unable to perform 'never_inline' call at compile-time", .{});
   7275     }
   7276 
   7277     const result: Air.Inst.Ref = if (is_inline_call) res: {
   7278         const func_val = try sema.resolveConstDefinedValue(block, func_src, func, .{
   7279             .needed_comptime_reason = "function being called at comptime must be comptime-known",
   7280             .block_comptime_reason = comptime_reason,
   7281         });
   7282         const prev_fn_index = sema.func_index;
   7283         const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) {
   7284             .extern_func => return sema.fail(block, call_src, "{s} call of extern function", .{
   7285                 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
   7286             }),
   7287             .func => func_val.toIntern(),
   7288             .ptr => |ptr| switch (ptr.addr) {
   7289                 .decl => |decl| blk: {
   7290                     const func_val_ptr = mod.declPtr(decl).val.toIntern();
   7291                     const intern_index = mod.intern_pool.indexToKey(func_val_ptr);
   7292                     if (intern_index == .extern_func or (intern_index == .variable and intern_index.variable.is_extern))
   7293                         return sema.fail(block, call_src, "{s} call of extern function pointer", .{
   7294                             @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
   7295                         });
   7296                     break :blk func_val_ptr;
   7297                 },
   7298                 else => {
   7299                     assert(callee_ty.isPtrAtRuntime(mod));
   7300                     return sema.fail(block, call_src, "{s} call of function pointer", .{
   7301                         @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
   7302                     });
   7303                 },
   7304             },
   7305             else => unreachable,
   7306         };
   7307         if (func_ty_info.is_var_args) {
   7308             return sema.fail(block, call_src, "{s} call of variadic function", .{
   7309                 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
   7310             });
   7311         }
   7312 
   7313         // Analyze the ZIR. The same ZIR gets analyzed into a runtime function
   7314         // or an inlined call depending on what union tag the `label` field is
   7315         // set to in the `Block`.
   7316         // This block instruction will be used to capture the return value from the
   7317         // inlined function.
   7318         const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   7319         try sema.air_instructions.append(gpa, .{
   7320             .tag = .block,
   7321             .data = undefined,
   7322         });
   7323         // This one is shared among sub-blocks within the same callee, but not
   7324         // shared among the entire inline/comptime call stack.
   7325         var inlining: Block.Inlining = .{
   7326             .call_block = block,
   7327             .call_src = call_src,
   7328             .has_comptime_args = false,
   7329             .func = module_fn_index,
   7330             .comptime_result = undefined,
   7331             .merges = .{
   7332                 .src_locs = .{},
   7333                 .results = .{},
   7334                 .br_list = .{},
   7335                 .block_inst = block_inst,
   7336             },
   7337         };
   7338 
   7339         const module_fn = mod.funcInfo(module_fn_index);
   7340         const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
   7341 
   7342         // We effectively want a child Sema here, but can't literally do that, because we need AIR
   7343         // to be shared. InlineCallSema is a wrapper which handles this for us. While `ics` is in
   7344         // scope, we should use its `caller`/`callee` methods rather than using `sema` directly
   7345         // whenever performing an operation where the difference matters.
   7346         var ics = InlineCallSema.init(
   7347             sema,
   7348             fn_owner_decl.getFileScope(mod).zir,
   7349             module_fn_index,
   7350             block.error_return_trace_index,
   7351         );
   7352         defer ics.deinit();
   7353 
   7354         var child_block: Block = .{
   7355             .parent = null,
   7356             .sema = sema,
   7357             .src_decl = module_fn.owner_decl,
   7358             .namespace = fn_owner_decl.src_namespace,
   7359             .wip_capture_scope = try mod.createCaptureScope(fn_owner_decl.src_scope),
   7360             .instructions = .{},
   7361             .label = null,
   7362             .inlining = &inlining,
   7363             .is_typeof = block.is_typeof,
   7364             .is_comptime = is_comptime_call,
   7365             .comptime_reason = comptime_reason,
   7366             .error_return_trace_index = block.error_return_trace_index,
   7367         };
   7368 
   7369         const merges = &child_block.inlining.?.merges;
   7370 
   7371         defer child_block.instructions.deinit(gpa);
   7372         defer merges.deinit(gpa);
   7373 
   7374         try sema.emitBackwardBranch(block, call_src);
   7375 
   7376         // Whether this call should be memoized, set to false if the call can
   7377         // mutate comptime state.
   7378         var should_memoize = true;
   7379 
   7380         // If it's a comptime function call, we need to memoize it as long as no external
   7381         // comptime memory is mutated.
   7382         const memoized_arg_values = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len);
   7383 
   7384         const owner_info = mod.typeToFunc(fn_owner_decl.ty).?;
   7385         const new_param_types = try sema.arena.alloc(InternPool.Index, owner_info.param_types.len);
   7386         var new_fn_info: InternPool.GetFuncTypeKey = .{
   7387             .param_types = new_param_types,
   7388             .return_type = owner_info.return_type,
   7389             .noalias_bits = owner_info.noalias_bits,
   7390             .alignment = if (owner_info.align_is_generic) null else owner_info.alignment,
   7391             .cc = if (owner_info.cc_is_generic) null else owner_info.cc,
   7392             .is_var_args = owner_info.is_var_args,
   7393             .is_noinline = owner_info.is_noinline,
   7394             .section_is_generic = owner_info.section_is_generic,
   7395             .addrspace_is_generic = owner_info.addrspace_is_generic,
   7396             .is_generic = owner_info.is_generic,
   7397         };
   7398 
   7399         // This will have return instructions analyzed as break instructions to
   7400         // the block_inst above. Here we are performing "comptime/inline semantic analysis"
   7401         // for a function body, which means we must map the parameter ZIR instructions to
   7402         // the AIR instructions of the callsite. The callee could be a generic function
   7403         // which means its parameter type expressions must be resolved in order and used
   7404         // to successively coerce the arguments.
   7405         const fn_info = ics.callee().code.getFnInfo(module_fn.zir_body_inst);
   7406         try ics.callee().inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
   7407 
   7408         var arg_i: u32 = 0;
   7409         for (fn_info.param_body) |inst| {
   7410             const opt_noreturn_ref = try analyzeInlineCallArg(
   7411                 &ics,
   7412                 block,
   7413                 &child_block,
   7414                 inst,
   7415                 new_param_types,
   7416                 &arg_i,
   7417                 args_info,
   7418                 is_comptime_call,
   7419                 &should_memoize,
   7420                 memoized_arg_values,
   7421                 func_ty_info,
   7422                 func,
   7423             );
   7424             if (opt_noreturn_ref) |ref| {
   7425                 // Analyzing this argument gave a ref of a noreturn type. Terminate argument analysis here.
   7426                 return ref;
   7427             }
   7428         }
   7429 
   7430         // From here, we only really need to use the callee Sema. Make it the active one, then we
   7431         // can just use `sema` directly.
   7432         _ = ics.callee();
   7433 
   7434         if (!inlining.has_comptime_args) {
   7435             if (module_fn.analysis(ip).state == .sema_failure)
   7436                 return error.AnalysisFail;
   7437 
   7438             var block_it = block;
   7439             while (block_it.inlining) |parent_inlining| {
   7440                 if (!parent_inlining.has_comptime_args and parent_inlining.func == module_fn_index) {
   7441                     const err_msg = try sema.errMsg(block, call_src, "inline call is recursive", .{});
   7442                     return sema.failWithOwnedErrorMsg(null, err_msg);
   7443                 }
   7444                 block_it = parent_inlining.call_block;
   7445             }
   7446         }
   7447 
   7448         // In case it is a generic function with an expression for the return type that depends
   7449         // on parameters, we must now do the same for the return type as we just did with
   7450         // each of the parameters, resolving the return type and providing it to the child
   7451         // `Sema` so that it can be used for the `ret_ptr` instruction.
   7452         const ret_ty_inst = if (fn_info.ret_ty_body.len != 0)
   7453             try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst)
   7454         else
   7455             try sema.resolveInst(fn_info.ret_ty_ref);
   7456         const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
   7457         sema.fn_ret_ty = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
   7458         if (module_fn.analysis(ip).inferred_error_set) {
   7459             // Create a fresh inferred error set type for inline/comptime calls.
   7460             const ies = try sema.arena.create(InferredErrorSet);
   7461             ies.* = .{ .func = .none };
   7462             sema.fn_ret_ty_ies = ies;
   7463             sema.fn_ret_ty = Type.fromInterned((try ip.get(gpa, .{ .error_union_type = .{
   7464                 .error_set_type = .adhoc_inferred_error_set_type,
   7465                 .payload_type = sema.fn_ret_ty.toIntern(),
   7466             } })));
   7467         }
   7468 
   7469         // This `res2` is here instead of directly breaking from `res` due to a stage1
   7470         // bug generating invalid LLVM IR.
   7471         const res2: Air.Inst.Ref = res2: {
   7472             if (should_memoize and is_comptime_call) {
   7473                 if (mod.intern_pool.getIfExists(.{ .memoized_call = .{
   7474                     .func = module_fn_index,
   7475                     .arg_values = memoized_arg_values,
   7476                     .result = .none,
   7477                 } })) |memoized_call_index| {
   7478                     const memoized_call = mod.intern_pool.indexToKey(memoized_call_index).memoized_call;
   7479                     break :res2 Air.internedToRef(memoized_call.result);
   7480                 }
   7481             }
   7482 
   7483             new_fn_info.return_type = sema.fn_ret_ty.toIntern();
   7484             const new_func_resolved_ty = try mod.funcType(new_fn_info);
   7485             if (!is_comptime_call and !block.is_typeof) {
   7486                 try emitDbgInline(block, prev_fn_index, module_fn_index, new_func_resolved_ty, .dbg_inline_begin);
   7487 
   7488                 const zir_tags = sema.code.instructions.items(.tag);
   7489                 for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) {
   7490                     .param, .param_comptime => {
   7491                         const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].pl_tok;
   7492                         const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
   7493                         const param_name = sema.code.nullTerminatedString(extra.data.name);
   7494                         const inst = sema.inst_map.get(param).?;
   7495 
   7496                         try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name);
   7497                     },
   7498                     .param_anytype, .param_anytype_comptime => {
   7499                         const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].str_tok;
   7500                         const param_name = inst_data.get(sema.code);
   7501                         const inst = sema.inst_map.get(param).?;
   7502 
   7503                         try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name);
   7504                     },
   7505                     else => continue,
   7506                 };
   7507             }
   7508 
   7509             if (is_comptime_call and ensure_result_used) {
   7510                 try sema.ensureResultUsed(block, sema.fn_ret_ty, call_src);
   7511             }
   7512 
   7513             const result = result: {
   7514                 sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
   7515                     error.ComptimeReturn => break :result inlining.comptime_result,
   7516                     else => |e| return e,
   7517                 };
   7518                 break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges);
   7519             };
   7520 
   7521             if (!is_comptime_call and !block.is_typeof and
   7522                 sema.typeOf(result).zigTypeTag(mod) != .NoReturn)
   7523             {
   7524                 try emitDbgInline(
   7525                     block,
   7526                     module_fn_index,
   7527                     prev_fn_index,
   7528                     mod.funcOwnerDeclPtr(sema.func_index).ty,
   7529                     .dbg_inline_end,
   7530                 );
   7531             }
   7532 
   7533             if (should_memoize and is_comptime_call) {
   7534                 const result_val = try sema.resolveConstValue(block, .unneeded, result, undefined);
   7535                 const result_interned = try result_val.intern2(sema.fn_ret_ty, mod);
   7536 
   7537                 // Transform ad-hoc inferred error set types into concrete error sets.
   7538                 const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_interned);
   7539 
   7540                 // TODO: check whether any external comptime memory was mutated by the
   7541                 // comptime function call. If so, then do not memoize the call here.
   7542                 _ = try mod.intern(.{ .memoized_call = .{
   7543                     .func = module_fn_index,
   7544                     .arg_values = memoized_arg_values,
   7545                     .result = result_transformed,
   7546                 } });
   7547 
   7548                 break :res2 Air.internedToRef(result_transformed);
   7549             }
   7550 
   7551             if (try sema.resolveValue(result)) |result_val| {
   7552                 const result_interned = try result_val.intern2(sema.fn_ret_ty, mod);
   7553                 const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_interned);
   7554                 break :res2 Air.internedToRef(result_transformed);
   7555             }
   7556 
   7557             const new_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result).toIntern());
   7558             if (new_ty != .none) {
   7559                 // TODO: mutate in place the previous instruction if possible
   7560                 // rather than adding a bitcast instruction.
   7561                 break :res2 try block.addBitCast(Type.fromInterned(new_ty), result);
   7562             }
   7563 
   7564             break :res2 result;
   7565         };
   7566 
   7567         break :res res2;
   7568     } else res: {
   7569         assert(!func_ty_info.is_generic);
   7570 
   7571         const args = try sema.arena.alloc(Air.Inst.Ref, args_info.count());
   7572         for (args, 0..) |*arg_out, arg_idx| {
   7573             // Non-generic, so param types are already resolved
   7574             const param_ty = if (arg_idx < func_ty_info.param_types.len) ty: {
   7575                 break :ty Type.fromInterned(func_ty_info.param_types.get(ip)[arg_idx]);
   7576             } else Type.fromInterned(InternPool.Index.var_args_param_type);
   7577             assert(!param_ty.isGenericPoison());
   7578             arg_out.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, func);
   7579             if (sema.typeOf(arg_out.*).zigTypeTag(mod) == .NoReturn) {
   7580                 return arg_out.*;
   7581             }
   7582         }
   7583 
   7584         if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
   7585 
   7586         try sema.queueFullTypeResolution(Type.fromInterned(func_ty_info.return_type));
   7587         if (sema.owner_func_index != .none and Type.fromInterned(func_ty_info.return_type).isError(mod)) {
   7588             ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn = true;
   7589         }
   7590 
   7591         if (try sema.resolveValue(func)) |func_val| {
   7592             if (mod.intern_pool.isFuncBody(func_val.toIntern())) {
   7593                 try mod.ensureFuncBodyAnalysisQueued(func_val.toIntern());
   7594             }
   7595         }
   7596 
   7597         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
   7598             args.len);
   7599         const func_inst = try block.addInst(.{
   7600             .tag = call_tag,
   7601             .data = .{ .pl_op = .{
   7602                 .operand = func,
   7603                 .payload = sema.addExtraAssumeCapacity(Air.Call{
   7604                     .args_len = @intCast(args.len),
   7605                 }),
   7606             } },
   7607         });
   7608         sema.appendRefsAssumeCapacity(args);
   7609 
   7610         if (call_tag == .call_always_tail) {
   7611             if (ensure_result_used) {
   7612                 try sema.ensureResultUsed(block, sema.typeOf(func_inst), call_src);
   7613             }
   7614             return sema.handleTailCall(block, call_src, func_ty, func_inst);
   7615         }
   7616         if (block.wantSafety() and func_ty_info.return_type == .noreturn_type) skip_safety: {
   7617             // Function pointers and extern functions aren't guaranteed to
   7618             // actually be noreturn so we add a safety check for them.
   7619             if (try sema.resolveValue(func)) |func_val| {
   7620                 switch (mod.intern_pool.indexToKey(func_val.toIntern())) {
   7621                     .func => break :skip_safety,
   7622                     .ptr => |ptr| switch (ptr.addr) {
   7623                         .decl => |decl| if (!mod.declPtr(decl).isExtern(mod)) break :skip_safety,
   7624                         else => {},
   7625                     },
   7626                     else => {},
   7627                 }
   7628             }
   7629             try sema.safetyPanic(block, call_src, .noreturn_returned);
   7630             return .unreachable_value;
   7631         }
   7632         if (func_ty_info.return_type == .noreturn_type) {
   7633             _ = try block.addNoOp(.unreach);
   7634             return .unreachable_value;
   7635         }
   7636         break :res func_inst;
   7637     };
   7638 
   7639     if (ensure_result_used) {
   7640         try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
   7641     }
   7642     return result;
   7643 }
   7644 
   7645 fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {
   7646     const mod = sema.mod;
   7647     const target = mod.getTarget();
   7648     const backend = mod.comp.getZigBackend();
   7649     if (!target_util.supportsTailCall(target, backend)) {
   7650         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", .{
   7651             @tagName(backend), @tagName(target.cpu.arch),
   7652         });
   7653     }
   7654     const func_decl = mod.funcOwnerDeclPtr(sema.owner_func_index);
   7655     if (!func_ty.eql(func_decl.ty, mod)) {
   7656         return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{}' does not match type of calling function '{}'", .{
   7657             func_ty.fmt(mod), func_decl.ty.fmt(mod),
   7658         });
   7659     }
   7660     _ = try block.addUnOp(.ret, result);
   7661     return .unreachable_value;
   7662 }
   7663 
   7664 /// Usually, returns null. If an argument was noreturn, returns that ref (which should become the call result).
   7665 fn analyzeInlineCallArg(
   7666     ics: *InlineCallSema,
   7667     arg_block: *Block,
   7668     param_block: *Block,
   7669     inst: Zir.Inst.Index,
   7670     new_param_types: []InternPool.Index,
   7671     arg_i: *u32,
   7672     args_info: CallArgsInfo,
   7673     is_comptime_call: bool,
   7674     should_memoize: *bool,
   7675     memoized_arg_values: []InternPool.Index,
   7676     func_ty_info: InternPool.Key.FuncType,
   7677     func_inst: Air.Inst.Ref,
   7678 ) !?Air.Inst.Ref {
   7679     const mod = ics.sema.mod;
   7680     const ip = &mod.intern_pool;
   7681     const zir_tags = ics.callee().code.instructions.items(.tag);
   7682     switch (zir_tags[@intFromEnum(inst)]) {
   7683         .param_comptime, .param_anytype_comptime => param_block.inlining.?.has_comptime_args = true,
   7684         else => {},
   7685     }
   7686     switch (zir_tags[@intFromEnum(inst)]) {
   7687         .param, .param_comptime => {
   7688             // Evaluate the parameter type expression now that previous ones have
   7689             // been mapped, and coerce the corresponding argument to it.
   7690             const pl_tok = ics.callee().code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
   7691             const param_src = pl_tok.src();
   7692             const extra = ics.callee().code.extraData(Zir.Inst.Param, pl_tok.payload_index);
   7693             const param_body = ics.callee().code.bodySlice(extra.end, extra.data.body_len);
   7694             const param_ty = param_ty: {
   7695                 const raw_param_ty = func_ty_info.param_types.get(ip)[arg_i.*];
   7696                 if (raw_param_ty != .generic_poison_type) break :param_ty raw_param_ty;
   7697                 const param_ty_inst = try ics.callee().resolveBody(param_block, param_body, inst);
   7698                 const param_ty = try ics.callee().analyzeAsType(param_block, param_src, param_ty_inst);
   7699                 break :param_ty param_ty.toIntern();
   7700             };
   7701             new_param_types[arg_i.*] = param_ty;
   7702             const casted_arg = try args_info.analyzeArg(ics.caller(), arg_block, arg_i.*, Type.fromInterned(param_ty), func_ty_info, func_inst);
   7703             if (ics.caller().typeOf(casted_arg).zigTypeTag(mod) == .NoReturn) {
   7704                 return casted_arg;
   7705             }
   7706             const arg_src = args_info.argSrc(arg_block, arg_i.*);
   7707             if (try ics.callee().typeRequiresComptime(Type.fromInterned(param_ty))) {
   7708                 _ = try ics.caller().resolveConstValue(arg_block, arg_src, casted_arg, .{
   7709                     .needed_comptime_reason = "argument to parameter with comptime-only type must be comptime-known",
   7710                     .block_comptime_reason = param_block.comptime_reason,
   7711                 });
   7712             } else if (!is_comptime_call and zir_tags[@intFromEnum(inst)] == .param_comptime) {
   7713                 _ = try ics.caller().resolveConstValue(arg_block, arg_src, casted_arg, .{
   7714                     .needed_comptime_reason = "parameter is comptime",
   7715                 });
   7716             }
   7717 
   7718             if (is_comptime_call) {
   7719                 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, casted_arg);
   7720                 const arg_val = try ics.caller().resolveConstValue(arg_block, arg_src, casted_arg, .{
   7721                     .needed_comptime_reason = "argument to function being called at comptime must be comptime-known",
   7722                     .block_comptime_reason = param_block.comptime_reason,
   7723                 });
   7724                 switch (arg_val.toIntern()) {
   7725                     .generic_poison, .generic_poison_type => {
   7726                         // This function is currently evaluated as part of an as-of-yet unresolvable
   7727                         // parameter or return type.
   7728                         return error.GenericPoison;
   7729                     },
   7730                     else => {},
   7731                 }
   7732                 // Needed so that lazy values do not trigger
   7733                 // assertion due to type not being resolved
   7734                 // when the hash function is called.
   7735                 const resolved_arg_val = try ics.caller().resolveLazyValue(arg_val);
   7736                 should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod);
   7737                 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(Type.fromInterned(param_ty), mod);
   7738             } else {
   7739                 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, casted_arg);
   7740             }
   7741 
   7742             if (try ics.caller().resolveValue(casted_arg)) |_| {
   7743                 param_block.inlining.?.has_comptime_args = true;
   7744             }
   7745 
   7746             arg_i.* += 1;
   7747         },
   7748         .param_anytype, .param_anytype_comptime => {
   7749             // No coercion needed.
   7750             const uncasted_arg = try args_info.analyzeArg(ics.caller(), arg_block, arg_i.*, Type.generic_poison, func_ty_info, func_inst);
   7751             if (ics.caller().typeOf(uncasted_arg).zigTypeTag(mod) == .NoReturn) {
   7752                 return uncasted_arg;
   7753             }
   7754             const arg_src = args_info.argSrc(arg_block, arg_i.*);
   7755             new_param_types[arg_i.*] = ics.caller().typeOf(uncasted_arg).toIntern();
   7756 
   7757             if (is_comptime_call) {
   7758                 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
   7759                 const arg_val = try ics.caller().resolveConstValue(arg_block, arg_src, uncasted_arg, .{
   7760                     .needed_comptime_reason = "argument to function being called at comptime must be comptime-known",
   7761                     .block_comptime_reason = param_block.comptime_reason,
   7762                 });
   7763                 switch (arg_val.toIntern()) {
   7764                     .generic_poison, .generic_poison_type => {
   7765                         // This function is currently evaluated as part of an as-of-yet unresolvable
   7766                         // parameter or return type.
   7767                         return error.GenericPoison;
   7768                     },
   7769                     else => {},
   7770                 }
   7771                 // Needed so that lazy values do not trigger
   7772                 // assertion due to type not being resolved
   7773                 // when the hash function is called.
   7774                 const resolved_arg_val = try ics.caller().resolveLazyValue(arg_val);
   7775                 should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod);
   7776                 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(ics.caller().typeOf(uncasted_arg), mod);
   7777             } else {
   7778                 if (zir_tags[@intFromEnum(inst)] == .param_anytype_comptime) {
   7779                     _ = try ics.caller().resolveConstValue(arg_block, arg_src, uncasted_arg, .{
   7780                         .needed_comptime_reason = "parameter is comptime",
   7781                     });
   7782                 }
   7783                 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
   7784             }
   7785 
   7786             if (try ics.caller().resolveValue(uncasted_arg)) |_| {
   7787                 param_block.inlining.?.has_comptime_args = true;
   7788             }
   7789 
   7790             arg_i.* += 1;
   7791         },
   7792         else => {},
   7793     }
   7794 
   7795     return null;
   7796 }
   7797 
   7798 fn instantiateGenericCall(
   7799     sema: *Sema,
   7800     block: *Block,
   7801     func: Air.Inst.Ref,
   7802     func_src: LazySrcLoc,
   7803     call_src: LazySrcLoc,
   7804     ensure_result_used: bool,
   7805     args_info: CallArgsInfo,
   7806     call_tag: Air.Inst.Tag,
   7807     call_dbg_node: ?Zir.Inst.Index,
   7808 ) CompileError!Air.Inst.Ref {
   7809     const mod = sema.mod;
   7810     const gpa = sema.gpa;
   7811     const ip = &mod.intern_pool;
   7812 
   7813     const func_val = try sema.resolveConstDefinedValue(block, func_src, func, .{
   7814         .needed_comptime_reason = "generic function being called must be comptime-known",
   7815     });
   7816     const generic_owner = switch (mod.intern_pool.indexToKey(func_val.toIntern())) {
   7817         .func => func_val.toIntern(),
   7818         .ptr => |ptr| mod.declPtr(ptr.addr.decl).val.toIntern(),
   7819         else => unreachable,
   7820     };
   7821     const generic_owner_func = mod.intern_pool.indexToKey(generic_owner).func;
   7822     const generic_owner_ty_info = mod.typeToFunc(Type.fromInterned(generic_owner_func.ty)).?;
   7823 
   7824     // Even though there may already be a generic instantiation corresponding
   7825     // to this callsite, we must evaluate the expressions of the generic
   7826     // function signature with the values of the callsite plugged in.
   7827     // Importantly, this may include type coercions that determine whether the
   7828     // instantiation is a match of a previous instantiation.
   7829     // The actual monomorphization happens via adding `func_instance` to
   7830     // `InternPool`.
   7831 
   7832     const fn_owner_decl = mod.declPtr(generic_owner_func.owner_decl);
   7833     const namespace_index = fn_owner_decl.src_namespace;
   7834     const namespace = mod.namespacePtr(namespace_index);
   7835     const fn_zir = namespace.file_scope.zir;
   7836     const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst);
   7837 
   7838     const comptime_args = try sema.arena.alloc(InternPool.Index, args_info.count());
   7839     @memset(comptime_args, .none);
   7840 
   7841     // We may overestimate the number of runtime args, but this will definitely be sufficient.
   7842     const max_runtime_args = args_info.count() - @popCount(generic_owner_ty_info.comptime_bits);
   7843     var runtime_args = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(sema.arena, max_runtime_args);
   7844 
   7845     // Re-run the block that creates the function, with the comptime parameters
   7846     // pre-populated inside `inst_map`. This causes `param_comptime` and
   7847     // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a
   7848     // new, monomorphized function, with the comptime parameters elided.
   7849     var child_sema: Sema = .{
   7850         .mod = mod,
   7851         .gpa = gpa,
   7852         .arena = sema.arena,
   7853         .code = fn_zir,
   7854         // We pass the generic callsite's owner decl here because whatever `Decl`
   7855         // dependencies are chased at this point should be attached to the
   7856         // callsite, not the `Decl` associated with the `func_instance`.
   7857         .owner_decl = sema.owner_decl,
   7858         .owner_decl_index = sema.owner_decl_index,
   7859         .func_index = sema.owner_func_index,
   7860         // This may not be known yet, since the calling convention could be generic, but there
   7861         // should be no illegal instructions encountered while creating the function anyway.
   7862         .func_is_naked = false,
   7863         .fn_ret_ty = Type.void,
   7864         .fn_ret_ty_ies = null,
   7865         .owner_func_index = .none,
   7866         .comptime_args = comptime_args,
   7867         .generic_owner = generic_owner,
   7868         .generic_call_src = call_src,
   7869         .generic_call_decl = block.src_decl.toOptional(),
   7870         .branch_quota = sema.branch_quota,
   7871         .branch_count = sema.branch_count,
   7872         .comptime_mutable_decls = sema.comptime_mutable_decls,
   7873     };
   7874     defer child_sema.deinit();
   7875 
   7876     var child_block: Block = .{
   7877         .parent = null,
   7878         .sema = &child_sema,
   7879         .src_decl = generic_owner_func.owner_decl,
   7880         .namespace = namespace_index,
   7881         .wip_capture_scope = try mod.createCaptureScope(sema.owner_decl.src_scope),
   7882         .instructions = .{},
   7883         .inlining = null,
   7884         .is_comptime = true,
   7885     };
   7886     defer child_block.instructions.deinit(gpa);
   7887 
   7888     try child_sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
   7889 
   7890     for (fn_info.param_body[0..args_info.count()], 0..) |param_inst, arg_index| {
   7891         const param_tag = fn_zir.instructions.items(.tag)[@intFromEnum(param_inst)];
   7892 
   7893         const param_ty = switch (generic_owner_ty_info.param_types.get(ip)[arg_index]) {
   7894             else => |ty| Type.fromInterned(ty), // parameter is not generic, so type is already resolved
   7895             .generic_poison_type => param_ty: {
   7896                 // We have every parameter before this one, so can resolve this parameter's type now.
   7897                 // However, first check the param type, since it may be anytype.
   7898                 switch (param_tag) {
   7899                     .param_anytype, .param_anytype_comptime => {
   7900                         // The parameter doesn't have a type.
   7901                         break :param_ty Type.generic_poison;
   7902                     },
   7903                     .param, .param_comptime => {
   7904                         // We now know every prior parameter, so can resolve this
   7905                         // parameter's type. The child sema has these types.
   7906                         const param_data = fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok;
   7907                         const param_extra = fn_zir.extraData(Zir.Inst.Param, param_data.payload_index);
   7908                         const param_ty_body = fn_zir.bodySlice(param_extra.end, param_extra.data.body_len);
   7909 
   7910                         // Make sure any nested instructions don't clobber our work.
   7911                         const prev_params = child_block.params;
   7912                         const prev_no_partial_func_ty = child_sema.no_partial_func_ty;
   7913                         const prev_generic_owner = child_sema.generic_owner;
   7914                         const prev_generic_call_src = child_sema.generic_call_src;
   7915                         const prev_generic_call_decl = child_sema.generic_call_decl;
   7916                         child_block.params = .{};
   7917                         child_sema.no_partial_func_ty = true;
   7918                         child_sema.generic_owner = .none;
   7919                         child_sema.generic_call_src = .unneeded;
   7920                         child_sema.generic_call_decl = .none;
   7921                         defer {
   7922                             child_block.params = prev_params;
   7923                             child_sema.no_partial_func_ty = prev_no_partial_func_ty;
   7924                             child_sema.generic_owner = prev_generic_owner;
   7925                             child_sema.generic_call_src = prev_generic_call_src;
   7926                             child_sema.generic_call_decl = prev_generic_call_decl;
   7927                         }
   7928 
   7929                         const param_ty_inst = try child_sema.resolveBody(&child_block, param_ty_body, param_inst);
   7930                         break :param_ty try child_sema.analyzeAsType(&child_block, param_data.src(), param_ty_inst);
   7931                     },
   7932                     else => unreachable,
   7933                 }
   7934             },
   7935         };
   7936         const arg_ref = try args_info.analyzeArg(sema, block, arg_index, param_ty, generic_owner_ty_info, func);
   7937         const arg_ty = sema.typeOf(arg_ref);
   7938         if (arg_ty.zigTypeTag(mod) == .NoReturn) {
   7939             // This terminates argument analysis.
   7940             return arg_ref;
   7941         }
   7942 
   7943         const arg_is_comptime = switch (param_tag) {
   7944             .param_comptime, .param_anytype_comptime => true,
   7945             .param, .param_anytype => try sema.typeRequiresComptime(arg_ty),
   7946             else => unreachable,
   7947         };
   7948 
   7949         if (arg_is_comptime) {
   7950             if (try sema.resolveValue(arg_ref)) |arg_val| {
   7951                 comptime_args[arg_index] = arg_val.toIntern();
   7952                 child_sema.inst_map.putAssumeCapacityNoClobber(
   7953                     param_inst,
   7954                     Air.internedToRef(arg_val.toIntern()),
   7955                 );
   7956             } else switch (param_tag) {
   7957                 .param_comptime,
   7958                 .param_anytype_comptime,
   7959                 => return sema.failWithOwnedErrorMsg(block, msg: {
   7960                     const arg_src = args_info.argSrc(block, arg_index);
   7961                     const msg = try sema.errMsg(block, arg_src, "runtime-known argument passed to comptime parameter", .{});
   7962                     errdefer msg.destroy(sema.gpa);
   7963                     const param_src = switch (param_tag) {
   7964                         .param_comptime => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok.src(),
   7965                         .param_anytype_comptime => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.src(),
   7966                         else => unreachable,
   7967                     };
   7968                     try child_sema.errNote(&child_block, param_src, msg, "declared comptime here", .{});
   7969                     break :msg msg;
   7970                 }),
   7971 
   7972                 .param,
   7973                 .param_anytype,
   7974                 => return sema.failWithOwnedErrorMsg(block, msg: {
   7975                     const arg_src = args_info.argSrc(block, arg_index);
   7976                     const msg = try sema.errMsg(block, arg_src, "runtime-known argument passed to parameter of comptime-only type", .{});
   7977                     errdefer msg.destroy(sema.gpa);
   7978                     const param_src = switch (param_tag) {
   7979                         .param => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok.src(),
   7980                         .param_anytype => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.src(),
   7981                         else => unreachable,
   7982                     };
   7983                     try child_sema.errNote(&child_block, param_src, msg, "declared here", .{});
   7984                     const src_decl = mod.declPtr(block.src_decl);
   7985                     try sema.explainWhyTypeIsComptime(msg, arg_src.toSrcLoc(src_decl, mod), arg_ty);
   7986                     break :msg msg;
   7987                 }),
   7988 
   7989                 else => unreachable,
   7990             }
   7991         } else {
   7992             // The parameter is runtime-known.
   7993             try sema.queueFullTypeResolution(arg_ty);
   7994             child_sema.inst_map.putAssumeCapacityNoClobber(param_inst, try child_block.addInst(.{
   7995                 .tag = .arg,
   7996                 .data = .{ .arg = .{
   7997                     .ty = Air.internedToRef(arg_ty.toIntern()),
   7998                     .src_index = @intCast(arg_index),
   7999                 } },
   8000             }));
   8001             const param_name: Zir.NullTerminatedString = switch (param_tag) {
   8002                 .param_anytype => @enumFromInt(fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.start),
   8003                 .param => name: {
   8004                     const inst_data = fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok;
   8005                     const extra = fn_zir.extraData(Zir.Inst.Param, inst_data.payload_index);
   8006                     break :name @enumFromInt(extra.data.name);
   8007                 },
   8008                 else => unreachable,
   8009             };
   8010             try child_block.params.append(sema.arena, .{
   8011                 .ty = arg_ty.toIntern(), // This is the type after coercion
   8012                 .is_comptime = false, // We're adding only runtime args to the instantiation
   8013                 .name = param_name,
   8014             });
   8015             runtime_args.appendAssumeCapacity(arg_ref);
   8016         }
   8017     }
   8018 
   8019     // We've already handled parameters, so don't resolve the whole body. Instead, just
   8020     // do the instructions after the params (i.e. the func itself).
   8021     const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body[args_info.count()..], fn_info.param_body_inst);
   8022     const callee_index = (child_sema.resolveConstDefinedValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable).toIntern();
   8023 
   8024     const callee = mod.funcInfo(callee_index);
   8025     callee.branchQuota(ip).* = @max(callee.branchQuota(ip).*, sema.branch_quota);
   8026 
   8027     try sema.addReferencedBy(block, call_src, callee.owner_decl);
   8028 
   8029     // Make a runtime call to the new function, making sure to omit the comptime args.
   8030     const func_ty = Type.fromInterned(callee.ty);
   8031     const func_ty_info = mod.typeToFunc(func_ty).?;
   8032 
   8033     // If the call evaluated to a return type that requires comptime, never mind
   8034     // our generic instantiation. Instead we need to perform a comptime call.
   8035     if (try sema.typeRequiresComptime(Type.fromInterned(func_ty_info.return_type))) {
   8036         return error.ComptimeReturn;
   8037     }
   8038     // Similarly, if the call evaluated to a generic type we need to instead
   8039     // call it inline.
   8040     if (func_ty_info.is_generic or func_ty_info.cc == .Inline) {
   8041         return error.GenericPoison;
   8042     }
   8043 
   8044     try sema.queueFullTypeResolution(Type.fromInterned(func_ty_info.return_type));
   8045 
   8046     if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
   8047 
   8048     if (sema.owner_func_index != .none and
   8049         Type.fromInterned(func_ty_info.return_type).isError(mod))
   8050     {
   8051         ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn = true;
   8052     }
   8053 
   8054     try mod.ensureFuncBodyAnalysisQueued(callee_index);
   8055 
   8056     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + runtime_args.items.len);
   8057     const result = try block.addInst(.{
   8058         .tag = call_tag,
   8059         .data = .{ .pl_op = .{
   8060             .operand = Air.internedToRef(callee_index),
   8061             .payload = sema.addExtraAssumeCapacity(Air.Call{
   8062                 .args_len = @intCast(runtime_args.items.len),
   8063             }),
   8064         } },
   8065     });
   8066     sema.appendRefsAssumeCapacity(runtime_args.items);
   8067 
   8068     if (ensure_result_used) {
   8069         try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
   8070     }
   8071     if (call_tag == .call_always_tail) {
   8072         return sema.handleTailCall(block, call_src, func_ty, result);
   8073     }
   8074     if (func_ty.fnReturnType(mod).isNoReturn(mod)) {
   8075         _ = try block.addNoOp(.unreach);
   8076         return .unreachable_value;
   8077     }
   8078     return result;
   8079 }
   8080 
   8081 fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
   8082     const mod = sema.mod;
   8083     const ip = &mod.intern_pool;
   8084     const tuple = switch (ip.indexToKey(ty.toIntern())) {
   8085         .anon_struct_type => |tuple| tuple,
   8086         else => return,
   8087     };
   8088     for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, field_val| {
   8089         try sema.resolveTupleLazyValues(block, src, Type.fromInterned(field_ty));
   8090         if (field_val == .none) continue;
   8091         // TODO: mutate in intern pool
   8092         _ = try sema.resolveLazyValue(Value.fromInterned(field_val));
   8093     }
   8094 }
   8095 
   8096 fn emitDbgInline(
   8097     block: *Block,
   8098     old_func: InternPool.Index,
   8099     new_func: InternPool.Index,
   8100     new_func_ty: Type,
   8101     tag: Air.Inst.Tag,
   8102 ) CompileError!void {
   8103     if (block.ownerModule().strip) return;
   8104 
   8105     // Recursive inline call; no dbg_inline needed.
   8106     if (old_func == new_func) return;
   8107 
   8108     _ = try block.addInst(.{
   8109         .tag = tag,
   8110         .data = .{ .ty_fn = .{
   8111             .ty = Air.internedToRef(new_func_ty.toIntern()),
   8112             .func = new_func,
   8113         } },
   8114     });
   8115 }
   8116 
   8117 fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8118     const mod = sema.mod;
   8119     const int_type = sema.code.instructions.items(.data)[@intFromEnum(inst)].int_type;
   8120     const ty = try mod.intType(int_type.signedness, int_type.bit_count);
   8121     return Air.internedToRef(ty.toIntern());
   8122 }
   8123 
   8124 fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8125     const tracy = trace(@src());
   8126     defer tracy.end();
   8127 
   8128     const mod = sema.mod;
   8129     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8130     const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
   8131     const child_type = try sema.resolveType(block, operand_src, inst_data.operand);
   8132     if (child_type.zigTypeTag(mod) == .Opaque) {
   8133         return sema.fail(block, operand_src, "opaque type '{}' cannot be optional", .{child_type.fmt(mod)});
   8134     } else if (child_type.zigTypeTag(mod) == .Null) {
   8135         return sema.fail(block, operand_src, "type '{}' cannot be optional", .{child_type.fmt(mod)});
   8136     }
   8137     const opt_type = try mod.optionalType(child_type.toIntern());
   8138 
   8139     return Air.internedToRef(opt_type.toIntern());
   8140 }
   8141 
   8142 fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8143     const mod = sema.mod;
   8144     const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
   8145     const maybe_wrapped_indexable_ty = sema.resolveType(block, .unneeded, bin.lhs) catch |err| switch (err) {
   8146         // Since this is a ZIR instruction that returns a type, encountering
   8147         // generic poison should not result in a failed compilation, but the
   8148         // generic poison type. This prevents unnecessary failures when
   8149         // constructing types at compile-time.
   8150         error.GenericPoison => return .generic_poison_type,
   8151         else => |e| return e,
   8152     };
   8153     const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(mod);
   8154     try sema.resolveTypeFields(indexable_ty);
   8155     assert(indexable_ty.isIndexable(mod)); // validated by a previous instruction
   8156     if (indexable_ty.zigTypeTag(mod) == .Struct) {
   8157         const elem_type = indexable_ty.structFieldType(@intFromEnum(bin.rhs), mod);
   8158         return Air.internedToRef(elem_type.toIntern());
   8159     } else {
   8160         const elem_type = indexable_ty.elemType2(mod);
   8161         return Air.internedToRef(elem_type.toIntern());
   8162     }
   8163 }
   8164 
   8165 fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8166     const mod = sema.mod;
   8167     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8168     const maybe_wrapped_ptr_ty = sema.resolveType(block, .unneeded, un_node.operand) catch |err| switch (err) {
   8169         error.GenericPoison => return .generic_poison_type,
   8170         else => |e| return e,
   8171     };
   8172     const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(mod);
   8173     assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction
   8174     const elem_ty = ptr_ty.childType(mod);
   8175     if (elem_ty.toIntern() == .anyopaque_type) {
   8176         // The pointer's actual child type is effectively unknown, so it makes
   8177         // sense to represent it with a generic poison.
   8178         return .generic_poison_type;
   8179     }
   8180     return Air.internedToRef(ptr_ty.childType(mod).toIntern());
   8181 }
   8182 
   8183 fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8184     const mod = sema.mod;
   8185     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8186     const src = un_node.src();
   8187     const ptr_ty = sema.resolveType(block, src, un_node.operand) catch |err| switch (err) {
   8188         error.GenericPoison => return .generic_poison_type,
   8189         else => |e| return e,
   8190     };
   8191     try sema.checkMemOperand(block, src, ptr_ty);
   8192     const elem_ty = switch (ptr_ty.ptrSize(mod)) {
   8193         .Slice, .Many, .C => ptr_ty.childType(mod),
   8194         .One => ptr_ty.childType(mod).childType(mod),
   8195     };
   8196     return Air.internedToRef(elem_ty.toIntern());
   8197 }
   8198 
   8199 fn zirVectorElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8200     const mod = sema.mod;
   8201     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8202     const vec_ty = sema.resolveType(block, .unneeded, un_node.operand) catch |err| switch (err) {
   8203         // Since this is a ZIR instruction that returns a type, encountering
   8204         // generic poison should not result in a failed compilation, but the
   8205         // generic poison type. This prevents unnecessary failures when
   8206         // constructing types at compile-time.
   8207         error.GenericPoison => return .generic_poison_type,
   8208         else => |e| return e,
   8209     };
   8210     if (!vec_ty.isVector(mod)) {
   8211         return sema.fail(block, un_node.src(), "expected vector type, found '{}'", .{vec_ty.fmt(mod)});
   8212     }
   8213     return Air.internedToRef(vec_ty.childType(mod).toIntern());
   8214 }
   8215 
   8216 fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8217     const mod = sema.mod;
   8218     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8219     const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   8220     const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
   8221     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8222     const len: u32 = @intCast(try sema.resolveInt(block, len_src, extra.lhs, Type.u32, .{
   8223         .needed_comptime_reason = "vector length must be comptime-known",
   8224     }));
   8225     const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs);
   8226     try sema.checkVectorElemType(block, elem_type_src, elem_type);
   8227     const vector_type = try mod.vectorType(.{
   8228         .len = len,
   8229         .child = elem_type.toIntern(),
   8230     });
   8231     return Air.internedToRef(vector_type.toIntern());
   8232 }
   8233 
   8234 fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8235     const tracy = trace(@src());
   8236     defer tracy.end();
   8237 
   8238     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8239     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8240     const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node };
   8241     const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node };
   8242     const len = try sema.resolveInt(block, len_src, extra.lhs, Type.usize, .{
   8243         .needed_comptime_reason = "array length must be comptime-known",
   8244     });
   8245     const elem_type = try sema.resolveType(block, elem_src, extra.rhs);
   8246     try sema.validateArrayElemType(block, elem_type, elem_src);
   8247     const array_ty = try sema.mod.arrayType(.{
   8248         .len = len,
   8249         .child = elem_type.toIntern(),
   8250     });
   8251 
   8252     return Air.internedToRef(array_ty.toIntern());
   8253 }
   8254 
   8255 fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8256     const tracy = trace(@src());
   8257     defer tracy.end();
   8258 
   8259     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8260     const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data;
   8261     const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node };
   8262     const sentinel_src: LazySrcLoc = .{ .node_offset_array_type_sentinel = inst_data.src_node };
   8263     const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node };
   8264     const len = try sema.resolveInt(block, len_src, extra.len, Type.usize, .{
   8265         .needed_comptime_reason = "array length must be comptime-known",
   8266     });
   8267     const elem_type = try sema.resolveType(block, elem_src, extra.elem_type);
   8268     try sema.validateArrayElemType(block, elem_type, elem_src);
   8269     const uncasted_sentinel = try sema.resolveInst(extra.sentinel);
   8270     const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src);
   8271     const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel, .{
   8272         .needed_comptime_reason = "array sentinel value must be comptime-known",
   8273     });
   8274     const array_ty = try sema.mod.arrayType(.{
   8275         .len = len,
   8276         .sentinel = sentinel_val.toIntern(),
   8277         .child = elem_type.toIntern(),
   8278     });
   8279 
   8280     return Air.internedToRef(array_ty.toIntern());
   8281 }
   8282 
   8283 fn validateArrayElemType(sema: *Sema, block: *Block, elem_type: Type, elem_src: LazySrcLoc) !void {
   8284     const mod = sema.mod;
   8285     if (elem_type.zigTypeTag(mod) == .Opaque) {
   8286         return sema.fail(block, elem_src, "array of opaque type '{}' not allowed", .{elem_type.fmt(mod)});
   8287     } else if (elem_type.zigTypeTag(mod) == .NoReturn) {
   8288         return sema.fail(block, elem_src, "array of 'noreturn' not allowed", .{});
   8289     }
   8290 }
   8291 
   8292 fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8293     const tracy = trace(@src());
   8294     defer tracy.end();
   8295 
   8296     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8297     if (true) {
   8298         return sema.failWithUseOfAsync(block, inst_data.src());
   8299     }
   8300     const mod = sema.mod;
   8301     const operand_src: LazySrcLoc = .{ .node_offset_anyframe_type = inst_data.src_node };
   8302     const return_type = try sema.resolveType(block, operand_src, inst_data.operand);
   8303     const anyframe_type = try mod.anyframeType(return_type);
   8304 
   8305     return Air.internedToRef(anyframe_type.toIntern());
   8306 }
   8307 
   8308 fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8309     const tracy = trace(@src());
   8310     defer tracy.end();
   8311 
   8312     const mod = sema.mod;
   8313     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8314     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8315     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
   8316     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
   8317     const error_set = try sema.resolveType(block, lhs_src, extra.lhs);
   8318     const payload = try sema.resolveType(block, rhs_src, extra.rhs);
   8319 
   8320     if (error_set.zigTypeTag(mod) != .ErrorSet) {
   8321         return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{
   8322             error_set.fmt(mod),
   8323         });
   8324     }
   8325     try sema.validateErrorUnionPayloadType(block, payload, rhs_src);
   8326     const err_union_ty = try mod.errorUnionType(error_set, payload);
   8327     return Air.internedToRef(err_union_ty.toIntern());
   8328 }
   8329 
   8330 fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void {
   8331     const mod = sema.mod;
   8332     if (payload_ty.zigTypeTag(mod) == .Opaque) {
   8333         return sema.fail(block, payload_src, "error union with payload of opaque type '{}' not allowed", .{
   8334             payload_ty.fmt(mod),
   8335         });
   8336     } else if (payload_ty.zigTypeTag(mod) == .ErrorSet) {
   8337         return sema.fail(block, payload_src, "error union with payload of error set type '{}' not allowed", .{
   8338             payload_ty.fmt(mod),
   8339         });
   8340     }
   8341 }
   8342 
   8343 fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8344     _ = block;
   8345     const mod = sema.mod;
   8346     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   8347     const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
   8348     _ = try mod.getErrorValue(name);
   8349     // Create an error set type with only this error value, and return the value.
   8350     const error_set_type = try mod.singleErrorSetType(name);
   8351     return Air.internedToRef((try mod.intern(.{ .err = .{
   8352         .ty = error_set_type.toIntern(),
   8353         .name = name,
   8354     } })));
   8355 }
   8356 
   8357 fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
   8358     const tracy = trace(@src());
   8359     defer tracy.end();
   8360 
   8361     const mod = sema.mod;
   8362     const ip = &mod.intern_pool;
   8363     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   8364     const src = LazySrcLoc.nodeOffset(extra.node);
   8365     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   8366     const uncasted_operand = try sema.resolveInst(extra.operand);
   8367     const operand = try sema.coerce(block, Type.anyerror, uncasted_operand, operand_src);
   8368     const err_int_ty = try mod.errorIntType();
   8369 
   8370     if (try sema.resolveValue(operand)) |val| {
   8371         if (val.isUndef(mod)) {
   8372             return mod.undefRef(err_int_ty);
   8373         }
   8374         const err_name = ip.indexToKey(val.toIntern()).err.name;
   8375         return Air.internedToRef((try mod.intValue(
   8376             err_int_ty,
   8377             try mod.getErrorValue(err_name),
   8378         )).toIntern());
   8379     }
   8380 
   8381     const op_ty = sema.typeOf(uncasted_operand);
   8382     switch (try sema.resolveInferredErrorSetTy(block, src, op_ty.toIntern())) {
   8383         .anyerror_type => {},
   8384         else => |err_set_ty_index| {
   8385             const names = ip.indexToKey(err_set_ty_index).error_set_type.names;
   8386             switch (names.len) {
   8387                 0 => return Air.internedToRef((try mod.intValue(err_int_ty, 0)).toIntern()),
   8388                 1 => {
   8389                     const int: Module.ErrorInt = @intCast(mod.global_error_set.getIndex(names.get(ip)[0]).?);
   8390                     return mod.intRef(err_int_ty, int);
   8391                 },
   8392                 else => {},
   8393             }
   8394         },
   8395     }
   8396 
   8397     try sema.requireRuntimeBlock(block, src, operand_src);
   8398     return block.addBitCast(err_int_ty, operand);
   8399 }
   8400 
   8401 fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
   8402     const tracy = trace(@src());
   8403     defer tracy.end();
   8404 
   8405     const mod = sema.mod;
   8406     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   8407     const src = LazySrcLoc.nodeOffset(extra.node);
   8408     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   8409     const uncasted_operand = try sema.resolveInst(extra.operand);
   8410     const err_int_ty = try mod.errorIntType();
   8411     const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src);
   8412 
   8413     if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| {
   8414         const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(mod));
   8415         if (int > mod.global_error_set.count() or int == 0)
   8416             return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int});
   8417         return Air.internedToRef((try mod.intern(.{ .err = .{
   8418             .ty = .anyerror_type,
   8419             .name = mod.global_error_set.keys()[int],
   8420         } })));
   8421     }
   8422     try sema.requireRuntimeBlock(block, src, operand_src);
   8423     if (block.wantSafety()) {
   8424         const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand);
   8425         const zero_val = Air.internedToRef((try mod.intValue(err_int_ty, 0)).toIntern());
   8426         const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val);
   8427         const ok = try block.addBinOp(.bool_and, is_lt_len, is_non_zero);
   8428         try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
   8429     }
   8430     return block.addInst(.{
   8431         .tag = .bitcast,
   8432         .data = .{ .ty_op = .{
   8433             .ty = .anyerror_type,
   8434             .operand = operand,
   8435         } },
   8436     });
   8437 }
   8438 
   8439 fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8440     const tracy = trace(@src());
   8441     defer tracy.end();
   8442 
   8443     const mod = sema.mod;
   8444     const ip = &mod.intern_pool;
   8445     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8446     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8447     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
   8448     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
   8449     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
   8450     const lhs = try sema.resolveInst(extra.lhs);
   8451     const rhs = try sema.resolveInst(extra.rhs);
   8452     if (sema.typeOf(lhs).zigTypeTag(mod) == .Bool and sema.typeOf(rhs).zigTypeTag(mod) == .Bool) {
   8453         const msg = msg: {
   8454             const msg = try sema.errMsg(block, lhs_src, "expected error set type, found 'bool'", .{});
   8455             errdefer msg.destroy(sema.gpa);
   8456             try sema.errNote(block, src, msg, "'||' merges error sets; 'or' performs boolean OR", .{});
   8457             break :msg msg;
   8458         };
   8459         return sema.failWithOwnedErrorMsg(block, msg);
   8460     }
   8461     const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs);
   8462     const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs);
   8463     if (lhs_ty.zigTypeTag(mod) != .ErrorSet)
   8464         return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{lhs_ty.fmt(mod)});
   8465     if (rhs_ty.zigTypeTag(mod) != .ErrorSet)
   8466         return sema.fail(block, rhs_src, "expected error set type, found '{}'", .{rhs_ty.fmt(mod)});
   8467 
   8468     // Anything merged with anyerror is anyerror.
   8469     if (lhs_ty.toIntern() == .anyerror_type or rhs_ty.toIntern() == .anyerror_type) {
   8470         return .anyerror_type;
   8471     }
   8472 
   8473     if (ip.isInferredErrorSetType(lhs_ty.toIntern())) {
   8474         switch (try sema.resolveInferredErrorSet(block, src, lhs_ty.toIntern())) {
   8475             // isAnyError might have changed from a false negative to a true
   8476             // positive after resolution.
   8477             .anyerror_type => return .anyerror_type,
   8478             else => {},
   8479         }
   8480     }
   8481     if (ip.isInferredErrorSetType(rhs_ty.toIntern())) {
   8482         switch (try sema.resolveInferredErrorSet(block, src, rhs_ty.toIntern())) {
   8483             // isAnyError might have changed from a false negative to a true
   8484             // positive after resolution.
   8485             .anyerror_type => return .anyerror_type,
   8486             else => {},
   8487         }
   8488     }
   8489 
   8490     const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty);
   8491     return Air.internedToRef(err_set_ty.toIntern());
   8492 }
   8493 
   8494 fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8495     _ = block;
   8496     const tracy = trace(@src());
   8497     defer tracy.end();
   8498 
   8499     const mod = sema.mod;
   8500     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   8501     const name = inst_data.get(sema.code);
   8502     return Air.internedToRef((try mod.intern(.{
   8503         .enum_literal = try mod.intern_pool.getOrPutString(sema.gpa, name),
   8504     })));
   8505 }
   8506 
   8507 fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8508     const mod = sema.mod;
   8509     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8510     const src = inst_data.src();
   8511     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   8512     const operand = try sema.resolveInst(inst_data.operand);
   8513     const operand_ty = sema.typeOf(operand);
   8514 
   8515     const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(mod)) {
   8516         .Enum => operand,
   8517         .Union => blk: {
   8518             try sema.resolveTypeFields(operand_ty);
   8519             const tag_ty = operand_ty.unionTagType(mod) orelse {
   8520                 return sema.fail(
   8521                     block,
   8522                     operand_src,
   8523                     "untagged union '{}' cannot be converted to integer",
   8524                     .{src},
   8525                 );
   8526             };
   8527             break :blk try sema.unionToTag(block, tag_ty, operand, operand_src);
   8528         },
   8529         else => {
   8530             return sema.fail(block, operand_src, "expected enum or tagged union, found '{}'", .{
   8531                 operand_ty.fmt(mod),
   8532             });
   8533         },
   8534     };
   8535     const enum_tag_ty = sema.typeOf(enum_tag);
   8536 
   8537     const int_tag_ty = enum_tag_ty.intTagType(mod);
   8538 
   8539     if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| {
   8540         return Air.internedToRef((try mod.getCoerced(opv, int_tag_ty)).toIntern());
   8541     }
   8542 
   8543     if (try sema.resolveValue(enum_tag)) |enum_tag_val| {
   8544         const val = try enum_tag_val.intFromEnum(enum_tag_ty, mod);
   8545         return Air.internedToRef(val.toIntern());
   8546     }
   8547 
   8548     try sema.requireRuntimeBlock(block, src, operand_src);
   8549     return block.addBitCast(int_tag_ty, enum_tag);
   8550 }
   8551 
   8552 fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8553     const mod = sema.mod;
   8554     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8555     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8556     const src = inst_data.src();
   8557     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   8558     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@enumFromInt");
   8559     const operand = try sema.resolveInst(extra.rhs);
   8560 
   8561     if (dest_ty.zigTypeTag(mod) != .Enum) {
   8562         return sema.fail(block, src, "expected enum, found '{}'", .{dest_ty.fmt(mod)});
   8563     }
   8564     _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
   8565 
   8566     if (try sema.resolveValue(operand)) |int_val| {
   8567         if (dest_ty.isNonexhaustiveEnum(mod)) {
   8568             const int_tag_ty = dest_ty.intTagType(mod);
   8569             if (try sema.intFitsInType(int_val, int_tag_ty, null)) {
   8570                 return Air.internedToRef((try mod.getCoerced(int_val, dest_ty)).toIntern());
   8571             }
   8572             const msg = msg: {
   8573                 const msg = try sema.errMsg(
   8574                     block,
   8575                     src,
   8576                     "int value '{}' out of range of non-exhaustive enum '{}'",
   8577                     .{ int_val.fmtValue(sema.typeOf(operand), mod), dest_ty.fmt(mod) },
   8578                 );
   8579                 errdefer msg.destroy(sema.gpa);
   8580                 try sema.addDeclaredHereNote(msg, dest_ty);
   8581                 break :msg msg;
   8582             };
   8583             return sema.failWithOwnedErrorMsg(block, msg);
   8584         }
   8585         if (int_val.isUndef(mod)) {
   8586             return sema.failWithUseOfUndef(block, operand_src);
   8587         }
   8588         if (!(try sema.enumHasInt(dest_ty, int_val))) {
   8589             const msg = msg: {
   8590                 const msg = try sema.errMsg(
   8591                     block,
   8592                     src,
   8593                     "enum '{}' has no tag with value '{}'",
   8594                     .{ dest_ty.fmt(mod), int_val.fmtValue(sema.typeOf(operand), mod) },
   8595                 );
   8596                 errdefer msg.destroy(sema.gpa);
   8597                 try sema.addDeclaredHereNote(msg, dest_ty);
   8598                 break :msg msg;
   8599             };
   8600             return sema.failWithOwnedErrorMsg(block, msg);
   8601         }
   8602         return Air.internedToRef((try mod.getCoerced(int_val, dest_ty)).toIntern());
   8603     }
   8604 
   8605     if (try sema.typeHasOnePossibleValue(dest_ty)) |opv| {
   8606         const result = Air.internedToRef(opv.toIntern());
   8607         // The operand is runtime-known but the result is comptime-known. In
   8608         // this case we still need a safety check.
   8609         // TODO add a safety check here. we can't use is_named_enum_value -
   8610         // it needs to convert the enum back to int and make sure it equals the operand int.
   8611         return result;
   8612     }
   8613 
   8614     try sema.requireRuntimeBlock(block, src, operand_src);
   8615     const result = try block.addTyOp(.intcast, dest_ty, operand);
   8616     if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum(mod) and
   8617         mod.backendSupportsFeature(.is_named_enum_value))
   8618     {
   8619         const ok = try block.addUnOp(.is_named_enum_value, result);
   8620         try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
   8621     }
   8622     return result;
   8623 }
   8624 
   8625 /// Pointer in, pointer out.
   8626 fn zirOptionalPayloadPtr(
   8627     sema: *Sema,
   8628     block: *Block,
   8629     inst: Zir.Inst.Index,
   8630     safety_check: bool,
   8631 ) CompileError!Air.Inst.Ref {
   8632     const tracy = trace(@src());
   8633     defer tracy.end();
   8634 
   8635     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8636     const optional_ptr = try sema.resolveInst(inst_data.operand);
   8637     const src = inst_data.src();
   8638 
   8639     return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false);
   8640 }
   8641 
   8642 fn analyzeOptionalPayloadPtr(
   8643     sema: *Sema,
   8644     block: *Block,
   8645     src: LazySrcLoc,
   8646     optional_ptr: Air.Inst.Ref,
   8647     safety_check: bool,
   8648     initializing: bool,
   8649 ) CompileError!Air.Inst.Ref {
   8650     const mod = sema.mod;
   8651     const optional_ptr_ty = sema.typeOf(optional_ptr);
   8652     assert(optional_ptr_ty.zigTypeTag(mod) == .Pointer);
   8653 
   8654     const opt_type = optional_ptr_ty.childType(mod);
   8655     if (opt_type.zigTypeTag(mod) != .Optional) {
   8656         return sema.fail(block, src, "expected optional type, found '{}'", .{opt_type.fmt(mod)});
   8657     }
   8658 
   8659     const child_type = opt_type.optionalChild(mod);
   8660     const child_pointer = try sema.ptrType(.{
   8661         .child = child_type.toIntern(),
   8662         .flags = .{
   8663             .is_const = optional_ptr_ty.isConstPtr(mod),
   8664             .address_space = optional_ptr_ty.ptrAddressSpace(mod),
   8665         },
   8666     });
   8667 
   8668     if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| {
   8669         if (initializing) {
   8670             if (!ptr_val.isComptimeMutablePtr(mod)) {
   8671                 // If the pointer resulting from this function was stored at comptime,
   8672                 // the optional non-null bit would be set that way. But in this case,
   8673                 // we need to emit a runtime instruction to do it.
   8674                 const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
   8675                 try sema.checkKnownAllocPtr(optional_ptr, opt_payload_ptr);
   8676             }
   8677             return Air.internedToRef((try mod.intern(.{ .ptr = .{
   8678                 .ty = child_pointer.toIntern(),
   8679                 .addr = .{ .opt_payload = ptr_val.toIntern() },
   8680             } })));
   8681         }
   8682         if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| {
   8683             if (val.isNull(mod)) {
   8684                 return sema.fail(block, src, "unable to unwrap null", .{});
   8685             }
   8686             // The same Value represents the pointer to the optional and the payload.
   8687             return Air.internedToRef((try mod.intern(.{ .ptr = .{
   8688                 .ty = child_pointer.toIntern(),
   8689                 .addr = .{ .opt_payload = ptr_val.toIntern() },
   8690             } })));
   8691         }
   8692     }
   8693 
   8694     try sema.requireRuntimeBlock(block, src, null);
   8695     if (safety_check and block.wantSafety()) {
   8696         const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);
   8697         try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
   8698     }
   8699 
   8700     if (initializing) {
   8701         const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
   8702         try sema.checkKnownAllocPtr(optional_ptr, opt_payload_ptr);
   8703         return opt_payload_ptr;
   8704     } else {
   8705         return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr);
   8706     }
   8707 }
   8708 
   8709 /// Value in, value out.
   8710 fn zirOptionalPayload(
   8711     sema: *Sema,
   8712     block: *Block,
   8713     inst: Zir.Inst.Index,
   8714     safety_check: bool,
   8715 ) CompileError!Air.Inst.Ref {
   8716     const tracy = trace(@src());
   8717     defer tracy.end();
   8718 
   8719     const mod = sema.mod;
   8720     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8721     const src = inst_data.src();
   8722     const operand = try sema.resolveInst(inst_data.operand);
   8723     const operand_ty = sema.typeOf(operand);
   8724     const result_ty = switch (operand_ty.zigTypeTag(mod)) {
   8725         .Optional => operand_ty.optionalChild(mod),
   8726         .Pointer => t: {
   8727             if (operand_ty.ptrSize(mod) != .C) {
   8728                 return sema.failWithExpectedOptionalType(block, src, operand_ty);
   8729             }
   8730             // TODO https://github.com/ziglang/zig/issues/6597
   8731             if (true) break :t operand_ty;
   8732             const ptr_info = operand_ty.ptrInfo(mod);
   8733             break :t try sema.ptrType(.{
   8734                 .child = ptr_info.child,
   8735                 .flags = .{
   8736                     .alignment = ptr_info.flags.alignment,
   8737                     .is_const = ptr_info.flags.is_const,
   8738                     .is_volatile = ptr_info.flags.is_volatile,
   8739                     .is_allowzero = ptr_info.flags.is_allowzero,
   8740                     .address_space = ptr_info.flags.address_space,
   8741                 },
   8742             });
   8743         },
   8744         else => return sema.failWithExpectedOptionalType(block, src, operand_ty),
   8745     };
   8746 
   8747     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
   8748         return if (val.optionalValue(mod)) |payload|
   8749             Air.internedToRef(payload.toIntern())
   8750         else
   8751             sema.fail(block, src, "unable to unwrap null", .{});
   8752     }
   8753 
   8754     try sema.requireRuntimeBlock(block, src, null);
   8755     if (safety_check and block.wantSafety()) {
   8756         const is_non_null = try block.addUnOp(.is_non_null, operand);
   8757         try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
   8758     }
   8759     return block.addTyOp(.optional_payload, result_ty, operand);
   8760 }
   8761 
   8762 /// Value in, value out
   8763 fn zirErrUnionPayload(
   8764     sema: *Sema,
   8765     block: *Block,
   8766     inst: Zir.Inst.Index,
   8767 ) CompileError!Air.Inst.Ref {
   8768     const tracy = trace(@src());
   8769     defer tracy.end();
   8770 
   8771     const mod = sema.mod;
   8772     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8773     const src = inst_data.src();
   8774     const operand = try sema.resolveInst(inst_data.operand);
   8775     const operand_src = src;
   8776     const err_union_ty = sema.typeOf(operand);
   8777     if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
   8778         return sema.fail(block, operand_src, "expected error union type, found '{}'", .{
   8779             err_union_ty.fmt(mod),
   8780         });
   8781     }
   8782     return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false);
   8783 }
   8784 
   8785 fn analyzeErrUnionPayload(
   8786     sema: *Sema,
   8787     block: *Block,
   8788     src: LazySrcLoc,
   8789     err_union_ty: Type,
   8790     operand: Air.Inst.Ref,
   8791     operand_src: LazySrcLoc,
   8792     safety_check: bool,
   8793 ) CompileError!Air.Inst.Ref {
   8794     const mod = sema.mod;
   8795     const payload_ty = err_union_ty.errorUnionPayload(mod);
   8796     if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
   8797         if (val.getErrorName(mod).unwrap()) |name| {
   8798             return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&mod.intern_pool)});
   8799         }
   8800         return Air.internedToRef(mod.intern_pool.indexToKey(val.toIntern()).error_union.val.payload);
   8801     }
   8802 
   8803     try sema.requireRuntimeBlock(block, src, null);
   8804 
   8805     // If the error set has no fields then no safety check is needed.
   8806     if (safety_check and block.wantSafety() and
   8807         !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod))
   8808     {
   8809         try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
   8810     }
   8811 
   8812     return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
   8813 }
   8814 
   8815 /// Pointer in, pointer out.
   8816 fn zirErrUnionPayloadPtr(
   8817     sema: *Sema,
   8818     block: *Block,
   8819     inst: Zir.Inst.Index,
   8820 ) CompileError!Air.Inst.Ref {
   8821     const tracy = trace(@src());
   8822     defer tracy.end();
   8823 
   8824     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8825     const operand = try sema.resolveInst(inst_data.operand);
   8826     const src = inst_data.src();
   8827 
   8828     return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
   8829 }
   8830 
   8831 fn analyzeErrUnionPayloadPtr(
   8832     sema: *Sema,
   8833     block: *Block,
   8834     src: LazySrcLoc,
   8835     operand: Air.Inst.Ref,
   8836     safety_check: bool,
   8837     initializing: bool,
   8838 ) CompileError!Air.Inst.Ref {
   8839     const mod = sema.mod;
   8840     const operand_ty = sema.typeOf(operand);
   8841     assert(operand_ty.zigTypeTag(mod) == .Pointer);
   8842 
   8843     if (operand_ty.childType(mod).zigTypeTag(mod) != .ErrorUnion) {
   8844         return sema.fail(block, src, "expected error union type, found '{}'", .{
   8845             operand_ty.childType(mod).fmt(mod),
   8846         });
   8847     }
   8848 
   8849     const err_union_ty = operand_ty.childType(mod);
   8850     const payload_ty = err_union_ty.errorUnionPayload(mod);
   8851     const operand_pointer_ty = try sema.ptrType(.{
   8852         .child = payload_ty.toIntern(),
   8853         .flags = .{
   8854             .is_const = operand_ty.isConstPtr(mod),
   8855             .address_space = operand_ty.ptrAddressSpace(mod),
   8856         },
   8857     });
   8858 
   8859     if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| {
   8860         if (initializing) {
   8861             if (!ptr_val.isComptimeMutablePtr(mod)) {
   8862                 // If the pointer resulting from this function was stored at comptime,
   8863                 // the error union error code would be set that way. But in this case,
   8864                 // we need to emit a runtime instruction to do it.
   8865                 try sema.requireRuntimeBlock(block, src, null);
   8866                 const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
   8867                 try sema.checkKnownAllocPtr(operand, eu_payload_ptr);
   8868             }
   8869             return Air.internedToRef((try mod.intern(.{ .ptr = .{
   8870                 .ty = operand_pointer_ty.toIntern(),
   8871                 .addr = .{ .eu_payload = ptr_val.toIntern() },
   8872             } })));
   8873         }
   8874         if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| {
   8875             if (val.getErrorName(mod).unwrap()) |name| {
   8876                 return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&mod.intern_pool)});
   8877             }
   8878             return Air.internedToRef((try mod.intern(.{ .ptr = .{
   8879                 .ty = operand_pointer_ty.toIntern(),
   8880                 .addr = .{ .eu_payload = ptr_val.toIntern() },
   8881             } })));
   8882         }
   8883     }
   8884 
   8885     try sema.requireRuntimeBlock(block, src, null);
   8886 
   8887     // If the error set has no fields then no safety check is needed.
   8888     if (safety_check and block.wantSafety() and
   8889         !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod))
   8890     {
   8891         try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
   8892     }
   8893 
   8894     if (initializing) {
   8895         const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
   8896         try sema.checkKnownAllocPtr(operand, eu_payload_ptr);
   8897         return eu_payload_ptr;
   8898     } else {
   8899         return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand);
   8900     }
   8901 }
   8902 
   8903 /// Value in, value out
   8904 fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8905     const tracy = trace(@src());
   8906     defer tracy.end();
   8907 
   8908     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8909     const src = inst_data.src();
   8910     const operand = try sema.resolveInst(inst_data.operand);
   8911     return sema.analyzeErrUnionCode(block, src, operand);
   8912 }
   8913 
   8914 fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref {
   8915     const mod = sema.mod;
   8916     const operand_ty = sema.typeOf(operand);
   8917     if (operand_ty.zigTypeTag(mod) != .ErrorUnion) {
   8918         return sema.fail(block, src, "expected error union type, found '{}'", .{
   8919             operand_ty.fmt(mod),
   8920         });
   8921     }
   8922 
   8923     const result_ty = operand_ty.errorUnionSet(mod);
   8924 
   8925     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
   8926         return Air.internedToRef((try mod.intern(.{ .err = .{
   8927             .ty = result_ty.toIntern(),
   8928             .name = mod.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
   8929         } })));
   8930     }
   8931 
   8932     try sema.requireRuntimeBlock(block, src, null);
   8933     return block.addTyOp(.unwrap_errunion_err, result_ty, operand);
   8934 }
   8935 
   8936 /// Pointer in, value out
   8937 fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8938     const tracy = trace(@src());
   8939     defer tracy.end();
   8940 
   8941     const mod = sema.mod;
   8942     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8943     const src = inst_data.src();
   8944     const operand = try sema.resolveInst(inst_data.operand);
   8945     const operand_ty = sema.typeOf(operand);
   8946     assert(operand_ty.zigTypeTag(mod) == .Pointer);
   8947 
   8948     if (operand_ty.childType(mod).zigTypeTag(mod) != .ErrorUnion) {
   8949         return sema.fail(block, src, "expected error union type, found '{}'", .{
   8950             operand_ty.childType(mod).fmt(mod),
   8951         });
   8952     }
   8953 
   8954     const result_ty = operand_ty.childType(mod).errorUnionSet(mod);
   8955 
   8956     if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
   8957         if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {
   8958             assert(val.getErrorName(mod) != .none);
   8959             return Air.internedToRef(val.toIntern());
   8960         }
   8961     }
   8962 
   8963     try sema.requireRuntimeBlock(block, src, null);
   8964     return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand);
   8965 }
   8966 
   8967 fn zirFunc(
   8968     sema: *Sema,
   8969     block: *Block,
   8970     inst: Zir.Inst.Index,
   8971     inferred_error_set: bool,
   8972 ) CompileError!Air.Inst.Ref {
   8973     const mod = sema.mod;
   8974     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8975     const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
   8976     const target = sema.mod.getTarget();
   8977     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node };
   8978 
   8979     var extra_index = extra.end;
   8980 
   8981     const ret_ty: Type = switch (extra.data.ret_body_len) {
   8982         0 => Type.void,
   8983         1 => blk: {
   8984             const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   8985             extra_index += 1;
   8986             if (sema.resolveType(block, ret_ty_src, ret_ty_ref)) |ret_ty| {
   8987                 break :blk ret_ty;
   8988             } else |err| switch (err) {
   8989                 error.GenericPoison => {
   8990                     break :blk Type.generic_poison;
   8991                 },
   8992                 else => |e| return e,
   8993             }
   8994         },
   8995         else => blk: {
   8996             const ret_ty_body = sema.code.bodySlice(extra_index, extra.data.ret_body_len);
   8997             extra_index += ret_ty_body.len;
   8998 
   8999             const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type, .{
   9000                 .needed_comptime_reason = "return type must be comptime-known",
   9001             });
   9002             break :blk ret_ty_val.toType();
   9003         },
   9004     };
   9005 
   9006     var src_locs: Zir.Inst.Func.SrcLocs = undefined;
   9007     const has_body = extra.data.body_len != 0;
   9008     if (has_body) {
   9009         extra_index += extra.data.body_len;
   9010         src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
   9011     }
   9012 
   9013     // If this instruction has a body it means it's the type of the `owner_decl`
   9014     // otherwise it's a function type without a `callconv` attribute and should
   9015     // never be `.C`.
   9016     const cc: std.builtin.CallingConvention = if (has_body and mod.declPtr(block.src_decl).is_exported)
   9017         .C
   9018     else
   9019         .Unspecified;
   9020 
   9021     return sema.funcCommon(
   9022         block,
   9023         inst_data.src_node,
   9024         inst,
   9025         .none,
   9026         target_util.defaultAddressSpace(target, .function),
   9027         .default,
   9028         cc,
   9029         ret_ty,
   9030         false,
   9031         inferred_error_set,
   9032         false,
   9033         has_body,
   9034         src_locs,
   9035         null,
   9036         0,
   9037         false,
   9038     );
   9039 }
   9040 
   9041 fn resolveGenericBody(
   9042     sema: *Sema,
   9043     block: *Block,
   9044     src: LazySrcLoc,
   9045     body: []const Zir.Inst.Index,
   9046     func_inst: Zir.Inst.Index,
   9047     dest_ty: Type,
   9048     reason: NeededComptimeReason,
   9049 ) !Value {
   9050     assert(body.len != 0);
   9051 
   9052     const err = err: {
   9053         // Make sure any nested param instructions don't clobber our work.
   9054         const prev_params = block.params;
   9055         const prev_no_partial_func_type = sema.no_partial_func_ty;
   9056         const prev_generic_owner = sema.generic_owner;
   9057         const prev_generic_call_src = sema.generic_call_src;
   9058         const prev_generic_call_decl = sema.generic_call_decl;
   9059         block.params = .{};
   9060         sema.no_partial_func_ty = true;
   9061         sema.generic_owner = .none;
   9062         sema.generic_call_src = .unneeded;
   9063         sema.generic_call_decl = .none;
   9064         defer {
   9065             block.params = prev_params;
   9066             sema.no_partial_func_ty = prev_no_partial_func_type;
   9067             sema.generic_owner = prev_generic_owner;
   9068             sema.generic_call_src = prev_generic_call_src;
   9069             sema.generic_call_decl = prev_generic_call_decl;
   9070         }
   9071 
   9072         const uncasted = sema.resolveBody(block, body, func_inst) catch |err| break :err err;
   9073         const result = sema.coerce(block, dest_ty, uncasted, src) catch |err| break :err err;
   9074         const val = sema.resolveConstDefinedValue(block, src, result, reason) catch |err| break :err err;
   9075         return val;
   9076     };
   9077     switch (err) {
   9078         error.GenericPoison => {
   9079             if (dest_ty.toIntern() == .type_type) {
   9080                 return Value.generic_poison_type;
   9081             } else {
   9082                 return Value.generic_poison;
   9083             }
   9084         },
   9085         else => |e| return e,
   9086     }
   9087 }
   9088 
   9089 /// Given a library name, examines if the library name should end up in
   9090 /// `link.File.Options.system_libs` table (for example, libc is always
   9091 /// specified via dedicated flag `link_libc` instead),
   9092 /// and puts it there if it doesn't exist.
   9093 /// It also dupes the library name which can then be saved as part of the
   9094 /// respective `Decl` (either `ExternFn` or `Var`).
   9095 /// The liveness of the duped library name is tied to liveness of `Module`.
   9096 /// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`).
   9097 fn handleExternLibName(
   9098     sema: *Sema,
   9099     block: *Block,
   9100     src_loc: LazySrcLoc,
   9101     lib_name: []const u8,
   9102 ) CompileError!void {
   9103     blk: {
   9104         const mod = sema.mod;
   9105         const comp = mod.comp;
   9106         const target = mod.getTarget();
   9107         log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});
   9108         if (target.is_libc_lib_name(lib_name)) {
   9109             if (!comp.config.link_libc) {
   9110                 return sema.fail(
   9111                     block,
   9112                     src_loc,
   9113                     "dependency on libc must be explicitly specified in the build command",
   9114                     .{},
   9115                 );
   9116             }
   9117             break :blk;
   9118         }
   9119         if (target.is_libcpp_lib_name(lib_name)) {
   9120             if (!comp.config.link_libcpp) return sema.fail(
   9121                 block,
   9122                 src_loc,
   9123                 "dependency on libc++ must be explicitly specified in the build command",
   9124                 .{},
   9125             );
   9126             break :blk;
   9127         }
   9128         if (mem.eql(u8, lib_name, "unwind")) {
   9129             if (!comp.config.link_libunwind) return sema.fail(
   9130                 block,
   9131                 src_loc,
   9132                 "dependency on libunwind must be explicitly specified in the build command",
   9133                 .{},
   9134             );
   9135             break :blk;
   9136         }
   9137         if (!target.isWasm() and !block.ownerModule().pic) {
   9138             return sema.fail(
   9139                 block,
   9140                 src_loc,
   9141                 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by '-l{s}' or '-fPIC'.",
   9142                 .{ lib_name, lib_name },
   9143             );
   9144         }
   9145         comp.addLinkLib(lib_name) catch |err| {
   9146             return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{
   9147                 lib_name, @errorName(err),
   9148             });
   9149         };
   9150     }
   9151 }
   9152 
   9153 /// These are calling conventions that are confirmed to work with variadic functions.
   9154 /// Any calling conventions not included here are either not yet verified to work with variadic
   9155 /// functions or there are no more other calling conventions that support variadic functions.
   9156 const calling_conventions_supporting_var_args = [_]std.builtin.CallingConvention{
   9157     .C,
   9158 };
   9159 fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention) bool {
   9160     return for (calling_conventions_supporting_var_args) |supported_cc| {
   9161         if (cc == supported_cc) return true;
   9162     } else false;
   9163 }
   9164 fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention) CompileError!void {
   9165     const CallingConventionsSupportingVarArgsList = struct {
   9166         pub fn format(_: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
   9167             _ = fmt;
   9168             _ = options;
   9169             for (calling_conventions_supporting_var_args, 0..) |cc_inner, i| {
   9170                 if (i != 0)
   9171                     try writer.writeAll(", ");
   9172                 try writer.print("'.{s}'", .{@tagName(cc_inner)});
   9173             }
   9174         }
   9175     };
   9176 
   9177     if (!callConvSupportsVarArgs(cc)) {
   9178         const msg = msg: {
   9179             const msg = try sema.errMsg(block, src, "variadic function does not support '.{s}' calling convention", .{@tagName(cc)});
   9180             errdefer msg.destroy(sema.gpa);
   9181             try sema.errNote(block, src, msg, "supported calling conventions: {}", .{CallingConventionsSupportingVarArgsList{}});
   9182             break :msg msg;
   9183         };
   9184         return sema.failWithOwnedErrorMsg(block, msg);
   9185     }
   9186 }
   9187 
   9188 const Section = union(enum) {
   9189     generic,
   9190     default,
   9191     explicit: InternPool.NullTerminatedString,
   9192 };
   9193 
   9194 fn funcCommon(
   9195     sema: *Sema,
   9196     block: *Block,
   9197     src_node_offset: i32,
   9198     func_inst: Zir.Inst.Index,
   9199     /// null means generic poison
   9200     alignment: ?Alignment,
   9201     /// null means generic poison
   9202     address_space: ?std.builtin.AddressSpace,
   9203     section: Section,
   9204     /// null means generic poison
   9205     cc: ?std.builtin.CallingConvention,
   9206     /// this might be Type.generic_poison
   9207     bare_return_type: Type,
   9208     var_args: bool,
   9209     inferred_error_set: bool,
   9210     is_extern: bool,
   9211     has_body: bool,
   9212     src_locs: Zir.Inst.Func.SrcLocs,
   9213     opt_lib_name: ?[]const u8,
   9214     noalias_bits: u32,
   9215     is_noinline: bool,
   9216 ) CompileError!Air.Inst.Ref {
   9217     const mod = sema.mod;
   9218     const gpa = sema.gpa;
   9219     const target = mod.getTarget();
   9220     const ip = &mod.intern_pool;
   9221     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
   9222     const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset };
   9223     const func_src = LazySrcLoc.nodeOffset(src_node_offset);
   9224 
   9225     var is_generic = bare_return_type.isGenericPoison() or
   9226         alignment == null or
   9227         address_space == null or
   9228         section == .generic or
   9229         cc == null;
   9230 
   9231     if (var_args) {
   9232         if (is_generic) {
   9233             return sema.fail(block, func_src, "generic function cannot be variadic", .{});
   9234         }
   9235         try sema.checkCallConvSupportsVarArgs(block, cc_src, cc.?);
   9236     }
   9237 
   9238     const is_source_decl = sema.generic_owner == .none;
   9239 
   9240     // In the case of generic calling convention, or generic alignment, we use
   9241     // default values which are only meaningful for the generic function, *not*
   9242     // the instantiation, which can depend on comptime parameters.
   9243     // Related proposal: https://github.com/ziglang/zig/issues/11834
   9244     const cc_resolved = cc orelse .Unspecified;
   9245     var comptime_bits: u32 = 0;
   9246     for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| {
   9247         const param_ty = Type.fromInterned(param_ty_ip);
   9248         const is_noalias = blk: {
   9249             const index = std.math.cast(u5, i) orelse break :blk false;
   9250             break :blk @as(u1, @truncate(noalias_bits >> index)) != 0;
   9251         };
   9252         const param_src: LazySrcLoc = .{ .fn_proto_param = .{
   9253             .decl = block.src_decl,
   9254             .fn_proto_node_offset = src_node_offset,
   9255             .param_index = @intCast(i),
   9256         } };
   9257         const requires_comptime = try sema.typeRequiresComptime(param_ty);
   9258         if (param_is_comptime or requires_comptime) {
   9259             comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error
   9260         }
   9261         const this_generic = param_ty.isGenericPoison();
   9262         is_generic = is_generic or this_generic;
   9263         if (param_is_comptime and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved)) {
   9264             return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc_resolved)});
   9265         }
   9266         if (this_generic and !sema.no_partial_func_ty and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved)) {
   9267             return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc_resolved)});
   9268         }
   9269         if (!param_ty.isValidParamType(mod)) {
   9270             const opaque_str = if (param_ty.zigTypeTag(mod) == .Opaque) "opaque " else "";
   9271             const msg = msg: {
   9272                 const msg = try sema.errMsg(block, param_src, "parameter of {s}type '{}' not allowed", .{
   9273                     opaque_str, param_ty.fmt(mod),
   9274                 });
   9275                 errdefer msg.destroy(sema.gpa);
   9276 
   9277                 try sema.addDeclaredHereNote(msg, param_ty);
   9278                 break :msg msg;
   9279             };
   9280             return sema.failWithOwnedErrorMsg(block, msg);
   9281         }
   9282         if (!this_generic and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved) and !try sema.validateExternType(param_ty, .param_ty)) {
   9283             const msg = msg: {
   9284                 const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{
   9285                     param_ty.fmt(mod), @tagName(cc_resolved),
   9286                 });
   9287                 errdefer msg.destroy(sema.gpa);
   9288 
   9289                 const src_decl = mod.declPtr(block.src_decl);
   9290                 try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl, mod), param_ty, .param_ty);
   9291 
   9292                 try sema.addDeclaredHereNote(msg, param_ty);
   9293                 break :msg msg;
   9294             };
   9295             return sema.failWithOwnedErrorMsg(block, msg);
   9296         }
   9297         if (is_source_decl and requires_comptime and !param_is_comptime and has_body) {
   9298             const msg = msg: {
   9299                 const msg = try sema.errMsg(block, param_src, "parameter of type '{}' must be declared comptime", .{
   9300                     param_ty.fmt(mod),
   9301                 });
   9302                 errdefer msg.destroy(sema.gpa);
   9303 
   9304                 const src_decl = mod.declPtr(block.src_decl);
   9305                 try sema.explainWhyTypeIsComptime(msg, param_src.toSrcLoc(src_decl, mod), param_ty);
   9306 
   9307                 try sema.addDeclaredHereNote(msg, param_ty);
   9308                 break :msg msg;
   9309             };
   9310             return sema.failWithOwnedErrorMsg(block, msg);
   9311         }
   9312         if (is_source_decl and !this_generic and is_noalias and
   9313             !(param_ty.zigTypeTag(mod) == .Pointer or param_ty.isPtrLikeOptional(mod)))
   9314         {
   9315             return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{});
   9316         }
   9317     }
   9318 
   9319     var ret_ty_requires_comptime = false;
   9320     const ret_poison = if (sema.typeRequiresComptime(bare_return_type)) |ret_comptime| rp: {
   9321         ret_ty_requires_comptime = ret_comptime;
   9322         break :rp bare_return_type.isGenericPoison();
   9323     } else |err| switch (err) {
   9324         error.GenericPoison => rp: {
   9325             is_generic = true;
   9326             break :rp true;
   9327         },
   9328         else => |e| return e,
   9329     };
   9330     const final_is_generic = is_generic or comptime_bits != 0 or ret_ty_requires_comptime;
   9331 
   9332     const param_types = block.params.items(.ty);
   9333 
   9334     if (!is_source_decl) {
   9335         assert(has_body);
   9336         assert(!is_generic);
   9337         assert(comptime_bits == 0);
   9338         assert(cc != null);
   9339         assert(section != .generic);
   9340         assert(address_space != null);
   9341         assert(!var_args);
   9342         if (inferred_error_set) {
   9343             try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src);
   9344         }
   9345         const func_index = try ip.getFuncInstance(gpa, .{
   9346             .param_types = param_types,
   9347             .noalias_bits = noalias_bits,
   9348             .bare_return_type = bare_return_type.toIntern(),
   9349             .cc = cc_resolved,
   9350             .alignment = alignment.?,
   9351             .section = switch (section) {
   9352                 .generic => unreachable,
   9353                 .default => .none,
   9354                 .explicit => |x| x.toOptional(),
   9355             },
   9356             .is_noinline = is_noinline,
   9357             .inferred_error_set = inferred_error_set,
   9358             .generic_owner = sema.generic_owner,
   9359             .comptime_args = sema.comptime_args,
   9360             .generation = mod.generation,
   9361         });
   9362         return finishFunc(
   9363             sema,
   9364             block,
   9365             func_index,
   9366             .none,
   9367             ret_poison,
   9368             bare_return_type,
   9369             ret_ty_src,
   9370             cc_resolved,
   9371             is_source_decl,
   9372             ret_ty_requires_comptime,
   9373             func_inst,
   9374             cc_src,
   9375             is_noinline,
   9376             is_generic,
   9377             final_is_generic,
   9378         );
   9379     }
   9380 
   9381     // extern_func and func_decl functions take ownership of `sema.owner_decl`.
   9382     sema.owner_decl.@"linksection" = switch (section) {
   9383         .generic => .none,
   9384         .default => .none,
   9385         .explicit => |section_name| section_name.toOptional(),
   9386     };
   9387     sema.owner_decl.alignment = alignment orelse .none;
   9388     sema.owner_decl.@"addrspace" = address_space orelse .generic;
   9389 
   9390     if (inferred_error_set) {
   9391         assert(!is_extern);
   9392         assert(has_body);
   9393         if (!ret_poison)
   9394             try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src);
   9395         const func_index = try ip.getFuncDeclIes(gpa, .{
   9396             .owner_decl = sema.owner_decl_index,
   9397 
   9398             .param_types = param_types,
   9399             .noalias_bits = noalias_bits,
   9400             .comptime_bits = comptime_bits,
   9401             .bare_return_type = bare_return_type.toIntern(),
   9402             .cc = cc,
   9403             .alignment = alignment,
   9404             .section_is_generic = section == .generic,
   9405             .addrspace_is_generic = address_space == null,
   9406             .is_var_args = var_args,
   9407             .is_generic = final_is_generic,
   9408             .is_noinline = is_noinline,
   9409 
   9410             .zir_body_inst = func_inst,
   9411             .lbrace_line = src_locs.lbrace_line,
   9412             .rbrace_line = src_locs.rbrace_line,
   9413             .lbrace_column = @as(u16, @truncate(src_locs.columns)),
   9414             .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),
   9415         });
   9416         return finishFunc(
   9417             sema,
   9418             block,
   9419             func_index,
   9420             .none,
   9421             ret_poison,
   9422             bare_return_type,
   9423             ret_ty_src,
   9424             cc_resolved,
   9425             is_source_decl,
   9426             ret_ty_requires_comptime,
   9427             func_inst,
   9428             cc_src,
   9429             is_noinline,
   9430             is_generic,
   9431             final_is_generic,
   9432         );
   9433     }
   9434 
   9435     const func_ty = try ip.getFuncType(gpa, .{
   9436         .param_types = param_types,
   9437         .noalias_bits = noalias_bits,
   9438         .comptime_bits = comptime_bits,
   9439         .return_type = bare_return_type.toIntern(),
   9440         .cc = cc,
   9441         .alignment = alignment,
   9442         .section_is_generic = section == .generic,
   9443         .addrspace_is_generic = address_space == null,
   9444         .is_var_args = var_args,
   9445         .is_generic = final_is_generic,
   9446         .is_noinline = is_noinline,
   9447     });
   9448 
   9449     if (is_extern) {
   9450         assert(comptime_bits == 0);
   9451         assert(cc != null);
   9452         assert(section != .generic);
   9453         assert(address_space != null);
   9454         assert(!is_generic);
   9455         if (opt_lib_name) |lib_name| try sema.handleExternLibName(block, .{
   9456             .node_offset_lib_name = src_node_offset,
   9457         }, lib_name);
   9458         const func_index = try ip.getExternFunc(gpa, .{
   9459             .ty = func_ty,
   9460             .decl = sema.owner_decl_index,
   9461             .lib_name = try mod.intern_pool.getOrPutStringOpt(gpa, opt_lib_name),
   9462         });
   9463         return finishFunc(
   9464             sema,
   9465             block,
   9466             func_index,
   9467             func_ty,
   9468             ret_poison,
   9469             bare_return_type,
   9470             ret_ty_src,
   9471             cc_resolved,
   9472             is_source_decl,
   9473             ret_ty_requires_comptime,
   9474             func_inst,
   9475             cc_src,
   9476             is_noinline,
   9477             is_generic,
   9478             final_is_generic,
   9479         );
   9480     }
   9481 
   9482     if (has_body) {
   9483         const func_index = try ip.getFuncDecl(gpa, .{
   9484             .owner_decl = sema.owner_decl_index,
   9485             .ty = func_ty,
   9486             .cc = cc,
   9487             .is_noinline = is_noinline,
   9488             .zir_body_inst = func_inst,
   9489             .lbrace_line = src_locs.lbrace_line,
   9490             .rbrace_line = src_locs.rbrace_line,
   9491             .lbrace_column = @as(u16, @truncate(src_locs.columns)),
   9492             .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),
   9493         });
   9494         return finishFunc(
   9495             sema,
   9496             block,
   9497             func_index,
   9498             func_ty,
   9499             ret_poison,
   9500             bare_return_type,
   9501             ret_ty_src,
   9502             cc_resolved,
   9503             is_source_decl,
   9504             ret_ty_requires_comptime,
   9505             func_inst,
   9506             cc_src,
   9507             is_noinline,
   9508             is_generic,
   9509             final_is_generic,
   9510         );
   9511     }
   9512 
   9513     return finishFunc(
   9514         sema,
   9515         block,
   9516         .none,
   9517         func_ty,
   9518         ret_poison,
   9519         bare_return_type,
   9520         ret_ty_src,
   9521         cc_resolved,
   9522         is_source_decl,
   9523         ret_ty_requires_comptime,
   9524         func_inst,
   9525         cc_src,
   9526         is_noinline,
   9527         is_generic,
   9528         final_is_generic,
   9529     );
   9530 }
   9531 
   9532 fn finishFunc(
   9533     sema: *Sema,
   9534     block: *Block,
   9535     opt_func_index: InternPool.Index,
   9536     func_ty: InternPool.Index,
   9537     ret_poison: bool,
   9538     bare_return_type: Type,
   9539     ret_ty_src: LazySrcLoc,
   9540     cc_resolved: std.builtin.CallingConvention,
   9541     is_source_decl: bool,
   9542     ret_ty_requires_comptime: bool,
   9543     func_inst: Zir.Inst.Index,
   9544     cc_src: LazySrcLoc,
   9545     is_noinline: bool,
   9546     is_generic: bool,
   9547     final_is_generic: bool,
   9548 ) CompileError!Air.Inst.Ref {
   9549     const mod = sema.mod;
   9550     const ip = &mod.intern_pool;
   9551     const gpa = sema.gpa;
   9552     const target = mod.getTarget();
   9553 
   9554     const return_type: Type = if (opt_func_index == .none or ret_poison)
   9555         bare_return_type
   9556     else
   9557         Type.fromInterned(ip.funcTypeReturnType(ip.typeOf(opt_func_index)));
   9558 
   9559     if (!return_type.isValidReturnType(mod)) {
   9560         const opaque_str = if (return_type.zigTypeTag(mod) == .Opaque) "opaque " else "";
   9561         const msg = msg: {
   9562             const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{
   9563                 opaque_str, return_type.fmt(mod),
   9564             });
   9565             errdefer msg.destroy(gpa);
   9566 
   9567             try sema.addDeclaredHereNote(msg, return_type);
   9568             break :msg msg;
   9569         };
   9570         return sema.failWithOwnedErrorMsg(block, msg);
   9571     }
   9572     if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved) and
   9573         !try sema.validateExternType(return_type, .ret_ty))
   9574     {
   9575         const msg = msg: {
   9576             const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{
   9577                 return_type.fmt(mod), @tagName(cc_resolved),
   9578             });
   9579             errdefer msg.destroy(gpa);
   9580 
   9581             const src_decl = mod.declPtr(block.src_decl);
   9582             try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl, mod), return_type, .ret_ty);
   9583 
   9584             try sema.addDeclaredHereNote(msg, return_type);
   9585             break :msg msg;
   9586         };
   9587         return sema.failWithOwnedErrorMsg(block, msg);
   9588     }
   9589 
   9590     // If the return type is comptime-only but not dependent on parameters then
   9591     // all parameter types also need to be comptime.
   9592     if (is_source_decl and opt_func_index != .none and ret_ty_requires_comptime) comptime_check: {
   9593         for (block.params.items(.is_comptime)) |is_comptime| {
   9594             if (!is_comptime) break;
   9595         } else break :comptime_check;
   9596 
   9597         const msg = try sema.errMsg(
   9598             block,
   9599             ret_ty_src,
   9600             "function with comptime-only return type '{}' requires all parameters to be comptime",
   9601             .{return_type.fmt(mod)},
   9602         );
   9603         try sema.explainWhyTypeIsComptime(msg, ret_ty_src.toSrcLoc(sema.owner_decl, mod), return_type);
   9604 
   9605         const tags = sema.code.instructions.items(.tag);
   9606         const data = sema.code.instructions.items(.data);
   9607         const param_body = sema.code.getParamBody(func_inst);
   9608         for (
   9609             block.params.items(.is_comptime),
   9610             block.params.items(.name),
   9611             param_body[0..block.params.len],
   9612         ) |is_comptime, name_nts, param_index| {
   9613             if (!is_comptime) {
   9614                 const param_src = switch (tags[@intFromEnum(param_index)]) {
   9615                     .param => data[@intFromEnum(param_index)].pl_tok.src(),
   9616                     .param_anytype => data[@intFromEnum(param_index)].str_tok.src(),
   9617                     else => unreachable,
   9618                 };
   9619                 const name = sema.code.nullTerminatedString2(name_nts);
   9620                 if (name.len != 0) {
   9621                     try sema.errNote(block, param_src, msg, "param '{s}' is required to be comptime", .{name});
   9622                 } else {
   9623                     try sema.errNote(block, param_src, msg, "param is required to be comptime", .{});
   9624                 }
   9625             }
   9626         }
   9627         return sema.failWithOwnedErrorMsg(block, msg);
   9628     }
   9629 
   9630     const arch = target.cpu.arch;
   9631     if (@as(?[]const u8, switch (cc_resolved) {
   9632         .Unspecified, .C, .Naked, .Async, .Inline => null,
   9633         .Interrupt => switch (arch) {
   9634             .x86, .x86_64, .avr, .msp430 => null,
   9635             else => "x86, x86_64, AVR, and MSP430",
   9636         },
   9637         .Signal => switch (arch) {
   9638             .avr => null,
   9639             else => "AVR",
   9640         },
   9641         .Stdcall, .Fastcall, .Thiscall => switch (arch) {
   9642             .x86 => null,
   9643             else => "x86",
   9644         },
   9645         .Vectorcall => switch (arch) {
   9646             .x86, .aarch64, .aarch64_be, .aarch64_32 => null,
   9647             else => "x86 and AArch64",
   9648         },
   9649         .APCS, .AAPCS, .AAPCSVFP => switch (arch) {
   9650             .arm, .armeb, .aarch64, .aarch64_be, .aarch64_32, .thumb, .thumbeb => null,
   9651             else => "ARM",
   9652         },
   9653         .SysV, .Win64 => switch (arch) {
   9654             .x86_64 => null,
   9655             else => "x86_64",
   9656         },
   9657         .Kernel => switch (arch) {
   9658             .nvptx, .nvptx64, .amdgcn, .spirv32, .spirv64 => null,
   9659             else => "nvptx, amdgcn and SPIR-V",
   9660         },
   9661     })) |allowed_platform| {
   9662         return sema.fail(block, cc_src, "callconv '{s}' is only available on {s}, not {s}", .{
   9663             @tagName(cc_resolved),
   9664             allowed_platform,
   9665             @tagName(arch),
   9666         });
   9667     }
   9668 
   9669     if (cc_resolved == .Inline and is_noinline) {
   9670         return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'Inline'", .{});
   9671     }
   9672     if (is_generic and sema.no_partial_func_ty) return error.GenericPoison;
   9673 
   9674     if (!final_is_generic and sema.wantErrorReturnTracing(return_type)) {
   9675         // Make sure that StackTrace's fields are resolved so that the backend can
   9676         // lower this fn type.
   9677         const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
   9678         try sema.resolveTypeFields(unresolved_stack_trace_ty);
   9679     }
   9680 
   9681     return Air.internedToRef(if (opt_func_index != .none) opt_func_index else func_ty);
   9682 }
   9683 
   9684 fn zirParam(
   9685     sema: *Sema,
   9686     block: *Block,
   9687     inst: Zir.Inst.Index,
   9688     comptime_syntax: bool,
   9689 ) CompileError!void {
   9690     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
   9691     const src = inst_data.src();
   9692     const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
   9693     const param_name: Zir.NullTerminatedString = @enumFromInt(extra.data.name);
   9694     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
   9695 
   9696     const param_ty = param_ty: {
   9697         const err = err: {
   9698             // Make sure any nested param instructions don't clobber our work.
   9699             const prev_params = block.params;
   9700             const prev_no_partial_func_type = sema.no_partial_func_ty;
   9701             const prev_generic_owner = sema.generic_owner;
   9702             const prev_generic_call_src = sema.generic_call_src;
   9703             const prev_generic_call_decl = sema.generic_call_decl;
   9704             block.params = .{};
   9705             sema.no_partial_func_ty = true;
   9706             sema.generic_owner = .none;
   9707             sema.generic_call_src = .unneeded;
   9708             sema.generic_call_decl = .none;
   9709             defer {
   9710                 block.params = prev_params;
   9711                 sema.no_partial_func_ty = prev_no_partial_func_type;
   9712                 sema.generic_owner = prev_generic_owner;
   9713                 sema.generic_call_src = prev_generic_call_src;
   9714                 sema.generic_call_decl = prev_generic_call_decl;
   9715             }
   9716 
   9717             if (sema.resolveBody(block, body, inst)) |param_ty_inst| {
   9718                 if (sema.analyzeAsType(block, src, param_ty_inst)) |param_ty| {
   9719                     break :param_ty param_ty;
   9720                 } else |err| break :err err;
   9721             } else |err| break :err err;
   9722         };
   9723         switch (err) {
   9724             error.GenericPoison => {
   9725                 // The type is not available until the generic instantiation.
   9726                 // We result the param instruction with a poison value and
   9727                 // insert an anytype parameter.
   9728                 try block.params.append(sema.arena, .{
   9729                     .ty = .generic_poison_type,
   9730                     .is_comptime = comptime_syntax,
   9731                     .name = param_name,
   9732                 });
   9733                 sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison);
   9734                 return;
   9735             },
   9736             else => |e| return e,
   9737         }
   9738     };
   9739 
   9740     const is_comptime = sema.typeRequiresComptime(param_ty) catch |err| switch (err) {
   9741         error.GenericPoison => {
   9742             // The type is not available until the generic instantiation.
   9743             // We result the param instruction with a poison value and
   9744             // insert an anytype parameter.
   9745             try block.params.append(sema.arena, .{
   9746                 .ty = .generic_poison_type,
   9747                 .is_comptime = comptime_syntax,
   9748                 .name = param_name,
   9749             });
   9750             sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison);
   9751             return;
   9752         },
   9753         else => |e| return e,
   9754     } or comptime_syntax;
   9755 
   9756     try block.params.append(sema.arena, .{
   9757         .ty = param_ty.toIntern(),
   9758         .is_comptime = comptime_syntax,
   9759         .name = param_name,
   9760     });
   9761 
   9762     if (is_comptime) {
   9763         // If this is a comptime parameter we can add a constant generic_poison
   9764         // since this is also a generic parameter.
   9765         sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison);
   9766     } else {
   9767         // Otherwise we need a dummy runtime instruction.
   9768         const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   9769         try sema.air_instructions.append(sema.gpa, .{
   9770             .tag = .alloc,
   9771             .data = .{ .ty = param_ty },
   9772         });
   9773         sema.inst_map.putAssumeCapacityNoClobber(inst, result_index.toRef());
   9774     }
   9775 }
   9776 
   9777 fn zirParamAnytype(
   9778     sema: *Sema,
   9779     block: *Block,
   9780     inst: Zir.Inst.Index,
   9781     comptime_syntax: bool,
   9782 ) CompileError!void {
   9783     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   9784     const param_name: Zir.NullTerminatedString = @enumFromInt(inst_data.start);
   9785 
   9786     // We are evaluating a generic function without any comptime args provided.
   9787 
   9788     try block.params.append(sema.arena, .{
   9789         .ty = .generic_poison_type,
   9790         .is_comptime = comptime_syntax,
   9791         .name = param_name,
   9792     });
   9793     sema.inst_map.putAssumeCapacity(inst, .generic_poison);
   9794 }
   9795 
   9796 fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9797     const tracy = trace(@src());
   9798     defer tracy.end();
   9799 
   9800     const bin_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
   9801     return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs, false);
   9802 }
   9803 
   9804 fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9805     const tracy = trace(@src());
   9806     defer tracy.end();
   9807 
   9808     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9809     const src = inst_data.src();
   9810     const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
   9811     sema.src = src;
   9812     return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false);
   9813 }
   9814 
   9815 fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9816     const tracy = trace(@src());
   9817     defer tracy.end();
   9818 
   9819     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9820     const src = inst_data.src();
   9821     const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
   9822     return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true);
   9823 }
   9824 
   9825 fn analyzeAs(
   9826     sema: *Sema,
   9827     block: *Block,
   9828     src: LazySrcLoc,
   9829     zir_dest_type: Zir.Inst.Ref,
   9830     zir_operand: Zir.Inst.Ref,
   9831     no_cast_to_comptime_int: bool,
   9832 ) CompileError!Air.Inst.Ref {
   9833     const mod = sema.mod;
   9834     const operand = try sema.resolveInst(zir_operand);
   9835     if (zir_dest_type == .var_args_param_type) return operand;
   9836     const operand_air_inst = sema.resolveInst(zir_dest_type) catch |err| switch (err) {
   9837         error.GenericPoison => return operand,
   9838         else => |e| return e,
   9839     };
   9840     if (operand_air_inst == .var_args_param_type) return operand;
   9841     const dest_ty = sema.analyzeAsType(block, src, operand_air_inst) catch |err| switch (err) {
   9842         error.GenericPoison => return operand,
   9843         else => |e| return e,
   9844     };
   9845     const dest_ty_tag = dest_ty.zigTypeTagOrPoison(mod) catch |err| switch (err) {
   9846         error.GenericPoison => return operand,
   9847     };
   9848     if (dest_ty_tag == .NoReturn) {
   9849         return sema.fail(block, src, "cannot cast to noreturn", .{});
   9850     }
   9851     const is_ret = if (zir_dest_type.toIndex()) |ptr_index|
   9852         sema.code.instructions.items(.tag)[@intFromEnum(ptr_index)] == .ret_type
   9853     else
   9854         false;
   9855     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) {
   9856         error.NotCoercible => unreachable,
   9857         else => |e| return e,
   9858     };
   9859 }
   9860 
   9861 fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9862     const tracy = trace(@src());
   9863     defer tracy.end();
   9864 
   9865     const mod = sema.mod;
   9866     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   9867     const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   9868     const operand = try sema.resolveInst(inst_data.operand);
   9869     const operand_ty = sema.typeOf(operand);
   9870     const ptr_ty = operand_ty.scalarType(mod);
   9871     const is_vector = operand_ty.zigTypeTag(mod) == .Vector;
   9872     if (!ptr_ty.isPtrAtRuntime(mod)) {
   9873         return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(mod)});
   9874     }
   9875     const pointee_ty = ptr_ty.childType(mod);
   9876     if (try sema.typeRequiresComptime(ptr_ty)) {
   9877         const msg = msg: {
   9878             const msg = try sema.errMsg(block, ptr_src, "comptime-only type '{}' has no pointer address", .{pointee_ty.fmt(mod)});
   9879             errdefer msg.destroy(sema.gpa);
   9880             const src_decl = mod.declPtr(block.src_decl);
   9881             try sema.explainWhyTypeIsComptime(msg, ptr_src.toSrcLoc(src_decl, mod), pointee_ty);
   9882             break :msg msg;
   9883         };
   9884         return sema.failWithOwnedErrorMsg(block, msg);
   9885     }
   9886     if (try sema.resolveValueIntable(operand)) |operand_val| ct: {
   9887         if (!is_vector) {
   9888             return Air.internedToRef((try mod.intValue(
   9889                 Type.usize,
   9890                 (try operand_val.getUnsignedIntAdvanced(mod, sema)).?,
   9891             )).toIntern());
   9892         }
   9893         const len = operand_ty.vectorLen(mod);
   9894         const dest_ty = try mod.vectorType(.{ .child = .usize_type, .len = len });
   9895         const new_elems = try sema.arena.alloc(InternPool.Index, len);
   9896         for (new_elems, 0..) |*new_elem, i| {
   9897             const ptr_val = try operand_val.elemValue(mod, i);
   9898             const addr = try ptr_val.getUnsignedIntAdvanced(mod, sema) orelse {
   9899                 // A vector element wasn't an integer pointer. This is a runtime operation.
   9900                 break :ct;
   9901             };
   9902             new_elem.* = (try mod.intValue(
   9903                 Type.usize,
   9904                 addr,
   9905             )).toIntern();
   9906         }
   9907         return Air.internedToRef(try mod.intern(.{ .aggregate = .{
   9908             .ty = dest_ty.toIntern(),
   9909             .storage = .{ .elems = new_elems },
   9910         } }));
   9911     }
   9912     try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);
   9913     if (!is_vector) {
   9914         return block.addUnOp(.int_from_ptr, operand);
   9915     }
   9916     const len = operand_ty.vectorLen(mod);
   9917     const dest_ty = try mod.vectorType(.{ .child = .usize_type, .len = len });
   9918     const new_elems = try sema.arena.alloc(Air.Inst.Ref, len);
   9919     for (new_elems, 0..) |*new_elem, i| {
   9920         const idx_ref = try mod.intRef(Type.usize, i);
   9921         const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref);
   9922         new_elem.* = try block.addUnOp(.int_from_ptr, old_elem);
   9923     }
   9924     return block.addAggregateInit(dest_ty, new_elems);
   9925 }
   9926 
   9927 fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9928     const tracy = trace(@src());
   9929     defer tracy.end();
   9930 
   9931     const mod = sema.mod;
   9932     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9933     const src = inst_data.src();
   9934     const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
   9935     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   9936     const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start));
   9937     const object = try sema.resolveInst(extra.lhs);
   9938     return sema.fieldVal(block, src, object, field_name, field_name_src);
   9939 }
   9940 
   9941 fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9942     const tracy = trace(@src());
   9943     defer tracy.end();
   9944 
   9945     const mod = sema.mod;
   9946     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9947     const src = inst_data.src();
   9948     const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
   9949     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   9950     const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start));
   9951     const object_ptr = try sema.resolveInst(extra.lhs);
   9952     return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
   9953 }
   9954 
   9955 fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9956     const tracy = trace(@src());
   9957     defer tracy.end();
   9958 
   9959     const mod = sema.mod;
   9960     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9961     const src = inst_data.src();
   9962     const field_name_src: LazySrcLoc = .{ .node_offset_field_name_init = inst_data.src_node };
   9963     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   9964     const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start));
   9965     const object_ptr = try sema.resolveInst(extra.lhs);
   9966     const struct_ty = sema.typeOf(object_ptr).childType(mod);
   9967     switch (struct_ty.zigTypeTag(mod)) {
   9968         .Struct, .Union => {
   9969             return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, true);
   9970         },
   9971         else => {
   9972             return sema.failWithStructInitNotSupported(block, src, struct_ty);
   9973         },
   9974     }
   9975 }
   9976 
   9977 fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9978     const tracy = trace(@src());
   9979     defer tracy.end();
   9980 
   9981     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9982     const src = inst_data.src();
   9983     const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
   9984     const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
   9985     const object = try sema.resolveInst(extra.lhs);
   9986     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{
   9987         .needed_comptime_reason = "field name must be comptime-known",
   9988     });
   9989     return sema.fieldVal(block, src, object, field_name, field_name_src);
   9990 }
   9991 
   9992 fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9993     const tracy = trace(@src());
   9994     defer tracy.end();
   9995 
   9996     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9997     const src = inst_data.src();
   9998     const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
   9999     const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
  10000     const object_ptr = try sema.resolveInst(extra.lhs);
  10001     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{
  10002         .needed_comptime_reason = "field name must be comptime-known",
  10003     });
  10004     return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
  10005 }
  10006 
  10007 fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10008     const tracy = trace(@src());
  10009     defer tracy.end();
  10010 
  10011     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10012     const src = inst_data.src();
  10013     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  10014     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10015 
  10016     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast");
  10017     const operand = try sema.resolveInst(extra.rhs);
  10018 
  10019     return sema.intCast(block, inst_data.src(), dest_ty, src, operand, operand_src, true);
  10020 }
  10021 
  10022 fn intCast(
  10023     sema: *Sema,
  10024     block: *Block,
  10025     src: LazySrcLoc,
  10026     dest_ty: Type,
  10027     dest_ty_src: LazySrcLoc,
  10028     operand: Air.Inst.Ref,
  10029     operand_src: LazySrcLoc,
  10030     runtime_safety: bool,
  10031 ) CompileError!Air.Inst.Ref {
  10032     const mod = sema.mod;
  10033     const operand_ty = sema.typeOf(operand);
  10034     const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src);
  10035     const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
  10036 
  10037     if (try sema.isComptimeKnown(operand)) {
  10038         return sema.coerce(block, dest_ty, operand, operand_src);
  10039     } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
  10040         return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});
  10041     }
  10042 
  10043     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src);
  10044     const is_vector = dest_ty.zigTypeTag(mod) == .Vector;
  10045 
  10046     if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| {
  10047         // requirement: intCast(u0, input) iff input == 0
  10048         if (runtime_safety and block.wantSafety()) {
  10049             try sema.requireRuntimeBlock(block, src, operand_src);
  10050             const wanted_info = dest_scalar_ty.intInfo(mod);
  10051             const wanted_bits = wanted_info.bits;
  10052 
  10053             if (wanted_bits == 0) {
  10054                 const ok = if (is_vector) ok: {
  10055                     const zeros = try sema.splat(operand_ty, try mod.intValue(operand_scalar_ty, 0));
  10056                     const zero_inst = Air.internedToRef(zeros.toIntern());
  10057                     const is_in_range = try block.addCmpVector(operand, zero_inst, .eq);
  10058                     const all_in_range = try block.addInst(.{
  10059                         .tag = .reduce,
  10060                         .data = .{ .reduce = .{ .operand = is_in_range, .operation = .And } },
  10061                     });
  10062                     break :ok all_in_range;
  10063                 } else ok: {
  10064                     const zero_inst = Air.internedToRef((try mod.intValue(operand_ty, 0)).toIntern());
  10065                     const is_in_range = try block.addBinOp(.cmp_lte, operand, zero_inst);
  10066                     break :ok is_in_range;
  10067                 };
  10068                 try sema.addSafetyCheck(block, src, ok, .cast_truncated_data);
  10069             }
  10070         }
  10071 
  10072         return Air.internedToRef(opv.toIntern());
  10073     }
  10074 
  10075     try sema.requireRuntimeBlock(block, src, operand_src);
  10076     if (runtime_safety and block.wantSafety()) {
  10077         const actual_info = operand_scalar_ty.intInfo(mod);
  10078         const wanted_info = dest_scalar_ty.intInfo(mod);
  10079         const actual_bits = actual_info.bits;
  10080         const wanted_bits = wanted_info.bits;
  10081         const actual_value_bits = actual_bits - @intFromBool(actual_info.signedness == .signed);
  10082         const wanted_value_bits = wanted_bits - @intFromBool(wanted_info.signedness == .signed);
  10083 
  10084         // range shrinkage
  10085         // requirement: int value fits into target type
  10086         if (wanted_value_bits < actual_value_bits) {
  10087             const dest_max_val_scalar = try dest_scalar_ty.maxIntScalar(mod, operand_scalar_ty);
  10088             const dest_max_val = try sema.splat(operand_ty, dest_max_val_scalar);
  10089             const dest_max = Air.internedToRef(dest_max_val.toIntern());
  10090             const diff = try block.addBinOp(.sub_wrap, dest_max, operand);
  10091 
  10092             if (actual_info.signedness == .signed) {
  10093                 // Reinterpret the sign-bit as part of the value. This will make
  10094                 // negative differences (`operand` > `dest_max`) appear too big.
  10095                 const unsigned_scalar_operand_ty = try mod.intType(.unsigned, actual_bits);
  10096                 const unsigned_operand_ty = if (is_vector) try mod.vectorType(.{
  10097                     .len = dest_ty.vectorLen(mod),
  10098                     .child = unsigned_scalar_operand_ty.toIntern(),
  10099                 }) else unsigned_scalar_operand_ty;
  10100                 const diff_unsigned = try block.addBitCast(unsigned_operand_ty, diff);
  10101 
  10102                 // If the destination type is signed, then we need to double its
  10103                 // range to account for negative values.
  10104                 const dest_range_val = if (wanted_info.signedness == .signed) range_val: {
  10105                     const one_scalar = try mod.intValue(unsigned_scalar_operand_ty, 1);
  10106                     const one = if (is_vector) Value.fromInterned((try mod.intern(.{ .aggregate = .{
  10107                         .ty = unsigned_operand_ty.toIntern(),
  10108                         .storage = .{ .repeated_elem = one_scalar.toIntern() },
  10109                     } }))) else one_scalar;
  10110                     const range_minus_one = try dest_max_val.shl(one, unsigned_operand_ty, sema.arena, mod);
  10111                     break :range_val try sema.intAdd(range_minus_one, one, unsigned_operand_ty, undefined);
  10112                 } else try mod.getCoerced(dest_max_val, unsigned_operand_ty);
  10113                 const dest_range = Air.internedToRef(dest_range_val.toIntern());
  10114 
  10115                 const ok = if (is_vector) ok: {
  10116                     const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte);
  10117                     const all_in_range = try block.addInst(.{
  10118                         .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  10119                         .data = .{ .reduce = .{
  10120                             .operand = is_in_range,
  10121                             .operation = .And,
  10122                         } },
  10123                     });
  10124                     break :ok all_in_range;
  10125                 } else ok: {
  10126                     const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range);
  10127                     break :ok is_in_range;
  10128                 };
  10129                 // TODO negative_to_unsigned?
  10130                 try sema.addSafetyCheck(block, src, ok, .cast_truncated_data);
  10131             } else {
  10132                 const ok = if (is_vector) ok: {
  10133                     const is_in_range = try block.addCmpVector(diff, dest_max, .lte);
  10134                     const all_in_range = try block.addInst(.{
  10135                         .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  10136                         .data = .{ .reduce = .{
  10137                             .operand = is_in_range,
  10138                             .operation = .And,
  10139                         } },
  10140                     });
  10141                     break :ok all_in_range;
  10142                 } else ok: {
  10143                     const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max);
  10144                     break :ok is_in_range;
  10145                 };
  10146                 try sema.addSafetyCheck(block, src, ok, .cast_truncated_data);
  10147             }
  10148         } else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) {
  10149             // no shrinkage, yes sign loss
  10150             // requirement: signed to unsigned >= 0
  10151             const ok = if (is_vector) ok: {
  10152                 const scalar_zero = try mod.intValue(operand_scalar_ty, 0);
  10153                 const zero_val = try sema.splat(operand_ty, scalar_zero);
  10154                 const zero_inst = Air.internedToRef(zero_val.toIntern());
  10155                 const is_in_range = try block.addCmpVector(operand, zero_inst, .gte);
  10156                 const all_in_range = try block.addInst(.{
  10157                     .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  10158                     .data = .{ .reduce = .{
  10159                         .operand = is_in_range,
  10160                         .operation = .And,
  10161                     } },
  10162                 });
  10163                 break :ok all_in_range;
  10164             } else ok: {
  10165                 const zero_inst = Air.internedToRef((try mod.intValue(operand_ty, 0)).toIntern());
  10166                 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);
  10167                 break :ok is_in_range;
  10168             };
  10169             try sema.addSafetyCheck(block, src, ok, .negative_to_unsigned);
  10170         }
  10171     }
  10172     return block.addTyOp(.intcast, dest_ty, operand);
  10173 }
  10174 
  10175 fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10176     const tracy = trace(@src());
  10177     defer tracy.end();
  10178 
  10179     const mod = sema.mod;
  10180     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10181     const src = inst_data.src();
  10182     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  10183     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10184 
  10185     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@bitCast");
  10186     const operand = try sema.resolveInst(extra.rhs);
  10187     const operand_ty = sema.typeOf(operand);
  10188     switch (dest_ty.zigTypeTag(mod)) {
  10189         .AnyFrame,
  10190         .ComptimeFloat,
  10191         .ComptimeInt,
  10192         .EnumLiteral,
  10193         .ErrorSet,
  10194         .ErrorUnion,
  10195         .Fn,
  10196         .Frame,
  10197         .NoReturn,
  10198         .Null,
  10199         .Opaque,
  10200         .Optional,
  10201         .Type,
  10202         .Undefined,
  10203         .Void,
  10204         => return sema.fail(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)}),
  10205 
  10206         .Enum => {
  10207             const msg = msg: {
  10208                 const msg = try sema.errMsg(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)});
  10209                 errdefer msg.destroy(sema.gpa);
  10210                 switch (operand_ty.zigTypeTag(mod)) {
  10211                     .Int, .ComptimeInt => try sema.errNote(block, src, msg, "use @enumFromInt to cast from '{}'", .{operand_ty.fmt(mod)}),
  10212                     else => {},
  10213                 }
  10214 
  10215                 break :msg msg;
  10216             };
  10217             return sema.failWithOwnedErrorMsg(block, msg);
  10218         },
  10219 
  10220         .Pointer => {
  10221             const msg = msg: {
  10222                 const msg = try sema.errMsg(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)});
  10223                 errdefer msg.destroy(sema.gpa);
  10224                 switch (operand_ty.zigTypeTag(mod)) {
  10225                     .Int, .ComptimeInt => try sema.errNote(block, src, msg, "use @ptrFromInt to cast from '{}'", .{operand_ty.fmt(mod)}),
  10226                     .Pointer => try sema.errNote(block, src, msg, "use @ptrCast to cast from '{}'", .{operand_ty.fmt(mod)}),
  10227                     else => {},
  10228                 }
  10229 
  10230                 break :msg msg;
  10231             };
  10232             return sema.failWithOwnedErrorMsg(block, msg);
  10233         },
  10234         .Struct, .Union => if (dest_ty.containerLayout(mod) == .Auto) {
  10235             const container = switch (dest_ty.zigTypeTag(mod)) {
  10236                 .Struct => "struct",
  10237                 .Union => "union",
  10238                 else => unreachable,
  10239             };
  10240             return sema.fail(block, src, "cannot @bitCast to '{}'; {s} does not have a guaranteed in-memory layout", .{
  10241                 dest_ty.fmt(mod), container,
  10242             });
  10243         },
  10244 
  10245         .Array,
  10246         .Bool,
  10247         .Float,
  10248         .Int,
  10249         .Vector,
  10250         => {},
  10251     }
  10252     switch (operand_ty.zigTypeTag(mod)) {
  10253         .AnyFrame,
  10254         .ComptimeFloat,
  10255         .ComptimeInt,
  10256         .EnumLiteral,
  10257         .ErrorSet,
  10258         .ErrorUnion,
  10259         .Fn,
  10260         .Frame,
  10261         .NoReturn,
  10262         .Null,
  10263         .Opaque,
  10264         .Optional,
  10265         .Type,
  10266         .Undefined,
  10267         .Void,
  10268         => return sema.fail(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)}),
  10269 
  10270         .Enum => {
  10271             const msg = msg: {
  10272                 const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)});
  10273                 errdefer msg.destroy(sema.gpa);
  10274                 switch (dest_ty.zigTypeTag(mod)) {
  10275                     .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @intFromEnum to cast to '{}'", .{dest_ty.fmt(mod)}),
  10276                     else => {},
  10277                 }
  10278 
  10279                 break :msg msg;
  10280             };
  10281             return sema.failWithOwnedErrorMsg(block, msg);
  10282         },
  10283         .Pointer => {
  10284             const msg = msg: {
  10285                 const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)});
  10286                 errdefer msg.destroy(sema.gpa);
  10287                 switch (dest_ty.zigTypeTag(mod)) {
  10288                     .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @intFromPtr to cast to '{}'", .{dest_ty.fmt(mod)}),
  10289                     .Pointer => try sema.errNote(block, operand_src, msg, "use @ptrCast to cast to '{}'", .{dest_ty.fmt(mod)}),
  10290                     else => {},
  10291                 }
  10292 
  10293                 break :msg msg;
  10294             };
  10295             return sema.failWithOwnedErrorMsg(block, msg);
  10296         },
  10297         .Struct, .Union => if (operand_ty.containerLayout(mod) == .Auto) {
  10298             const container = switch (operand_ty.zigTypeTag(mod)) {
  10299                 .Struct => "struct",
  10300                 .Union => "union",
  10301                 else => unreachable,
  10302             };
  10303             return sema.fail(block, operand_src, "cannot @bitCast from '{}'; {s} does not have a guaranteed in-memory layout", .{
  10304                 operand_ty.fmt(mod), container,
  10305             });
  10306         },
  10307 
  10308         .Array,
  10309         .Bool,
  10310         .Float,
  10311         .Int,
  10312         .Vector,
  10313         => {},
  10314     }
  10315     return sema.bitCast(block, dest_ty, operand, inst_data.src(), operand_src);
  10316 }
  10317 
  10318 fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10319     const tracy = trace(@src());
  10320     defer tracy.end();
  10321 
  10322     const mod = sema.mod;
  10323     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10324     const src = inst_data.src();
  10325     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  10326     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10327 
  10328     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast");
  10329     const dest_scalar_ty = dest_ty.scalarType(mod);
  10330 
  10331     const operand = try sema.resolveInst(extra.rhs);
  10332     const operand_ty = sema.typeOf(operand);
  10333     const operand_scalar_ty = operand_ty.scalarType(mod);
  10334 
  10335     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
  10336     const is_vector = dest_ty.zigTypeTag(mod) == .Vector;
  10337 
  10338     const target = mod.getTarget();
  10339     const dest_is_comptime_float = switch (dest_scalar_ty.zigTypeTag(mod)) {
  10340         .ComptimeFloat => true,
  10341         .Float => false,
  10342         else => return sema.fail(
  10343             block,
  10344             src,
  10345             "expected float or vector type, found '{}'",
  10346             .{dest_ty.fmt(mod)},
  10347         ),
  10348     };
  10349 
  10350     switch (operand_scalar_ty.zigTypeTag(mod)) {
  10351         .ComptimeFloat, .Float, .ComptimeInt => {},
  10352         else => return sema.fail(
  10353             block,
  10354             operand_src,
  10355             "expected float or vector type, found '{}'",
  10356             .{operand_ty.fmt(mod)},
  10357         ),
  10358     }
  10359 
  10360     if (try sema.resolveValue(operand)) |operand_val| {
  10361         if (!is_vector) {
  10362             return Air.internedToRef((try operand_val.floatCast(dest_ty, mod)).toIntern());
  10363         }
  10364         const vec_len = operand_ty.vectorLen(mod);
  10365         const new_elems = try sema.arena.alloc(InternPool.Index, vec_len);
  10366         for (new_elems, 0..) |*new_elem, i| {
  10367             const old_elem = try operand_val.elemValue(mod, i);
  10368             new_elem.* = (try old_elem.floatCast(dest_scalar_ty, mod)).toIntern();
  10369         }
  10370         return Air.internedToRef(try mod.intern(.{ .aggregate = .{
  10371             .ty = dest_ty.toIntern(),
  10372             .storage = .{ .elems = new_elems },
  10373         } }));
  10374     }
  10375     if (dest_is_comptime_float) {
  10376         return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{});
  10377     }
  10378     try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
  10379 
  10380     const src_bits = operand_scalar_ty.floatBits(target);
  10381     const dst_bits = dest_scalar_ty.floatBits(target);
  10382     if (dst_bits >= src_bits) {
  10383         return sema.coerce(block, dest_ty, operand, operand_src);
  10384     }
  10385     if (!is_vector) {
  10386         return block.addTyOp(.fptrunc, dest_ty, operand);
  10387     }
  10388     const vec_len = operand_ty.vectorLen(mod);
  10389     const new_elems = try sema.arena.alloc(Air.Inst.Ref, vec_len);
  10390     for (new_elems, 0..) |*new_elem, i| {
  10391         const idx_ref = try mod.intRef(Type.usize, i);
  10392         const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref);
  10393         new_elem.* = try block.addTyOp(.fptrunc, dest_scalar_ty, old_elem);
  10394     }
  10395     return block.addAggregateInit(dest_ty, new_elems);
  10396 }
  10397 
  10398 fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10399     const tracy = trace(@src());
  10400     defer tracy.end();
  10401 
  10402     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10403     const src = inst_data.src();
  10404     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10405     const array = try sema.resolveInst(extra.lhs);
  10406     const elem_index = try sema.resolveInst(extra.rhs);
  10407     return sema.elemVal(block, src, array, elem_index, src, false);
  10408 }
  10409 
  10410 fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10411     const tracy = trace(@src());
  10412     defer tracy.end();
  10413 
  10414     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10415     const src = inst_data.src();
  10416     const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
  10417     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10418     const array = try sema.resolveInst(extra.lhs);
  10419     const elem_index = try sema.resolveInst(extra.rhs);
  10420     return sema.elemVal(block, src, array, elem_index, elem_index_src, true);
  10421 }
  10422 
  10423 fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10424     const tracy = trace(@src());
  10425     defer tracy.end();
  10426 
  10427     const mod = sema.mod;
  10428     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].elem_val_imm;
  10429     const array = try sema.resolveInst(inst_data.operand);
  10430     const elem_index = try mod.intRef(Type.usize, inst_data.idx);
  10431     return sema.elemVal(block, .unneeded, array, elem_index, .unneeded, false);
  10432 }
  10433 
  10434 fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10435     const tracy = trace(@src());
  10436     defer tracy.end();
  10437 
  10438     const mod = sema.mod;
  10439     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10440     const src = inst_data.src();
  10441     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10442     const array_ptr = try sema.resolveInst(extra.lhs);
  10443     const elem_index = try sema.resolveInst(extra.rhs);
  10444     const indexable_ty = sema.typeOf(array_ptr);
  10445     if (indexable_ty.zigTypeTag(mod) != .Pointer) {
  10446         const capture_src: LazySrcLoc = .{ .for_capture_from_input = inst_data.src_node };
  10447         const msg = msg: {
  10448             const msg = try sema.errMsg(block, capture_src, "pointer capture of non pointer type '{}'", .{
  10449                 indexable_ty.fmt(mod),
  10450             });
  10451             errdefer msg.destroy(sema.gpa);
  10452             if (indexable_ty.isIndexable(mod)) {
  10453                 try sema.errNote(block, src, msg, "consider using '&' here", .{});
  10454             }
  10455             break :msg msg;
  10456         };
  10457         return sema.failWithOwnedErrorMsg(block, msg);
  10458     }
  10459     return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false);
  10460 }
  10461 
  10462 fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10463     const tracy = trace(@src());
  10464     defer tracy.end();
  10465 
  10466     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10467     const src = inst_data.src();
  10468     const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
  10469     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10470     const array_ptr = try sema.resolveInst(extra.lhs);
  10471     const elem_index = try sema.resolveInst(extra.rhs);
  10472     return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true);
  10473 }
  10474 
  10475 fn zirArrayInitElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10476     const tracy = trace(@src());
  10477     defer tracy.end();
  10478 
  10479     const mod = sema.mod;
  10480     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10481     const src = inst_data.src();
  10482     const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
  10483     const array_ptr = try sema.resolveInst(extra.ptr);
  10484     const elem_index = try sema.mod.intRef(Type.usize, extra.index);
  10485     const array_ty = sema.typeOf(array_ptr).childType(mod);
  10486     switch (array_ty.zigTypeTag(mod)) {
  10487         .Array, .Vector => {},
  10488         else => if (!array_ty.isTuple(mod)) {
  10489             return sema.failWithArrayInitNotSupported(block, src, array_ty);
  10490         },
  10491     }
  10492     return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true);
  10493 }
  10494 
  10495 fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10496     const tracy = trace(@src());
  10497     defer tracy.end();
  10498 
  10499     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10500     const src = inst_data.src();
  10501     const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
  10502     const array_ptr = try sema.resolveInst(extra.lhs);
  10503     const start = try sema.resolveInst(extra.start);
  10504     const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
  10505     const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
  10506     const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
  10507 
  10508     return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src, false);
  10509 }
  10510 
  10511 fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10512     const tracy = trace(@src());
  10513     defer tracy.end();
  10514 
  10515     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10516     const src = inst_data.src();
  10517     const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;
  10518     const array_ptr = try sema.resolveInst(extra.lhs);
  10519     const start = try sema.resolveInst(extra.start);
  10520     const end = try sema.resolveInst(extra.end);
  10521     const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
  10522     const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
  10523     const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
  10524 
  10525     return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src, false);
  10526 }
  10527 
  10528 fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10529     const tracy = trace(@src());
  10530     defer tracy.end();
  10531 
  10532     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10533     const src = inst_data.src();
  10534     const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node };
  10535     const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;
  10536     const array_ptr = try sema.resolveInst(extra.lhs);
  10537     const start = try sema.resolveInst(extra.start);
  10538     const end: Air.Inst.Ref = if (extra.end == .none) .none else try sema.resolveInst(extra.end);
  10539     const sentinel = try sema.resolveInst(extra.sentinel);
  10540     const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
  10541     const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
  10542     const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
  10543 
  10544     return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false);
  10545 }
  10546 
  10547 fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10548     const tracy = trace(@src());
  10549     defer tracy.end();
  10550 
  10551     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10552     const src = inst_data.src();
  10553     const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
  10554     const array_ptr = try sema.resolveInst(extra.lhs);
  10555     const start = try sema.resolveInst(extra.start);
  10556     const len = try sema.resolveInst(extra.len);
  10557     const sentinel = if (extra.sentinel == .none) .none else try sema.resolveInst(extra.sentinel);
  10558     const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
  10559     const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset };
  10560     const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
  10561     const sentinel_src: LazySrcLoc = if (sentinel == .none)
  10562         .unneeded
  10563     else
  10564         .{ .node_offset_slice_sentinel = inst_data.src_node };
  10565 
  10566     return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
  10567 }
  10568 
  10569 /// Holds common data used when analyzing or resolving switch prong bodies,
  10570 /// including setting up captures.
  10571 const SwitchProngAnalysis = struct {
  10572     sema: *Sema,
  10573     /// The block containing the `switch_block` itself.
  10574     parent_block: *Block,
  10575     /// The raw switch operand value (*not* the condition). Always defined.
  10576     operand: Air.Inst.Ref,
  10577     /// May be `undefined` if no prong has a by-ref capture.
  10578     operand_ptr: Air.Inst.Ref,
  10579     /// The switch condition value. For unions, `operand` is the union and `cond` is its tag.
  10580     cond: Air.Inst.Ref,
  10581     /// If this switch is on an error set, this is the type to assign to the
  10582     /// `else` prong. If `null`, the prong should be unreachable.
  10583     else_error_ty: ?Type,
  10584     /// The index of the `switch_block` instruction itself.
  10585     switch_block_inst: Zir.Inst.Index,
  10586     /// The dummy index into which inline tag captures should be placed. May be
  10587     /// undefined if no prong has a tag capture.
  10588     tag_capture_inst: Zir.Inst.Index,
  10589 
  10590     /// Resolve a switch prong which is determined at comptime to have no peers.
  10591     /// Uses `resolveBlockBody`. Sets up captures as needed.
  10592     fn resolveProngComptime(
  10593         spa: SwitchProngAnalysis,
  10594         child_block: *Block,
  10595         prong_type: enum { normal, special },
  10596         prong_body: []const Zir.Inst.Index,
  10597         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  10598         /// Must use the `scalar_capture`, `special_capture`, or `multi_capture` union field.
  10599         raw_capture_src: Module.SwitchProngSrc,
  10600         /// The set of all values which can reach this prong. May be undefined
  10601         /// if the prong is special or contains ranges.
  10602         case_vals: []const Air.Inst.Ref,
  10603         /// The inline capture of this prong. If this is not an inline prong,
  10604         /// this is `.none`.
  10605         inline_case_capture: Air.Inst.Ref,
  10606         /// Whether this prong has an inline tag capture. If `true`, then
  10607         /// `inline_case_capture` cannot be `.none`.
  10608         has_tag_capture: bool,
  10609         merges: *Block.Merges,
  10610     ) CompileError!Air.Inst.Ref {
  10611         const sema = spa.sema;
  10612         const src = sema.code.instructions.items(.data)[@intFromEnum(spa.switch_block_inst)].pl_node.src();
  10613 
  10614         if (has_tag_capture) {
  10615             const tag_ref = try spa.analyzeTagCapture(child_block, raw_capture_src, inline_case_capture);
  10616             sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
  10617         }
  10618         defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
  10619 
  10620         switch (capture) {
  10621             .none => {
  10622                 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
  10623             },
  10624 
  10625             .by_val, .by_ref => {
  10626                 const capture_ref = try spa.analyzeCapture(
  10627                     child_block,
  10628                     capture == .by_ref,
  10629                     prong_type == .special,
  10630                     raw_capture_src,
  10631                     case_vals,
  10632                     inline_case_capture,
  10633                 );
  10634 
  10635                 if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) {
  10636                     // This prong should be unreachable!
  10637                     return .unreachable_value;
  10638                 }
  10639 
  10640                 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
  10641                 defer assert(sema.inst_map.remove(spa.switch_block_inst));
  10642 
  10643                 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
  10644             },
  10645         }
  10646     }
  10647 
  10648     /// Analyze a switch prong which may have peers at runtime.
  10649     /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed.
  10650     fn analyzeProngRuntime(
  10651         spa: SwitchProngAnalysis,
  10652         case_block: *Block,
  10653         prong_type: enum { normal, special },
  10654         prong_body: []const Zir.Inst.Index,
  10655         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  10656         /// Must use the `scalar`, `special`, or `multi_capture` union field.
  10657         raw_capture_src: Module.SwitchProngSrc,
  10658         /// The set of all values which can reach this prong. May be undefined
  10659         /// if the prong is special or contains ranges.
  10660         case_vals: []const Air.Inst.Ref,
  10661         /// The inline capture of this prong. If this is not an inline prong,
  10662         /// this is `.none`.
  10663         inline_case_capture: Air.Inst.Ref,
  10664         /// Whether this prong has an inline tag capture. If `true`, then
  10665         /// `inline_case_capture` cannot be `.none`.
  10666         has_tag_capture: bool,
  10667     ) CompileError!void {
  10668         const sema = spa.sema;
  10669 
  10670         if (has_tag_capture) {
  10671             const tag_ref = try spa.analyzeTagCapture(case_block, raw_capture_src, inline_case_capture);
  10672             sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
  10673         }
  10674         defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
  10675 
  10676         switch (capture) {
  10677             .none => {
  10678                 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
  10679             },
  10680 
  10681             .by_val, .by_ref => {
  10682                 const capture_ref = try spa.analyzeCapture(
  10683                     case_block,
  10684                     capture == .by_ref,
  10685                     prong_type == .special,
  10686                     raw_capture_src,
  10687                     case_vals,
  10688                     inline_case_capture,
  10689                 );
  10690 
  10691                 if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) {
  10692                     // No need to analyze any further, the prong is unreachable
  10693                     return;
  10694                 }
  10695 
  10696                 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
  10697                 defer assert(sema.inst_map.remove(spa.switch_block_inst));
  10698 
  10699                 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
  10700             },
  10701         }
  10702     }
  10703 
  10704     fn analyzeTagCapture(
  10705         spa: SwitchProngAnalysis,
  10706         block: *Block,
  10707         raw_capture_src: Module.SwitchProngSrc,
  10708         inline_case_capture: Air.Inst.Ref,
  10709     ) CompileError!Air.Inst.Ref {
  10710         const sema = spa.sema;
  10711         const mod = sema.mod;
  10712         const operand_ty = sema.typeOf(spa.operand);
  10713         if (operand_ty.zigTypeTag(mod) != .Union) {
  10714             const zir_datas = sema.code.instructions.items(.data);
  10715             const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node;
  10716             const raw_tag_capture_src: Module.SwitchProngSrc = switch (raw_capture_src) {
  10717                 .scalar_capture => |i| .{ .scalar_tag_capture = i },
  10718                 .multi_capture => |i| .{ .multi_tag_capture = i },
  10719                 .special_capture => .special_tag_capture,
  10720                 else => unreachable,
  10721             };
  10722             const capture_src = raw_tag_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none);
  10723             const msg = msg: {
  10724                 const msg = try sema.errMsg(block, capture_src, "cannot capture tag of non-union type '{}'", .{
  10725                     operand_ty.fmt(mod),
  10726                 });
  10727                 errdefer msg.destroy(sema.gpa);
  10728                 try sema.addDeclaredHereNote(msg, operand_ty);
  10729                 break :msg msg;
  10730             };
  10731             return sema.failWithOwnedErrorMsg(block, msg);
  10732         }
  10733         assert(inline_case_capture != .none);
  10734         return inline_case_capture;
  10735     }
  10736 
  10737     fn analyzeCapture(
  10738         spa: SwitchProngAnalysis,
  10739         block: *Block,
  10740         capture_byref: bool,
  10741         is_special_prong: bool,
  10742         raw_capture_src: Module.SwitchProngSrc,
  10743         case_vals: []const Air.Inst.Ref,
  10744         inline_case_capture: Air.Inst.Ref,
  10745     ) CompileError!Air.Inst.Ref {
  10746         const sema = spa.sema;
  10747         const mod = sema.mod;
  10748         const ip = &mod.intern_pool;
  10749 
  10750         const zir_datas = sema.code.instructions.items(.data);
  10751         const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node;
  10752 
  10753         const operand_ty = sema.typeOf(spa.operand);
  10754         const operand_ptr_ty = if (capture_byref) sema.typeOf(spa.operand_ptr) else undefined;
  10755         const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_node_offset };
  10756 
  10757         if (inline_case_capture != .none) {
  10758             const item_val = sema.resolveConstDefinedValue(block, .unneeded, inline_case_capture, undefined) catch unreachable;
  10759             if (operand_ty.zigTypeTag(mod) == .Union) {
  10760                 const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, mod).?);
  10761                 const union_obj = mod.typeToUnion(operand_ty).?;
  10762                 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
  10763                 if (capture_byref) {
  10764                     const ptr_field_ty = try sema.ptrType(.{
  10765                         .child = field_ty.toIntern(),
  10766                         .flags = .{
  10767                             .is_const = !operand_ptr_ty.ptrIsMutable(mod),
  10768                             .is_volatile = operand_ptr_ty.isVolatilePtr(mod),
  10769                             .address_space = operand_ptr_ty.ptrAddressSpace(mod),
  10770                         },
  10771                     });
  10772                     if (try sema.resolveDefinedValue(block, sema.src, spa.operand_ptr)) |union_ptr| {
  10773                         return Air.internedToRef((try mod.intern(.{ .ptr = .{
  10774                             .ty = ptr_field_ty.toIntern(),
  10775                             .addr = .{ .field = .{
  10776                                 .base = union_ptr.toIntern(),
  10777                                 .index = field_index,
  10778                             } },
  10779                         } })));
  10780                     }
  10781                     return block.addStructFieldPtr(spa.operand_ptr, field_index, ptr_field_ty);
  10782                 } else {
  10783                     if (try sema.resolveDefinedValue(block, sema.src, spa.operand)) |union_val| {
  10784                         const tag_and_val = ip.indexToKey(union_val.toIntern()).un;
  10785                         return Air.internedToRef(tag_and_val.val);
  10786                     }
  10787                     return block.addStructFieldVal(spa.operand, field_index, field_ty);
  10788                 }
  10789             } else if (capture_byref) {
  10790                 return anonDeclRef(sema, item_val.toIntern());
  10791             } else {
  10792                 return inline_case_capture;
  10793             }
  10794         }
  10795 
  10796         if (is_special_prong) {
  10797             if (capture_byref) {
  10798                 return spa.operand_ptr;
  10799             }
  10800 
  10801             switch (operand_ty.zigTypeTag(mod)) {
  10802                 .ErrorSet => if (spa.else_error_ty) |ty| {
  10803                     return sema.bitCast(block, ty, spa.operand, operand_src, null);
  10804                 } else {
  10805                     try block.addUnreachable(operand_src, false);
  10806                     return .unreachable_value;
  10807                 },
  10808                 else => return spa.operand,
  10809             }
  10810         }
  10811 
  10812         switch (operand_ty.zigTypeTag(mod)) {
  10813             .Union => {
  10814                 const union_obj = mod.typeToUnion(operand_ty).?;
  10815                 const first_item_val = sema.resolveConstDefinedValue(block, .unneeded, case_vals[0], undefined) catch unreachable;
  10816 
  10817                 const first_field_index: u32 = mod.unionTagFieldIndex(union_obj, first_item_val).?;
  10818                 const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[first_field_index]);
  10819 
  10820                 const field_indices = try sema.arena.alloc(u32, case_vals.len);
  10821                 for (case_vals, field_indices) |item, *field_idx| {
  10822                     const item_val = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch unreachable;
  10823                     field_idx.* = mod.unionTagFieldIndex(union_obj, item_val).?;
  10824                 }
  10825 
  10826                 // Fast path: if all the operands are the same type already, we don't need to hit
  10827                 // PTR! This will also allow us to emit simpler code.
  10828                 const same_types = for (field_indices[1..]) |field_idx| {
  10829                     const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10830                     if (!field_ty.eql(first_field_ty, sema.mod)) break false;
  10831                 } else true;
  10832 
  10833                 const capture_ty = if (same_types) first_field_ty else capture_ty: {
  10834                     // We need values to run PTR on, so make a bunch of undef constants.
  10835                     const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
  10836                     for (dummy_captures, field_indices) |*dummy, field_idx| {
  10837                         const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10838                         dummy.* = try mod.undefRef(field_ty);
  10839                     }
  10840 
  10841                     const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
  10842                     @memset(case_srcs, .unneeded);
  10843 
  10844                     break :capture_ty sema.resolvePeerTypes(block, .unneeded, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
  10845                         error.NeededSourceLocation => {
  10846                             // This must be a multi-prong so this must be a `multi_capture` src
  10847                             const multi_idx = raw_capture_src.multi_capture;
  10848                             const src_decl_ptr = sema.mod.declPtr(block.src_decl);
  10849                             for (case_srcs, 0..) |*case_src, i| {
  10850                                 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(i) } };
  10851                                 case_src.* = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
  10852                             }
  10853                             const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
  10854                             _ = sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err1| switch (err1) {
  10855                                 error.AnalysisFail => {
  10856                                     const msg = sema.err orelse return error.AnalysisFail;
  10857                                     try sema.reparentOwnedErrorMsg(block, capture_src, msg, "capture group with incompatible types", .{});
  10858                                     return error.AnalysisFail;
  10859                                 },
  10860                                 else => |e| return e,
  10861                             };
  10862                             unreachable;
  10863                         },
  10864                         else => |e| return e,
  10865                     };
  10866                 };
  10867 
  10868                 // By-reference captures have some further restrictions which make them easier to emit
  10869                 if (capture_byref) {
  10870                     const operand_ptr_info = operand_ptr_ty.ptrInfo(mod);
  10871                     const capture_ptr_ty = try sema.ptrType(.{
  10872                         .child = capture_ty.toIntern(),
  10873                         .flags = .{
  10874                             // TODO: alignment!
  10875                             .is_const = operand_ptr_info.flags.is_const,
  10876                             .is_volatile = operand_ptr_info.flags.is_volatile,
  10877                             .address_space = operand_ptr_info.flags.address_space,
  10878                         },
  10879                     });
  10880 
  10881                     // By-ref captures of hetereogeneous types are only allowed if each field
  10882                     // pointer type is in-memory coercible to the capture pointer type.
  10883                     if (!same_types) {
  10884                         for (field_indices, 0..) |field_idx, i| {
  10885                             const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10886                             const field_ptr_ty = try sema.ptrType(.{
  10887                                 .child = field_ty.toIntern(),
  10888                                 .flags = .{
  10889                                     // TODO: alignment!
  10890                                     .is_const = operand_ptr_info.flags.is_const,
  10891                                     .is_volatile = operand_ptr_info.flags.is_volatile,
  10892                                     .address_space = operand_ptr_info.flags.address_space,
  10893                                 },
  10894                             });
  10895                             if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
  10896                                 const multi_idx = raw_capture_src.multi_capture;
  10897                                 const src_decl_ptr = sema.mod.declPtr(block.src_decl);
  10898                                 const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
  10899                                 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(i) } };
  10900                                 const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
  10901                                 const msg = msg: {
  10902                                     const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{});
  10903                                     errdefer msg.destroy(sema.gpa);
  10904                                     try sema.errNote(block, case_src, msg, "pointer type child '{}' cannot cast into resolved pointer type child '{}'", .{
  10905                                         field_ty.fmt(sema.mod),
  10906                                         capture_ty.fmt(sema.mod),
  10907                                     });
  10908                                     try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{});
  10909                                     break :msg msg;
  10910                                 };
  10911                                 return sema.failWithOwnedErrorMsg(block, msg);
  10912                             }
  10913                         }
  10914                     }
  10915 
  10916                     if (try sema.resolveDefinedValue(block, operand_src, spa.operand_ptr)) |op_ptr_val| {
  10917                         if (op_ptr_val.isUndef(mod)) return mod.undefRef(capture_ptr_ty);
  10918                         return Air.internedToRef((try mod.intern(.{ .ptr = .{
  10919                             .ty = capture_ptr_ty.toIntern(),
  10920                             .addr = .{ .field = .{
  10921                                 .base = op_ptr_val.toIntern(),
  10922                                 .index = first_field_index,
  10923                             } },
  10924                         } })));
  10925                     }
  10926 
  10927                     try sema.requireRuntimeBlock(block, operand_src, null);
  10928                     return block.addStructFieldPtr(spa.operand_ptr, first_field_index, capture_ptr_ty);
  10929                 }
  10930 
  10931                 if (try sema.resolveDefinedValue(block, operand_src, spa.operand)) |operand_val| {
  10932                     if (operand_val.isUndef(mod)) return mod.undefRef(capture_ty);
  10933                     const union_val = ip.indexToKey(operand_val.toIntern()).un;
  10934                     if (Value.fromInterned(union_val.tag).isUndef(mod)) return mod.undefRef(capture_ty);
  10935                     const uncoerced = Air.internedToRef(union_val.val);
  10936                     return sema.coerce(block, capture_ty, uncoerced, operand_src);
  10937                 }
  10938 
  10939                 try sema.requireRuntimeBlock(block, operand_src, null);
  10940 
  10941                 if (same_types) {
  10942                     return block.addStructFieldVal(spa.operand, first_field_index, capture_ty);
  10943                 }
  10944 
  10945                 // We may have to emit a switch block which coerces the operand to the capture type.
  10946                 // If we can, try to avoid that using in-memory coercions.
  10947                 const first_non_imc = in_mem: {
  10948                     for (field_indices, 0..) |field_idx, i| {
  10949                         const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10950                         if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
  10951                             break :in_mem i;
  10952                         }
  10953                     }
  10954                     // All fields are in-memory coercible to the resolved type!
  10955                     // Just take the first field and bitcast the result.
  10956                     const uncoerced = try block.addStructFieldVal(spa.operand, first_field_index, first_field_ty);
  10957                     return block.addBitCast(capture_ty, uncoerced);
  10958                 };
  10959 
  10960                 // By-val capture with heterogeneous types which are not all in-memory coercible to
  10961                 // the resolved capture type. We finally have to fall back to the ugly method.
  10962 
  10963                 // However, let's first track which operands are in-memory coercible. There may well
  10964                 // be several, and we can squash all of these cases into the same switch prong using
  10965                 // a simple bitcast. We'll make this the 'else' prong.
  10966 
  10967                 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_indices.len);
  10968                 in_mem_coercible.unset(first_non_imc);
  10969                 {
  10970                     const next = first_non_imc + 1;
  10971                     for (field_indices[next..], next..) |field_idx, i| {
  10972                         const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10973                         if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
  10974                             in_mem_coercible.unset(i);
  10975                         }
  10976                     }
  10977                 }
  10978 
  10979                 const capture_block_inst = try block.addInstAsIndex(.{
  10980                     .tag = .block,
  10981                     .data = .{
  10982                         .ty_pl = .{
  10983                             .ty = Air.internedToRef(capture_ty.toIntern()),
  10984                             .payload = undefined, // updated below
  10985                         },
  10986                     },
  10987                 });
  10988 
  10989                 const prong_count = field_indices.len - in_mem_coercible.count();
  10990 
  10991                 const estimated_extra = prong_count * 6; // 2 for Case, 1 item, probably 3 insts
  10992                 var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra);
  10993                 defer cases_extra.deinit();
  10994 
  10995                 {
  10996                     // Non-bitcast cases
  10997                     var it = in_mem_coercible.iterator(.{ .kind = .unset });
  10998                     while (it.next()) |idx| {
  10999                         var coerce_block = block.makeSubBlock();
  11000                         defer coerce_block.instructions.deinit(sema.gpa);
  11001 
  11002                         const field_idx = field_indices[idx];
  11003                         const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);
  11004                         const uncoerced = try coerce_block.addStructFieldVal(spa.operand, field_idx, field_ty);
  11005                         const coerced = sema.coerce(&coerce_block, capture_ty, uncoerced, .unneeded) catch |err| switch (err) {
  11006                             error.NeededSourceLocation => {
  11007                                 const multi_idx = raw_capture_src.multi_capture;
  11008                                 const src_decl_ptr = sema.mod.declPtr(block.src_decl);
  11009                                 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(idx) } };
  11010                                 const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
  11011                                 _ = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);
  11012                                 unreachable;
  11013                             },
  11014                             else => |e| return e,
  11015                         };
  11016                         _ = try coerce_block.addBr(capture_block_inst, coerced);
  11017 
  11018                         try cases_extra.ensureUnusedCapacity(3 + coerce_block.instructions.items.len);
  11019                         cases_extra.appendAssumeCapacity(1); // items_len
  11020                         cases_extra.appendAssumeCapacity(@intCast(coerce_block.instructions.items.len)); // body_len
  11021                         cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item
  11022                         cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body
  11023                     }
  11024                 }
  11025                 const else_body_len = len: {
  11026                     // 'else' prong uses a bitcast
  11027                     var coerce_block = block.makeSubBlock();
  11028                     defer coerce_block.instructions.deinit(sema.gpa);
  11029 
  11030                     const first_imc_item_idx = in_mem_coercible.findFirstSet().?;
  11031                     const first_imc_field_idx = field_indices[first_imc_item_idx];
  11032                     const first_imc_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]);
  11033                     const uncoerced = try coerce_block.addStructFieldVal(spa.operand, first_imc_field_idx, first_imc_field_ty);
  11034                     const coerced = try coerce_block.addBitCast(capture_ty, uncoerced);
  11035                     _ = try coerce_block.addBr(capture_block_inst, coerced);
  11036 
  11037                     try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items));
  11038                     break :len coerce_block.instructions.items.len;
  11039                 };
  11040 
  11041                 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).Struct.fields.len +
  11042                     cases_extra.items.len +
  11043                     @typeInfo(Air.Block).Struct.fields.len +
  11044                     1);
  11045 
  11046                 const switch_br_inst: u32 = @intCast(sema.air_instructions.len);
  11047                 try sema.air_instructions.append(sema.gpa, .{
  11048                     .tag = .switch_br,
  11049                     .data = .{ .pl_op = .{
  11050                         .operand = spa.cond,
  11051                         .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
  11052                             .cases_len = @intCast(prong_count),
  11053                             .else_body_len = @intCast(else_body_len),
  11054                         }),
  11055                     } },
  11056                 });
  11057                 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
  11058 
  11059                 // Set up block body
  11060                 sema.air_instructions.items(.data)[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{
  11061                     .body_len = 1,
  11062                 });
  11063                 sema.air_extra.appendAssumeCapacity(switch_br_inst);
  11064 
  11065                 return capture_block_inst.toRef();
  11066             },
  11067             .ErrorSet => {
  11068                 if (capture_byref) {
  11069                     const capture_src = raw_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none);
  11070                     return sema.fail(
  11071                         block,
  11072                         capture_src,
  11073                         "error set cannot be captured by reference",
  11074                         .{},
  11075                     );
  11076                 }
  11077 
  11078                 if (case_vals.len == 1) {
  11079                     const item_val = sema.resolveConstDefinedValue(block, .unneeded, case_vals[0], undefined) catch unreachable;
  11080                     const item_ty = try mod.singleErrorSetType(item_val.getErrorName(mod).unwrap().?);
  11081                     return sema.bitCast(block, item_ty, spa.operand, operand_src, null);
  11082                 }
  11083 
  11084                 var names: InferredErrorSet.NameMap = .{};
  11085                 try names.ensureUnusedCapacity(sema.arena, case_vals.len);
  11086                 for (case_vals) |err| {
  11087                     const err_val = sema.resolveConstDefinedValue(block, .unneeded, err, undefined) catch unreachable;
  11088                     names.putAssumeCapacityNoClobber(err_val.getErrorName(mod).unwrap().?, {});
  11089                 }
  11090                 const error_ty = try mod.errorSetFromUnsortedNames(names.keys());
  11091                 return sema.bitCast(block, error_ty, spa.operand, operand_src, null);
  11092             },
  11093             else => {
  11094                 // In this case the capture value is just the passed-through value
  11095                 // of the switch condition.
  11096                 if (capture_byref) {
  11097                     return spa.operand_ptr;
  11098                 } else {
  11099                     return spa.operand;
  11100                 }
  11101             },
  11102         }
  11103     }
  11104 };
  11105 
  11106 fn switchCond(
  11107     sema: *Sema,
  11108     block: *Block,
  11109     src: LazySrcLoc,
  11110     operand: Air.Inst.Ref,
  11111 ) CompileError!Air.Inst.Ref {
  11112     const mod = sema.mod;
  11113     const operand_ty = sema.typeOf(operand);
  11114     switch (operand_ty.zigTypeTag(mod)) {
  11115         .Type,
  11116         .Void,
  11117         .Bool,
  11118         .Int,
  11119         .Float,
  11120         .ComptimeFloat,
  11121         .ComptimeInt,
  11122         .EnumLiteral,
  11123         .Pointer,
  11124         .Fn,
  11125         .ErrorSet,
  11126         .Enum,
  11127         => {
  11128             if (operand_ty.isSlice(mod)) {
  11129                 return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(mod)});
  11130             }
  11131             if ((try sema.typeHasOnePossibleValue(operand_ty))) |opv| {
  11132                 return Air.internedToRef(opv.toIntern());
  11133             }
  11134             return operand;
  11135         },
  11136 
  11137         .Union => {
  11138             try sema.resolveTypeFields(operand_ty);
  11139             const enum_ty = operand_ty.unionTagType(mod) orelse {
  11140                 const msg = msg: {
  11141                     const msg = try sema.errMsg(block, src, "switch on union with no attached enum", .{});
  11142                     errdefer msg.destroy(sema.gpa);
  11143                     if (operand_ty.declSrcLocOrNull(mod)) |union_src| {
  11144                         try mod.errNoteNonLazy(union_src, msg, "consider 'union(enum)' here", .{});
  11145                     }
  11146                     break :msg msg;
  11147                 };
  11148                 return sema.failWithOwnedErrorMsg(block, msg);
  11149             };
  11150             return sema.unionToTag(block, enum_ty, operand, src);
  11151         },
  11152 
  11153         .ErrorUnion,
  11154         .NoReturn,
  11155         .Array,
  11156         .Struct,
  11157         .Undefined,
  11158         .Null,
  11159         .Optional,
  11160         .Opaque,
  11161         .Vector,
  11162         .Frame,
  11163         .AnyFrame,
  11164         => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(mod)}),
  11165     }
  11166 }
  11167 
  11168 const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, Module.SwitchProngSrc);
  11169 
  11170 fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref {
  11171     const tracy = trace(@src());
  11172     defer tracy.end();
  11173 
  11174     const mod = sema.mod;
  11175     const gpa = sema.gpa;
  11176     const ip = &mod.intern_pool;
  11177     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  11178     const src = inst_data.src();
  11179     const src_node_offset = inst_data.src_node;
  11180     const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
  11181     const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
  11182     const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
  11183 
  11184     const raw_operand_val: Air.Inst.Ref, const raw_operand_ptr: Air.Inst.Ref = blk: {
  11185         const maybe_ptr = try sema.resolveInst(extra.data.operand);
  11186         if (operand_is_ref) {
  11187             const val = try sema.analyzeLoad(block, src, maybe_ptr, operand_src);
  11188             break :blk .{ val, maybe_ptr };
  11189         } else {
  11190             break :blk .{ maybe_ptr, undefined };
  11191         }
  11192     };
  11193 
  11194     const operand = try sema.switchCond(block, operand_src, raw_operand_val);
  11195 
  11196     // AstGen guarantees that the instruction immediately preceding
  11197     // switch_block(_ref) is a dbg_stmt
  11198     const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
  11199 
  11200     var header_extra_index: usize = extra.end;
  11201 
  11202     const scalar_cases_len = extra.data.bits.scalar_cases_len;
  11203     const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
  11204         const multi_cases_len = sema.code.extra[header_extra_index];
  11205         header_extra_index += 1;
  11206         break :blk multi_cases_len;
  11207     } else 0;
  11208 
  11209     const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
  11210         const tag_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
  11211         header_extra_index += 1;
  11212         // SwitchProngAnalysis wants inst_map to have space for the tag capture.
  11213         // Note that the normal capture is referred to via the switch block
  11214         // index, which there is already necessarily space for.
  11215         try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst});
  11216         break :blk tag_capture_inst;
  11217     } else undefined;
  11218 
  11219     var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
  11220     defer case_vals.deinit(gpa);
  11221 
  11222     const Special = struct {
  11223         body: []const Zir.Inst.Index,
  11224         end: usize,
  11225         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  11226         is_inline: bool,
  11227         has_tag_capture: bool,
  11228     };
  11229 
  11230     const special_prong = extra.data.bits.specialProng();
  11231     const special: Special = switch (special_prong) {
  11232         .none => .{
  11233             .body = &.{},
  11234             .end = header_extra_index,
  11235             .capture = .none,
  11236             .is_inline = false,
  11237             .has_tag_capture = false,
  11238         },
  11239         .under, .@"else" => blk: {
  11240             const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
  11241             const extra_body_start = header_extra_index + 1;
  11242             break :blk .{
  11243                 .body = sema.code.bodySlice(extra_body_start, info.body_len),
  11244                 .end = extra_body_start + info.body_len,
  11245                 .capture = info.capture,
  11246                 .is_inline = info.is_inline,
  11247                 .has_tag_capture = info.has_tag_capture,
  11248             };
  11249         },
  11250     };
  11251 
  11252     const maybe_union_ty = sema.typeOf(raw_operand_val);
  11253     const union_originally = maybe_union_ty.zigTypeTag(mod) == .Union;
  11254 
  11255     // Duplicate checking variables later also used for `inline else`.
  11256     var seen_enum_fields: []?Module.SwitchProngSrc = &.{};
  11257     var seen_errors = SwitchErrorSet.init(gpa);
  11258     var range_set = RangeSet.init(gpa, mod);
  11259     var true_count: u8 = 0;
  11260     var false_count: u8 = 0;
  11261 
  11262     defer {
  11263         range_set.deinit();
  11264         gpa.free(seen_enum_fields);
  11265         seen_errors.deinit();
  11266     }
  11267 
  11268     var empty_enum = false;
  11269 
  11270     const operand_ty = sema.typeOf(operand);
  11271     const err_set = operand_ty.zigTypeTag(mod) == .ErrorSet;
  11272 
  11273     var else_error_ty: ?Type = null;
  11274 
  11275     // Validate usage of '_' prongs.
  11276     if (special_prong == .under and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) {
  11277         const msg = msg: {
  11278             const msg = try sema.errMsg(
  11279                 block,
  11280                 src,
  11281                 "'_' prong only allowed when switching on non-exhaustive enums",
  11282                 .{},
  11283             );
  11284             errdefer msg.destroy(gpa);
  11285             try sema.errNote(
  11286                 block,
  11287                 special_prong_src,
  11288                 msg,
  11289                 "'_' prong here",
  11290                 .{},
  11291             );
  11292             try sema.errNote(
  11293                 block,
  11294                 src,
  11295                 msg,
  11296                 "consider using 'else'",
  11297                 .{},
  11298             );
  11299             break :msg msg;
  11300         };
  11301         return sema.failWithOwnedErrorMsg(block, msg);
  11302     }
  11303 
  11304     // Validate for duplicate items, missing else prong, and invalid range.
  11305     switch (operand_ty.zigTypeTag(mod)) {
  11306         .Union => unreachable, // handled in zirSwitchCond
  11307         .Enum => {
  11308             seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount(mod));
  11309             empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum(mod);
  11310             @memset(seen_enum_fields, null);
  11311             // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
  11312 
  11313             var extra_index: usize = special.end;
  11314             {
  11315                 var scalar_i: u32 = 0;
  11316                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11317                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11318                     extra_index += 1;
  11319                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11320                     extra_index += 1 + info.body_len;
  11321 
  11322                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
  11323                         block,
  11324                         seen_enum_fields,
  11325                         &range_set,
  11326                         item_ref,
  11327                         operand_ty,
  11328                         src_node_offset,
  11329                         .{ .scalar = scalar_i },
  11330                     ));
  11331                 }
  11332             }
  11333             {
  11334                 var multi_i: u32 = 0;
  11335                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11336                     const items_len = sema.code.extra[extra_index];
  11337                     extra_index += 1;
  11338                     const ranges_len = sema.code.extra[extra_index];
  11339                     extra_index += 1;
  11340                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11341                     extra_index += 1;
  11342                     const items = sema.code.refSlice(extra_index, items_len);
  11343                     extra_index += items_len + info.body_len;
  11344 
  11345                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11346                     for (items, 0..) |item_ref, item_i| {
  11347                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
  11348                             block,
  11349                             seen_enum_fields,
  11350                             &range_set,
  11351                             item_ref,
  11352                             operand_ty,
  11353                             src_node_offset,
  11354                             .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } },
  11355                         ));
  11356                     }
  11357 
  11358                     try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
  11359                 }
  11360             }
  11361             const all_tags_handled = for (seen_enum_fields) |seen_src| {
  11362                 if (seen_src == null) break false;
  11363             } else true;
  11364 
  11365             if (special_prong == .@"else") {
  11366                 if (all_tags_handled and !operand_ty.isNonexhaustiveEnum(mod)) return sema.fail(
  11367                     block,
  11368                     special_prong_src,
  11369                     "unreachable else prong; all cases already handled",
  11370                     .{},
  11371                 );
  11372             } else if (!all_tags_handled) {
  11373                 const msg = msg: {
  11374                     const msg = try sema.errMsg(
  11375                         block,
  11376                         src,
  11377                         "switch must handle all possibilities",
  11378                         .{},
  11379                     );
  11380                     errdefer msg.destroy(sema.gpa);
  11381                     for (seen_enum_fields, 0..) |seen_src, i| {
  11382                         if (seen_src != null) continue;
  11383 
  11384                         const field_name = operand_ty.enumFieldName(i, mod);
  11385                         try sema.addFieldErrNote(
  11386                             operand_ty,
  11387                             i,
  11388                             msg,
  11389                             "unhandled enumeration value: '{}'",
  11390                             .{field_name.fmt(&mod.intern_pool)},
  11391                         );
  11392                     }
  11393                     try mod.errNoteNonLazy(
  11394                         operand_ty.declSrcLoc(mod),
  11395                         msg,
  11396                         "enum '{}' declared here",
  11397                         .{operand_ty.fmt(mod)},
  11398                     );
  11399                     break :msg msg;
  11400                 };
  11401                 return sema.failWithOwnedErrorMsg(block, msg);
  11402             } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum(mod) and !union_originally) {
  11403                 return sema.fail(
  11404                     block,
  11405                     src,
  11406                     "switch on non-exhaustive enum must include 'else' or '_' prong",
  11407                     .{},
  11408                 );
  11409             }
  11410         },
  11411         .ErrorSet => {
  11412             var extra_index: usize = special.end;
  11413             {
  11414                 var scalar_i: u32 = 0;
  11415                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11416                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11417                     extra_index += 1;
  11418                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11419                     extra_index += 1 + info.body_len;
  11420 
  11421                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
  11422                         block,
  11423                         &seen_errors,
  11424                         item_ref,
  11425                         operand_ty,
  11426                         src_node_offset,
  11427                         .{ .scalar = scalar_i },
  11428                     ));
  11429                 }
  11430             }
  11431             {
  11432                 var multi_i: u32 = 0;
  11433                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11434                     const items_len = sema.code.extra[extra_index];
  11435                     extra_index += 1;
  11436                     const ranges_len = sema.code.extra[extra_index];
  11437                     extra_index += 1;
  11438                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11439                     extra_index += 1;
  11440                     const items = sema.code.refSlice(extra_index, items_len);
  11441                     extra_index += items_len + info.body_len;
  11442 
  11443                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11444                     for (items, 0..) |item_ref, item_i| {
  11445                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
  11446                             block,
  11447                             &seen_errors,
  11448                             item_ref,
  11449                             operand_ty,
  11450                             src_node_offset,
  11451                             .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } },
  11452                         ));
  11453                     }
  11454 
  11455                     try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
  11456                 }
  11457             }
  11458 
  11459             switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) {
  11460                 .anyerror_type => {
  11461                     if (special_prong != .@"else") {
  11462                         return sema.fail(
  11463                             block,
  11464                             src,
  11465                             "else prong required when switching on type 'anyerror'",
  11466                             .{},
  11467                         );
  11468                     }
  11469                     else_error_ty = Type.anyerror;
  11470                 },
  11471                 else => |err_set_ty_index| else_validation: {
  11472                     const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names;
  11473                     var maybe_msg: ?*Module.ErrorMsg = null;
  11474                     errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
  11475 
  11476                     for (error_names.get(ip)) |error_name| {
  11477                         if (!seen_errors.contains(error_name) and special_prong != .@"else") {
  11478                             const msg = maybe_msg orelse blk: {
  11479                                 maybe_msg = try sema.errMsg(
  11480                                     block,
  11481                                     src,
  11482                                     "switch must handle all possibilities",
  11483                                     .{},
  11484                                 );
  11485                                 break :blk maybe_msg.?;
  11486                             };
  11487 
  11488                             try sema.errNote(
  11489                                 block,
  11490                                 src,
  11491                                 msg,
  11492                                 "unhandled error value: 'error.{}'",
  11493                                 .{error_name.fmt(ip)},
  11494                             );
  11495                         }
  11496                     }
  11497 
  11498                     if (maybe_msg) |msg| {
  11499                         maybe_msg = null;
  11500                         try sema.addDeclaredHereNote(msg, operand_ty);
  11501                         return sema.failWithOwnedErrorMsg(block, msg);
  11502                     }
  11503 
  11504                     if (special_prong == .@"else" and
  11505                         seen_errors.count() == error_names.len)
  11506                     {
  11507                         // In order to enable common patterns for generic code allow simple else bodies
  11508                         // else => unreachable,
  11509                         // else => return,
  11510                         // else => |e| return e,
  11511                         // even if all the possible errors were already handled.
  11512                         const tags = sema.code.instructions.items(.tag);
  11513                         for (special.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) {
  11514                             .dbg_block_begin,
  11515                             .dbg_block_end,
  11516                             .dbg_stmt,
  11517                             .dbg_var_val,
  11518                             .ret_type,
  11519                             .as_node,
  11520                             .ret_node,
  11521                             .@"unreachable",
  11522                             .@"defer",
  11523                             .defer_err_code,
  11524                             .err_union_code,
  11525                             .ret_err_value_code,
  11526                             .restore_err_ret_index,
  11527                             .is_non_err,
  11528                             .ret_is_non_err,
  11529                             .condbr,
  11530                             => {},
  11531                             else => break,
  11532                         } else break :else_validation;
  11533 
  11534                         return sema.fail(
  11535                             block,
  11536                             special_prong_src,
  11537                             "unreachable else prong; all cases already handled",
  11538                             .{},
  11539                         );
  11540                     }
  11541 
  11542                     var names: InferredErrorSet.NameMap = .{};
  11543                     try names.ensureUnusedCapacity(sema.arena, error_names.len);
  11544                     for (error_names.get(ip)) |error_name| {
  11545                         if (seen_errors.contains(error_name)) continue;
  11546 
  11547                         names.putAssumeCapacityNoClobber(error_name, {});
  11548                     }
  11549                     // No need to keep the hash map metadata correct; here we
  11550                     // extract the (sorted) keys only.
  11551                     else_error_ty = try mod.errorSetFromUnsortedNames(names.keys());
  11552                 },
  11553             }
  11554         },
  11555         .Int, .ComptimeInt => {
  11556             var extra_index: usize = special.end;
  11557             {
  11558                 var scalar_i: u32 = 0;
  11559                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11560                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11561                     extra_index += 1;
  11562                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11563                     extra_index += 1 + info.body_len;
  11564 
  11565                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
  11566                         block,
  11567                         &range_set,
  11568                         item_ref,
  11569                         operand_ty,
  11570                         src_node_offset,
  11571                         .{ .scalar = scalar_i },
  11572                     ));
  11573                 }
  11574             }
  11575             {
  11576                 var multi_i: u32 = 0;
  11577                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11578                     const items_len = sema.code.extra[extra_index];
  11579                     extra_index += 1;
  11580                     const ranges_len = sema.code.extra[extra_index];
  11581                     extra_index += 1;
  11582                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11583                     extra_index += 1;
  11584                     const items = sema.code.refSlice(extra_index, items_len);
  11585                     extra_index += items_len;
  11586 
  11587                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11588                     for (items, 0..) |item_ref, item_i| {
  11589                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
  11590                             block,
  11591                             &range_set,
  11592                             item_ref,
  11593                             operand_ty,
  11594                             src_node_offset,
  11595                             .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } },
  11596                         ));
  11597                     }
  11598 
  11599                     try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len);
  11600                     var range_i: u32 = 0;
  11601                     while (range_i < ranges_len) : (range_i += 1) {
  11602                         const item_first: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11603                         extra_index += 1;
  11604                         const item_last: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11605                         extra_index += 1;
  11606 
  11607                         const vals = try sema.validateSwitchRange(
  11608                             block,
  11609                             &range_set,
  11610                             item_first,
  11611                             item_last,
  11612                             operand_ty,
  11613                             src_node_offset,
  11614                             .{ .range = .{ .prong = multi_i, .item = range_i } },
  11615                         );
  11616                         case_vals.appendAssumeCapacity(vals[0]);
  11617                         case_vals.appendAssumeCapacity(vals[1]);
  11618                     }
  11619 
  11620                     extra_index += info.body_len;
  11621                 }
  11622             }
  11623 
  11624             check_range: {
  11625                 if (operand_ty.zigTypeTag(mod) == .Int) {
  11626                     const min_int = try operand_ty.minInt(mod, operand_ty);
  11627                     const max_int = try operand_ty.maxInt(mod, operand_ty);
  11628                     if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {
  11629                         if (special_prong == .@"else") {
  11630                             return sema.fail(
  11631                                 block,
  11632                                 special_prong_src,
  11633                                 "unreachable else prong; all cases already handled",
  11634                                 .{},
  11635                             );
  11636                         }
  11637                         break :check_range;
  11638                     }
  11639                 }
  11640                 if (special_prong != .@"else") {
  11641                     return sema.fail(
  11642                         block,
  11643                         src,
  11644                         "switch must handle all possibilities",
  11645                         .{},
  11646                     );
  11647                 }
  11648             }
  11649         },
  11650         .Bool => {
  11651             var extra_index: usize = special.end;
  11652             {
  11653                 var scalar_i: u32 = 0;
  11654                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11655                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11656                     extra_index += 1;
  11657                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11658                     extra_index += 1 + info.body_len;
  11659 
  11660                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
  11661                         block,
  11662                         &true_count,
  11663                         &false_count,
  11664                         item_ref,
  11665                         src_node_offset,
  11666                         .{ .scalar = scalar_i },
  11667                     ));
  11668                 }
  11669             }
  11670             {
  11671                 var multi_i: u32 = 0;
  11672                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11673                     const items_len = sema.code.extra[extra_index];
  11674                     extra_index += 1;
  11675                     const ranges_len = sema.code.extra[extra_index];
  11676                     extra_index += 1;
  11677                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11678                     extra_index += 1;
  11679                     const items = sema.code.refSlice(extra_index, items_len);
  11680                     extra_index += items_len + info.body_len;
  11681 
  11682                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11683                     for (items, 0..) |item_ref, item_i| {
  11684                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
  11685                             block,
  11686                             &true_count,
  11687                             &false_count,
  11688                             item_ref,
  11689                             src_node_offset,
  11690                             .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } },
  11691                         ));
  11692                     }
  11693 
  11694                     try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
  11695                 }
  11696             }
  11697             switch (special_prong) {
  11698                 .@"else" => {
  11699                     if (true_count + false_count == 2) {
  11700                         return sema.fail(
  11701                             block,
  11702                             special_prong_src,
  11703                             "unreachable else prong; all cases already handled",
  11704                             .{},
  11705                         );
  11706                     }
  11707                 },
  11708                 .under, .none => {
  11709                     if (true_count + false_count < 2) {
  11710                         return sema.fail(
  11711                             block,
  11712                             src,
  11713                             "switch must handle all possibilities",
  11714                             .{},
  11715                         );
  11716                     }
  11717                 },
  11718             }
  11719         },
  11720         .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
  11721             if (special_prong != .@"else") {
  11722                 return sema.fail(
  11723                     block,
  11724                     src,
  11725                     "else prong required when switching on type '{}'",
  11726                     .{operand_ty.fmt(mod)},
  11727                 );
  11728             }
  11729 
  11730             var seen_values = ValueSrcMap{};
  11731             defer seen_values.deinit(gpa);
  11732 
  11733             var extra_index: usize = special.end;
  11734             {
  11735                 var scalar_i: u32 = 0;
  11736                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11737                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11738                     extra_index += 1;
  11739                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11740                     extra_index += 1;
  11741                     extra_index += info.body_len;
  11742 
  11743                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
  11744                         block,
  11745                         &seen_values,
  11746                         item_ref,
  11747                         operand_ty,
  11748                         src_node_offset,
  11749                         .{ .scalar = scalar_i },
  11750                     ));
  11751                 }
  11752             }
  11753             {
  11754                 var multi_i: u32 = 0;
  11755                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11756                     const items_len = sema.code.extra[extra_index];
  11757                     extra_index += 1;
  11758                     const ranges_len = sema.code.extra[extra_index];
  11759                     extra_index += 1;
  11760                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11761                     extra_index += 1;
  11762                     const items = sema.code.refSlice(extra_index, items_len);
  11763                     extra_index += items_len + info.body_len;
  11764 
  11765                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11766                     for (items, 0..) |item_ref, item_i| {
  11767                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
  11768                             block,
  11769                             &seen_values,
  11770                             item_ref,
  11771                             operand_ty,
  11772                             src_node_offset,
  11773                             .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } },
  11774                         ));
  11775                     }
  11776 
  11777                     try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
  11778                 }
  11779             }
  11780         },
  11781 
  11782         .ErrorUnion,
  11783         .NoReturn,
  11784         .Array,
  11785         .Struct,
  11786         .Undefined,
  11787         .Null,
  11788         .Optional,
  11789         .Opaque,
  11790         .Vector,
  11791         .Frame,
  11792         .AnyFrame,
  11793         .ComptimeFloat,
  11794         .Float,
  11795         => return sema.fail(block, operand_src, "invalid switch operand type '{}'", .{
  11796             operand_ty.fmt(mod),
  11797         }),
  11798     }
  11799 
  11800     const spa: SwitchProngAnalysis = .{
  11801         .sema = sema,
  11802         .parent_block = block,
  11803         .operand = raw_operand_val,
  11804         .operand_ptr = raw_operand_ptr,
  11805         .cond = operand,
  11806         .else_error_ty = else_error_ty,
  11807         .switch_block_inst = inst,
  11808         .tag_capture_inst = tag_capture_inst,
  11809     };
  11810 
  11811     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  11812     try sema.air_instructions.append(gpa, .{
  11813         .tag = .block,
  11814         .data = undefined,
  11815     });
  11816     var label: Block.Label = .{
  11817         .zir_block = inst,
  11818         .merges = .{
  11819             .src_locs = .{},
  11820             .results = .{},
  11821             .br_list = .{},
  11822             .block_inst = block_inst,
  11823         },
  11824     };
  11825 
  11826     var child_block: Block = .{
  11827         .parent = block,
  11828         .sema = sema,
  11829         .src_decl = block.src_decl,
  11830         .namespace = block.namespace,
  11831         .wip_capture_scope = block.wip_capture_scope,
  11832         .instructions = .{},
  11833         .label = &label,
  11834         .inlining = block.inlining,
  11835         .is_comptime = block.is_comptime,
  11836         .comptime_reason = block.comptime_reason,
  11837         .is_typeof = block.is_typeof,
  11838         .c_import_buf = block.c_import_buf,
  11839         .runtime_cond = block.runtime_cond,
  11840         .runtime_loop = block.runtime_loop,
  11841         .runtime_index = block.runtime_index,
  11842         .want_safety = block.want_safety,
  11843         .error_return_trace_index = block.error_return_trace_index,
  11844     };
  11845     const merges = &child_block.label.?.merges;
  11846     defer child_block.instructions.deinit(gpa);
  11847     defer merges.deinit(gpa);
  11848 
  11849     if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| {
  11850         const resolved_operand_val = try sema.resolveLazyValue(operand_val);
  11851         var extra_index: usize = special.end;
  11852         {
  11853             var scalar_i: usize = 0;
  11854             while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11855                 extra_index += 1;
  11856                 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11857                 extra_index += 1;
  11858                 const body = sema.code.bodySlice(extra_index, info.body_len);
  11859                 extra_index += info.body_len;
  11860 
  11861                 const item = case_vals.items[scalar_i];
  11862                 const item_val = sema.resolveConstDefinedValue(&child_block, .unneeded, item, undefined) catch unreachable;
  11863                 if (operand_val.eql(item_val, operand_ty, sema.mod)) {
  11864                     if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
  11865                     return spa.resolveProngComptime(
  11866                         &child_block,
  11867                         .normal,
  11868                         body,
  11869                         info.capture,
  11870                         .{ .scalar_capture = @intCast(scalar_i) },
  11871                         &.{item},
  11872                         if (info.is_inline) operand else .none,
  11873                         info.has_tag_capture,
  11874                         merges,
  11875                     );
  11876                 }
  11877             }
  11878         }
  11879         {
  11880             var multi_i: usize = 0;
  11881             var case_val_idx: usize = scalar_cases_len;
  11882             while (multi_i < multi_cases_len) : (multi_i += 1) {
  11883                 const items_len = sema.code.extra[extra_index];
  11884                 extra_index += 1;
  11885                 const ranges_len = sema.code.extra[extra_index];
  11886                 extra_index += 1;
  11887                 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11888                 extra_index += 1 + items_len;
  11889                 const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len);
  11890 
  11891                 const items = case_vals.items[case_val_idx..][0..items_len];
  11892                 case_val_idx += items_len;
  11893 
  11894                 for (items) |item| {
  11895                     // Validation above ensured these will succeed.
  11896                     const item_val = sema.resolveConstDefinedValue(&child_block, .unneeded, item, undefined) catch unreachable;
  11897                     if (operand_val.eql(item_val, operand_ty, sema.mod)) {
  11898                         if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
  11899                         return spa.resolveProngComptime(
  11900                             &child_block,
  11901                             .normal,
  11902                             body,
  11903                             info.capture,
  11904                             .{ .multi_capture = @intCast(multi_i) },
  11905                             items,
  11906                             if (info.is_inline) operand else .none,
  11907                             info.has_tag_capture,
  11908                             merges,
  11909                         );
  11910                     }
  11911                 }
  11912 
  11913                 var range_i: usize = 0;
  11914                 while (range_i < ranges_len) : (range_i += 1) {
  11915                     const range_items = case_vals.items[case_val_idx..][0..2];
  11916                     extra_index += 2;
  11917                     case_val_idx += 2;
  11918 
  11919                     // Validation above ensured these will succeed.
  11920                     const first_val = sema.resolveConstDefinedValue(&child_block, .unneeded, range_items[0], undefined) catch unreachable;
  11921                     const last_val = sema.resolveConstDefinedValue(&child_block, .unneeded, range_items[1], undefined) catch unreachable;
  11922                     if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and
  11923                         (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty)))
  11924                     {
  11925                         if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
  11926                         return spa.resolveProngComptime(
  11927                             &child_block,
  11928                             .normal,
  11929                             body,
  11930                             info.capture,
  11931                             .{ .multi_capture = @intCast(multi_i) },
  11932                             undefined, // case_vals may be undefined for ranges
  11933                             if (info.is_inline) operand else .none,
  11934                             info.has_tag_capture,
  11935                             merges,
  11936                         );
  11937                     }
  11938                 }
  11939 
  11940                 extra_index += info.body_len;
  11941             }
  11942         }
  11943         if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, special.body, operand);
  11944         if (empty_enum) {
  11945             return .void_value;
  11946         }
  11947 
  11948         return spa.resolveProngComptime(
  11949             &child_block,
  11950             .special,
  11951             special.body,
  11952             special.capture,
  11953             .special_capture,
  11954             undefined, // case_vals may be undefined for special prongs
  11955             if (special.is_inline) operand else .none,
  11956             special.has_tag_capture,
  11957             merges,
  11958         );
  11959     }
  11960 
  11961     if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
  11962         if (empty_enum) {
  11963             return .void_value;
  11964         }
  11965         if (special_prong == .none) {
  11966             return sema.fail(block, src, "switch must handle all possibilities", .{});
  11967         }
  11968         if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand, operand_src)) {
  11969             return .unreachable_value;
  11970         }
  11971         if (mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and
  11972             (!operand_ty.isNonexhaustiveEnum(mod) or union_originally))
  11973         {
  11974             try sema.zirDbgStmt(block, cond_dbg_node_index);
  11975             const ok = try block.addUnOp(.is_named_enum_value, operand);
  11976             try sema.addSafetyCheck(block, src, ok, .corrupt_switch);
  11977         }
  11978 
  11979         return spa.resolveProngComptime(
  11980             &child_block,
  11981             .special,
  11982             special.body,
  11983             special.capture,
  11984             .special_capture,
  11985             undefined, // case_vals may be undefined for special prongs
  11986             .none,
  11987             false,
  11988             merges,
  11989         );
  11990     }
  11991 
  11992     if (child_block.is_comptime) {
  11993         _ = try sema.resolveConstDefinedValue(&child_block, operand_src, operand, .{
  11994             .needed_comptime_reason = "condition in comptime switch must be comptime-known",
  11995             .block_comptime_reason = child_block.comptime_reason,
  11996         });
  11997         unreachable;
  11998     }
  11999 
  12000     const estimated_cases_extra = (scalar_cases_len + multi_cases_len) *
  12001         @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2;
  12002     var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra);
  12003     defer cases_extra.deinit(gpa);
  12004 
  12005     var case_block = child_block.makeSubBlock();
  12006     case_block.runtime_loop = null;
  12007     case_block.runtime_cond = operand_src;
  12008     case_block.runtime_index.increment();
  12009     defer case_block.instructions.deinit(gpa);
  12010 
  12011     var extra_index: usize = special.end;
  12012 
  12013     var scalar_i: usize = 0;
  12014     while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  12015         extra_index += 1;
  12016         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  12017         extra_index += 1;
  12018         const body = sema.code.bodySlice(extra_index, info.body_len);
  12019         extra_index += info.body_len;
  12020 
  12021         case_block.instructions.shrinkRetainingCapacity(0);
  12022         case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope);
  12023 
  12024         const item = case_vals.items[scalar_i];
  12025         // `item` is already guaranteed to be constant known.
  12026 
  12027         const analyze_body = if (union_originally) blk: {
  12028             const unresolved_item_val = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch unreachable;
  12029             const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable;
  12030             const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
  12031             break :blk field_ty.zigTypeTag(mod) != .NoReturn;
  12032         } else true;
  12033 
  12034         if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) {
  12035             // nothing to do here
  12036         } else if (analyze_body) {
  12037             try spa.analyzeProngRuntime(
  12038                 &case_block,
  12039                 .normal,
  12040                 body,
  12041                 info.capture,
  12042                 .{ .scalar_capture = @intCast(scalar_i) },
  12043                 &.{item},
  12044                 if (info.is_inline) item else .none,
  12045                 info.has_tag_capture,
  12046             );
  12047         } else {
  12048             _ = try case_block.addNoOp(.unreach);
  12049         }
  12050 
  12051         try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  12052         cases_extra.appendAssumeCapacity(1); // items_len
  12053         cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
  12054         cases_extra.appendAssumeCapacity(@intFromEnum(item));
  12055         cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12056     }
  12057 
  12058     var is_first = true;
  12059     var prev_cond_br: Air.Inst.Index = undefined;
  12060     var first_else_body: []const Air.Inst.Index = &.{};
  12061     defer gpa.free(first_else_body);
  12062     var prev_then_body: []const Air.Inst.Index = &.{};
  12063     defer gpa.free(prev_then_body);
  12064 
  12065     var cases_len = scalar_cases_len;
  12066     var case_val_idx: usize = scalar_cases_len;
  12067     var multi_i: u32 = 0;
  12068     while (multi_i < multi_cases_len) : (multi_i += 1) {
  12069         const items_len = sema.code.extra[extra_index];
  12070         extra_index += 1;
  12071         const ranges_len = sema.code.extra[extra_index];
  12072         extra_index += 1;
  12073         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  12074         extra_index += 1 + items_len;
  12075 
  12076         const items = case_vals.items[case_val_idx..][0..items_len];
  12077         case_val_idx += items_len;
  12078 
  12079         case_block.instructions.shrinkRetainingCapacity(0);
  12080         case_block.wip_capture_scope = child_block.wip_capture_scope;
  12081 
  12082         // Generate all possible cases as scalar prongs.
  12083         if (info.is_inline) {
  12084             const body_start = extra_index + 2 * ranges_len;
  12085             const body = sema.code.bodySlice(body_start, info.body_len);
  12086             var emit_bb = false;
  12087 
  12088             var range_i: u32 = 0;
  12089             while (range_i < ranges_len) : (range_i += 1) {
  12090                 const range_items = case_vals.items[case_val_idx..][0..2];
  12091                 extra_index += 2;
  12092                 case_val_idx += 2;
  12093 
  12094                 const item_first_ref = range_items[0];
  12095                 const item_last_ref = range_items[1];
  12096 
  12097                 var item = sema.resolveConstDefinedValue(block, .unneeded, item_first_ref, undefined) catch unreachable;
  12098                 const item_last = sema.resolveConstDefinedValue(block, .unneeded, item_last_ref, undefined) catch unreachable;
  12099 
  12100                 while (item.compareScalar(.lte, item_last, operand_ty, mod)) : ({
  12101                     // Previous validation has resolved any possible lazy values.
  12102                     item = sema.intAddScalar(item, try mod.intValue(operand_ty, 1), operand_ty) catch |err| switch (err) {
  12103                         error.Overflow => unreachable,
  12104                         else => |e| return e,
  12105                     };
  12106                 }) {
  12107                     cases_len += 1;
  12108 
  12109                     const item_ref = Air.internedToRef(item.toIntern());
  12110 
  12111                     case_block.instructions.shrinkRetainingCapacity(0);
  12112                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  12113 
  12114                     if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
  12115                         error.NeededSourceLocation => {
  12116                             const case_src = Module.SwitchProngSrc{
  12117                                 .range = .{ .prong = multi_i, .item = range_i },
  12118                             };
  12119                             const decl = mod.declPtr(case_block.src_decl);
  12120                             try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none));
  12121                             unreachable;
  12122                         },
  12123                         else => return err,
  12124                     };
  12125                     emit_bb = true;
  12126 
  12127                     try spa.analyzeProngRuntime(
  12128                         &case_block,
  12129                         .normal,
  12130                         body,
  12131                         info.capture,
  12132                         .{ .multi_capture = multi_i },
  12133                         undefined, // case_vals may be undefined for ranges
  12134                         item_ref,
  12135                         info.has_tag_capture,
  12136                     );
  12137 
  12138                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  12139                     cases_extra.appendAssumeCapacity(1); // items_len
  12140                     cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
  12141                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12142                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12143 
  12144                     if (item.compareScalar(.eq, item_last, operand_ty, mod)) break;
  12145                 }
  12146             }
  12147 
  12148             for (items, 0..) |item, item_i| {
  12149                 cases_len += 1;
  12150 
  12151                 case_block.instructions.shrinkRetainingCapacity(0);
  12152                 case_block.wip_capture_scope = child_block.wip_capture_scope;
  12153 
  12154                 const analyze_body = if (union_originally) blk: {
  12155                     const item_val = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch unreachable;
  12156                     const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
  12157                     break :blk field_ty.zigTypeTag(mod) != .NoReturn;
  12158                 } else true;
  12159 
  12160                 if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
  12161                     error.NeededSourceLocation => {
  12162                         const case_src = Module.SwitchProngSrc{
  12163                             .multi = .{ .prong = multi_i, .item = @intCast(item_i) },
  12164                         };
  12165                         const decl = mod.declPtr(case_block.src_decl);
  12166                         try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none));
  12167                         unreachable;
  12168                     },
  12169                     else => return err,
  12170                 };
  12171                 emit_bb = true;
  12172 
  12173                 if (analyze_body) {
  12174                     try spa.analyzeProngRuntime(
  12175                         &case_block,
  12176                         .normal,
  12177                         body,
  12178                         info.capture,
  12179                         .{ .multi_capture = multi_i },
  12180                         &.{item},
  12181                         item,
  12182                         info.has_tag_capture,
  12183                     );
  12184                 } else {
  12185                     _ = try case_block.addNoOp(.unreach);
  12186                 }
  12187 
  12188                 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  12189                 cases_extra.appendAssumeCapacity(1); // items_len
  12190                 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
  12191                 cases_extra.appendAssumeCapacity(@intFromEnum(item));
  12192                 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12193             }
  12194 
  12195             extra_index += info.body_len;
  12196             continue;
  12197         }
  12198 
  12199         var any_ok: Air.Inst.Ref = .none;
  12200 
  12201         // If there are any ranges, we have to put all the items into the
  12202         // else prong. Otherwise, we can take advantage of multiple items
  12203         // mapping to the same body.
  12204         if (ranges_len == 0) {
  12205             cases_len += 1;
  12206 
  12207             const analyze_body = if (union_originally)
  12208                 for (items) |item| {
  12209                     const item_val = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch unreachable;
  12210                     const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
  12211                     if (field_ty.zigTypeTag(mod) != .NoReturn) break true;
  12212                 } else false
  12213             else
  12214                 true;
  12215 
  12216             const body = sema.code.bodySlice(extra_index, info.body_len);
  12217             extra_index += info.body_len;
  12218             if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) {
  12219                 // nothing to do here
  12220             } else if (analyze_body) {
  12221                 try spa.analyzeProngRuntime(
  12222                     &case_block,
  12223                     .normal,
  12224                     body,
  12225                     info.capture,
  12226                     .{ .multi_capture = multi_i },
  12227                     items,
  12228                     .none,
  12229                     false,
  12230                 );
  12231             } else {
  12232                 _ = try case_block.addNoOp(.unreach);
  12233             }
  12234 
  12235             try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len +
  12236                 case_block.instructions.items.len);
  12237 
  12238             cases_extra.appendAssumeCapacity(@intCast(items.len));
  12239             cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
  12240 
  12241             for (items) |item| {
  12242                 cases_extra.appendAssumeCapacity(@intFromEnum(item));
  12243             }
  12244 
  12245             cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12246         } else {
  12247             for (items) |item| {
  12248                 const cmp_ok = try case_block.addBinOp(if (case_block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, item);
  12249                 if (any_ok != .none) {
  12250                     any_ok = try case_block.addBinOp(.bool_or, any_ok, cmp_ok);
  12251                 } else {
  12252                     any_ok = cmp_ok;
  12253                 }
  12254             }
  12255 
  12256             var range_i: usize = 0;
  12257             while (range_i < ranges_len) : (range_i += 1) {
  12258                 const range_items = case_vals.items[case_val_idx..][0..2];
  12259                 extra_index += 2;
  12260                 case_val_idx += 2;
  12261 
  12262                 const item_first = range_items[0];
  12263                 const item_last = range_items[1];
  12264 
  12265                 // operand >= first and operand <= last
  12266                 const range_first_ok = try case_block.addBinOp(
  12267                     if (case_block.float_mode == .Optimized) .cmp_gte_optimized else .cmp_gte,
  12268                     operand,
  12269                     item_first,
  12270                 );
  12271                 const range_last_ok = try case_block.addBinOp(
  12272                     if (case_block.float_mode == .Optimized) .cmp_lte_optimized else .cmp_lte,
  12273                     operand,
  12274                     item_last,
  12275                 );
  12276                 const range_ok = try case_block.addBinOp(
  12277                     .bool_and,
  12278                     range_first_ok,
  12279                     range_last_ok,
  12280                 );
  12281                 if (any_ok != .none) {
  12282                     any_ok = try case_block.addBinOp(.bool_or, any_ok, range_ok);
  12283                 } else {
  12284                     any_ok = range_ok;
  12285                 }
  12286             }
  12287 
  12288             const new_cond_br = try case_block.addInstAsIndex(.{ .tag = .cond_br, .data = .{
  12289                 .pl_op = .{
  12290                     .operand = any_ok,
  12291                     .payload = undefined,
  12292                 },
  12293             } });
  12294             var cond_body = try case_block.instructions.toOwnedSlice(gpa);
  12295             defer gpa.free(cond_body);
  12296 
  12297             case_block.instructions.shrinkRetainingCapacity(0);
  12298             case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope);
  12299 
  12300             const body = sema.code.bodySlice(extra_index, info.body_len);
  12301             extra_index += info.body_len;
  12302             if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) {
  12303                 // nothing to do here
  12304             } else {
  12305                 try spa.analyzeProngRuntime(
  12306                     &case_block,
  12307                     .normal,
  12308                     body,
  12309                     info.capture,
  12310                     .{ .multi_capture = multi_i },
  12311                     items,
  12312                     .none,
  12313                     false,
  12314                 );
  12315             }
  12316 
  12317             if (is_first) {
  12318                 is_first = false;
  12319                 first_else_body = cond_body;
  12320                 cond_body = &.{};
  12321             } else {
  12322                 try sema.air_extra.ensureUnusedCapacity(
  12323                     gpa,
  12324                     @typeInfo(Air.CondBr).Struct.fields.len + prev_then_body.len + cond_body.len,
  12325                 );
  12326 
  12327                 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload =
  12328                     sema.addExtraAssumeCapacity(Air.CondBr{
  12329                     .then_body_len = @intCast(prev_then_body.len),
  12330                     .else_body_len = @intCast(cond_body.len),
  12331                 });
  12332                 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body));
  12333                 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cond_body));
  12334             }
  12335             gpa.free(prev_then_body);
  12336             prev_then_body = try case_block.instructions.toOwnedSlice(gpa);
  12337             prev_cond_br = new_cond_br;
  12338         }
  12339     }
  12340 
  12341     var final_else_body: []const Air.Inst.Index = &.{};
  12342     if (special.body.len != 0 or !is_first or case_block.wantSafety()) {
  12343         var emit_bb = false;
  12344         if (special.is_inline) switch (operand_ty.zigTypeTag(mod)) {
  12345             .Enum => {
  12346                 if (operand_ty.isNonexhaustiveEnum(mod) and !union_originally) {
  12347                     return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
  12348                         operand_ty.fmt(mod),
  12349                     });
  12350                 }
  12351                 for (seen_enum_fields, 0..) |f, i| {
  12352                     if (f != null) continue;
  12353                     cases_len += 1;
  12354 
  12355                     const item_val = try mod.enumValueFieldIndex(operand_ty, @intCast(i));
  12356                     const item_ref = Air.internedToRef(item_val.toIntern());
  12357 
  12358                     case_block.instructions.shrinkRetainingCapacity(0);
  12359                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  12360 
  12361                     const analyze_body = if (union_originally) blk: {
  12362                         const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
  12363                         break :blk field_ty.zigTypeTag(mod) != .NoReturn;
  12364                     } else true;
  12365 
  12366                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12367                     emit_bb = true;
  12368 
  12369                     if (analyze_body) {
  12370                         try spa.analyzeProngRuntime(
  12371                             &case_block,
  12372                             .special,
  12373                             special.body,
  12374                             special.capture,
  12375                             .special_capture,
  12376                             &.{item_ref},
  12377                             item_ref,
  12378                             special.has_tag_capture,
  12379                         );
  12380                     } else {
  12381                         _ = try case_block.addNoOp(.unreach);
  12382                     }
  12383 
  12384                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  12385                     cases_extra.appendAssumeCapacity(1); // items_len
  12386                     cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
  12387                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12388                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12389                 }
  12390             },
  12391             .ErrorSet => {
  12392                 if (operand_ty.isAnyError(mod)) {
  12393                     return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
  12394                         operand_ty.fmt(mod),
  12395                     });
  12396                 }
  12397                 for (0..operand_ty.errorSetNames(mod).len) |i| {
  12398                     const error_name = operand_ty.errorSetNames(mod)[i];
  12399                     if (seen_errors.contains(error_name)) continue;
  12400                     cases_len += 1;
  12401 
  12402                     const item_val = try mod.intern(.{ .err = .{
  12403                         .ty = operand_ty.toIntern(),
  12404                         .name = error_name,
  12405                     } });
  12406                     const item_ref = Air.internedToRef(item_val);
  12407 
  12408                     case_block.instructions.shrinkRetainingCapacity(0);
  12409                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  12410 
  12411                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12412                     emit_bb = true;
  12413 
  12414                     try spa.analyzeProngRuntime(
  12415                         &case_block,
  12416                         .special,
  12417                         special.body,
  12418                         special.capture,
  12419                         .special_capture,
  12420                         &.{item_ref},
  12421                         item_ref,
  12422                         special.has_tag_capture,
  12423                     );
  12424 
  12425                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  12426                     cases_extra.appendAssumeCapacity(1); // items_len
  12427                     cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
  12428                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12429                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12430                 }
  12431             },
  12432             .Int => {
  12433                 var it = try RangeSetUnhandledIterator.init(sema, operand_ty, range_set);
  12434                 while (try it.next()) |cur| {
  12435                     cases_len += 1;
  12436 
  12437                     const item_ref = Air.internedToRef(cur);
  12438 
  12439                     case_block.instructions.shrinkRetainingCapacity(0);
  12440                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  12441 
  12442                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12443                     emit_bb = true;
  12444 
  12445                     try spa.analyzeProngRuntime(
  12446                         &case_block,
  12447                         .special,
  12448                         special.body,
  12449                         special.capture,
  12450                         .special_capture,
  12451                         &.{item_ref},
  12452                         item_ref,
  12453                         special.has_tag_capture,
  12454                     );
  12455 
  12456                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  12457                     cases_extra.appendAssumeCapacity(1); // items_len
  12458                     cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
  12459                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12460                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12461                 }
  12462             },
  12463             .Bool => {
  12464                 if (true_count == 0) {
  12465                     cases_len += 1;
  12466 
  12467                     case_block.instructions.shrinkRetainingCapacity(0);
  12468                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  12469 
  12470                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12471                     emit_bb = true;
  12472 
  12473                     try spa.analyzeProngRuntime(
  12474                         &case_block,
  12475                         .special,
  12476                         special.body,
  12477                         special.capture,
  12478                         .special_capture,
  12479                         &.{.bool_true},
  12480                         .bool_true,
  12481                         special.has_tag_capture,
  12482                     );
  12483 
  12484                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  12485                     cases_extra.appendAssumeCapacity(1); // items_len
  12486                     cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
  12487                     cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true));
  12488                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12489                 }
  12490                 if (false_count == 0) {
  12491                     cases_len += 1;
  12492 
  12493                     case_block.instructions.shrinkRetainingCapacity(0);
  12494                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  12495 
  12496                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12497                     emit_bb = true;
  12498 
  12499                     try spa.analyzeProngRuntime(
  12500                         &case_block,
  12501                         .special,
  12502                         special.body,
  12503                         special.capture,
  12504                         .special_capture,
  12505                         &.{.bool_false},
  12506                         .bool_false,
  12507                         special.has_tag_capture,
  12508                     );
  12509 
  12510                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  12511                     cases_extra.appendAssumeCapacity(1); // items_len
  12512                     cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
  12513                     cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false));
  12514                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12515                 }
  12516             },
  12517             else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
  12518                 operand_ty.fmt(mod),
  12519             }),
  12520         };
  12521 
  12522         case_block.instructions.shrinkRetainingCapacity(0);
  12523         case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope);
  12524 
  12525         if (mod.backendSupportsFeature(.is_named_enum_value) and
  12526             special.body.len != 0 and block.wantSafety() and
  12527             operand_ty.zigTypeTag(mod) == .Enum and
  12528             (!operand_ty.isNonexhaustiveEnum(mod) or union_originally))
  12529         {
  12530             try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
  12531             const ok = try case_block.addUnOp(.is_named_enum_value, operand);
  12532             try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
  12533         }
  12534 
  12535         const analyze_body = if (union_originally and !special.is_inline)
  12536             for (seen_enum_fields, 0..) |seen_field, index| {
  12537                 if (seen_field != null) continue;
  12538                 const union_obj = mod.typeToUnion(maybe_union_ty).?;
  12539                 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[index]);
  12540                 if (field_ty.zigTypeTag(mod) != .NoReturn) break true;
  12541             } else false
  12542         else
  12543             true;
  12544         if (special.body.len != 0 and err_set and
  12545             try sema.maybeErrorUnwrap(&case_block, special.body, operand, operand_src))
  12546         {
  12547             // nothing to do here
  12548         } else if (special.body.len != 0 and analyze_body and !special.is_inline) {
  12549             try spa.analyzeProngRuntime(
  12550                 &case_block,
  12551                 .special,
  12552                 special.body,
  12553                 special.capture,
  12554                 .special_capture,
  12555                 undefined, // case_vals may be undefined for special prongs
  12556                 .none,
  12557                 false,
  12558             );
  12559         } else {
  12560             // We still need a terminator in this block, but we have proven
  12561             // that it is unreachable.
  12562             if (case_block.wantSafety()) {
  12563                 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
  12564                 try sema.safetyPanic(&case_block, src, .corrupt_switch);
  12565             } else {
  12566                 _ = try case_block.addNoOp(.unreach);
  12567             }
  12568         }
  12569 
  12570         if (is_first) {
  12571             final_else_body = case_block.instructions.items;
  12572         } else {
  12573             try sema.air_extra.ensureUnusedCapacity(gpa, prev_then_body.len +
  12574                 @typeInfo(Air.CondBr).Struct.fields.len + case_block.instructions.items.len);
  12575 
  12576             sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload =
  12577                 sema.addExtraAssumeCapacity(Air.CondBr{
  12578                 .then_body_len = @intCast(prev_then_body.len),
  12579                 .else_body_len = @intCast(case_block.instructions.items.len),
  12580             });
  12581             sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body));
  12582             sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12583             final_else_body = first_else_body;
  12584         }
  12585     }
  12586 
  12587     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len +
  12588         cases_extra.items.len + final_else_body.len);
  12589 
  12590     _ = try child_block.addInst(.{ .tag = .switch_br, .data = .{ .pl_op = .{
  12591         .operand = operand,
  12592         .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
  12593             .cases_len = @intCast(cases_len),
  12594             .else_body_len = @intCast(final_else_body.len),
  12595         }),
  12596     } } });
  12597     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items));
  12598     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(final_else_body));
  12599 
  12600     return sema.analyzeBlockBody(block, src, &child_block, merges);
  12601 }
  12602 
  12603 const RangeSetUnhandledIterator = struct {
  12604     mod: *Module,
  12605     cur: ?InternPool.Index,
  12606     max: InternPool.Index,
  12607     range_i: usize,
  12608     ranges: []const RangeSet.Range,
  12609     limbs: []math.big.Limb,
  12610 
  12611     const preallocated_limbs = math.big.int.calcTwosCompLimbCount(128);
  12612 
  12613     fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator {
  12614         const mod = sema.mod;
  12615         const int_type = mod.intern_pool.indexToKey(ty.toIntern()).int_type;
  12616         const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits);
  12617         return .{
  12618             .mod = mod,
  12619             .cur = (try ty.minInt(mod, ty)).toIntern(),
  12620             .max = (try ty.maxInt(mod, ty)).toIntern(),
  12621             .range_i = 0,
  12622             .ranges = range_set.ranges.items,
  12623             .limbs = if (needed_limbs > preallocated_limbs)
  12624                 try sema.arena.alloc(math.big.Limb, needed_limbs)
  12625             else
  12626                 &.{},
  12627         };
  12628     }
  12629 
  12630     fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index {
  12631         if (val == it.max) return null;
  12632         const int = it.mod.intern_pool.indexToKey(val).int;
  12633 
  12634         switch (int.storage) {
  12635             inline .u64, .i64 => |val_int| {
  12636                 const next_int = @addWithOverflow(val_int, 1);
  12637                 if (next_int[1] == 0)
  12638                     return (try it.mod.intValue(Type.fromInterned(int.ty), next_int[0])).toIntern();
  12639             },
  12640             .big_int => {},
  12641             .lazy_align, .lazy_size => unreachable,
  12642         }
  12643 
  12644         var val_space: InternPool.Key.Int.Storage.BigIntSpace = undefined;
  12645         const val_bigint = int.storage.toBigInt(&val_space);
  12646 
  12647         var result_limbs: [preallocated_limbs]math.big.Limb = undefined;
  12648         var result_bigint = math.big.int.Mutable.init(
  12649             if (it.limbs.len > 0) it.limbs else &result_limbs,
  12650             0,
  12651         );
  12652 
  12653         result_bigint.addScalar(val_bigint, 1);
  12654         return (try it.mod.intValue_big(Type.fromInterned(int.ty), result_bigint.toConst())).toIntern();
  12655     }
  12656 
  12657     fn next(it: *RangeSetUnhandledIterator) !?InternPool.Index {
  12658         var cur = it.cur orelse return null;
  12659         while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) {
  12660             defer it.range_i += 1;
  12661             cur = (try it.addOne(it.ranges[it.range_i].last)) orelse {
  12662                 it.cur = null;
  12663                 return null;
  12664             };
  12665         }
  12666         it.cur = try it.addOne(cur);
  12667         return cur;
  12668     }
  12669 };
  12670 
  12671 const ResolvedSwitchItem = struct {
  12672     ref: Air.Inst.Ref,
  12673     val: InternPool.Index,
  12674 };
  12675 fn resolveSwitchItemVal(
  12676     sema: *Sema,
  12677     block: *Block,
  12678     item_ref: Zir.Inst.Ref,
  12679     /// Coerce `item_ref` to this type.
  12680     coerce_ty: Type,
  12681     switch_node_offset: i32,
  12682     switch_prong_src: Module.SwitchProngSrc,
  12683     range_expand: Module.SwitchProngSrc.RangeExpand,
  12684 ) CompileError!ResolvedSwitchItem {
  12685     const mod = sema.mod;
  12686     const uncoerced_item = try sema.resolveInst(item_ref);
  12687 
  12688     // Constructing a LazySrcLoc is costly because we only have the switch AST node.
  12689     // Only if we know for sure we need to report a compile error do we resolve the
  12690     // full source locations.
  12691 
  12692     const item = sema.coerce(block, coerce_ty, uncoerced_item, .unneeded) catch |err| switch (err) {
  12693         error.NeededSourceLocation => {
  12694             const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand);
  12695             _ = try sema.coerce(block, coerce_ty, uncoerced_item, src);
  12696             unreachable;
  12697         },
  12698         else => |e| return e,
  12699     };
  12700 
  12701     const maybe_lazy = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch |err| switch (err) {
  12702         error.NeededSourceLocation => {
  12703             const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand);
  12704             _ = try sema.resolveConstDefinedValue(block, src, item, .{
  12705                 .needed_comptime_reason = "switch prong values must be comptime-known",
  12706             });
  12707             unreachable;
  12708         },
  12709         else => |e| return e,
  12710     };
  12711 
  12712     const val = try sema.resolveLazyValue(maybe_lazy);
  12713     const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: {
  12714         break :blk Air.internedToRef(val.toIntern());
  12715     } else item;
  12716 
  12717     return .{ .ref = new_item, .val = val.toIntern() };
  12718 }
  12719 
  12720 fn validateSwitchRange(
  12721     sema: *Sema,
  12722     block: *Block,
  12723     range_set: *RangeSet,
  12724     first_ref: Zir.Inst.Ref,
  12725     last_ref: Zir.Inst.Ref,
  12726     operand_ty: Type,
  12727     src_node_offset: i32,
  12728     switch_prong_src: Module.SwitchProngSrc,
  12729 ) CompileError![2]Air.Inst.Ref {
  12730     const mod = sema.mod;
  12731     const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, src_node_offset, switch_prong_src, .first);
  12732     const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, src_node_offset, switch_prong_src, .last);
  12733     if (try Value.fromInterned(first.val).compareAll(.gt, Value.fromInterned(last.val), operand_ty, mod)) {
  12734         const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), src_node_offset, .first);
  12735         return sema.fail(block, src, "range start value is greater than the end value", .{});
  12736     }
  12737     const maybe_prev_src = try range_set.add(first.val, last.val, switch_prong_src);
  12738     try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
  12739     return .{ first.ref, last.ref };
  12740 }
  12741 
  12742 fn validateSwitchItemInt(
  12743     sema: *Sema,
  12744     block: *Block,
  12745     range_set: *RangeSet,
  12746     item_ref: Zir.Inst.Ref,
  12747     operand_ty: Type,
  12748     src_node_offset: i32,
  12749     switch_prong_src: Module.SwitchProngSrc,
  12750 ) CompileError!Air.Inst.Ref {
  12751     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
  12752     const maybe_prev_src = try range_set.add(item.val, item.val, switch_prong_src);
  12753     try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
  12754     return item.ref;
  12755 }
  12756 
  12757 fn validateSwitchItemEnum(
  12758     sema: *Sema,
  12759     block: *Block,
  12760     seen_fields: []?Module.SwitchProngSrc,
  12761     range_set: *RangeSet,
  12762     item_ref: Zir.Inst.Ref,
  12763     operand_ty: Type,
  12764     src_node_offset: i32,
  12765     switch_prong_src: Module.SwitchProngSrc,
  12766 ) CompileError!Air.Inst.Ref {
  12767     const ip = &sema.mod.intern_pool;
  12768     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
  12769     const int = ip.indexToKey(item.val).enum_tag.int;
  12770     const field_index = ip.indexToKey(ip.typeOf(item.val)).enum_type.tagValueIndex(ip, int) orelse {
  12771         const maybe_prev_src = try range_set.add(int, int, switch_prong_src);
  12772         try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
  12773         return item.ref;
  12774     };
  12775     const maybe_prev_src = seen_fields[field_index];
  12776     seen_fields[field_index] = switch_prong_src;
  12777     try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
  12778     return item.ref;
  12779 }
  12780 
  12781 fn validateSwitchItemError(
  12782     sema: *Sema,
  12783     block: *Block,
  12784     seen_errors: *SwitchErrorSet,
  12785     item_ref: Zir.Inst.Ref,
  12786     operand_ty: Type,
  12787     src_node_offset: i32,
  12788     switch_prong_src: Module.SwitchProngSrc,
  12789 ) CompileError!Air.Inst.Ref {
  12790     const ip = &sema.mod.intern_pool;
  12791     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
  12792     const error_name = ip.indexToKey(item.val).err.name;
  12793     const maybe_prev_src = if (try seen_errors.fetchPut(error_name, switch_prong_src)) |prev|
  12794         prev.value
  12795     else
  12796         null;
  12797     try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
  12798     return item.ref;
  12799 }
  12800 
  12801 fn validateSwitchDupe(
  12802     sema: *Sema,
  12803     block: *Block,
  12804     maybe_prev_src: ?Module.SwitchProngSrc,
  12805     switch_prong_src: Module.SwitchProngSrc,
  12806     src_node_offset: i32,
  12807 ) CompileError!void {
  12808     const prev_prong_src = maybe_prev_src orelse return;
  12809     const mod = sema.mod;
  12810     const block_src_decl = mod.declPtr(block.src_decl);
  12811     const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none);
  12812     const prev_src = prev_prong_src.resolve(mod, block_src_decl, src_node_offset, .none);
  12813     const msg = msg: {
  12814         const msg = try sema.errMsg(
  12815             block,
  12816             src,
  12817             "duplicate switch value",
  12818             .{},
  12819         );
  12820         errdefer msg.destroy(sema.gpa);
  12821         try sema.errNote(
  12822             block,
  12823             prev_src,
  12824             msg,
  12825             "previous value here",
  12826             .{},
  12827         );
  12828         break :msg msg;
  12829     };
  12830     return sema.failWithOwnedErrorMsg(block, msg);
  12831 }
  12832 
  12833 fn validateSwitchItemBool(
  12834     sema: *Sema,
  12835     block: *Block,
  12836     true_count: *u8,
  12837     false_count: *u8,
  12838     item_ref: Zir.Inst.Ref,
  12839     src_node_offset: i32,
  12840     switch_prong_src: Module.SwitchProngSrc,
  12841 ) CompileError!Air.Inst.Ref {
  12842     const mod = sema.mod;
  12843     const item = try sema.resolveSwitchItemVal(block, item_ref, Type.bool, src_node_offset, switch_prong_src, .none);
  12844     if (Value.fromInterned(item.val).toBool()) {
  12845         true_count.* += 1;
  12846     } else {
  12847         false_count.* += 1;
  12848     }
  12849     if (true_count.* > 1 or false_count.* > 1) {
  12850         const block_src_decl = sema.mod.declPtr(block.src_decl);
  12851         const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none);
  12852         return sema.fail(block, src, "duplicate switch value", .{});
  12853     }
  12854     return item.ref;
  12855 }
  12856 
  12857 const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, Module.SwitchProngSrc);
  12858 
  12859 fn validateSwitchItemSparse(
  12860     sema: *Sema,
  12861     block: *Block,
  12862     seen_values: *ValueSrcMap,
  12863     item_ref: Zir.Inst.Ref,
  12864     operand_ty: Type,
  12865     src_node_offset: i32,
  12866     switch_prong_src: Module.SwitchProngSrc,
  12867 ) CompileError!Air.Inst.Ref {
  12868     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
  12869     const kv = (try seen_values.fetchPut(sema.gpa, item.val, switch_prong_src)) orelse return item.ref;
  12870     try sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset);
  12871     unreachable;
  12872 }
  12873 
  12874 fn validateSwitchNoRange(
  12875     sema: *Sema,
  12876     block: *Block,
  12877     ranges_len: u32,
  12878     operand_ty: Type,
  12879     src_node_offset: i32,
  12880 ) CompileError!void {
  12881     if (ranges_len == 0)
  12882         return;
  12883 
  12884     const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
  12885     const range_src: LazySrcLoc = .{ .node_offset_switch_range = src_node_offset };
  12886 
  12887     const msg = msg: {
  12888         const msg = try sema.errMsg(
  12889             block,
  12890             operand_src,
  12891             "ranges not allowed when switching on type '{}'",
  12892             .{operand_ty.fmt(sema.mod)},
  12893         );
  12894         errdefer msg.destroy(sema.gpa);
  12895         try sema.errNote(
  12896             block,
  12897             range_src,
  12898             msg,
  12899             "range here",
  12900             .{},
  12901         );
  12902         break :msg msg;
  12903     };
  12904     return sema.failWithOwnedErrorMsg(block, msg);
  12905 }
  12906 
  12907 fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref, operand_src: LazySrcLoc) !bool {
  12908     const mod = sema.mod;
  12909     if (!mod.backendSupportsFeature(.panic_unwrap_error)) return false;
  12910 
  12911     const tags = sema.code.instructions.items(.tag);
  12912     for (body) |inst| {
  12913         switch (tags[@intFromEnum(inst)]) {
  12914             .@"unreachable" => if (!block.wantSafety()) return false,
  12915             .save_err_ret_index,
  12916             .dbg_block_begin,
  12917             .dbg_block_end,
  12918             .dbg_stmt,
  12919             .str,
  12920             .as_node,
  12921             .panic,
  12922             .field_val,
  12923             => {},
  12924             else => return false,
  12925         }
  12926     }
  12927 
  12928     for (body) |inst| {
  12929         const air_inst = switch (tags[@intFromEnum(inst)]) {
  12930             .dbg_block_begin,
  12931             .dbg_block_end,
  12932             => continue,
  12933             .dbg_stmt => {
  12934                 try sema.zirDbgStmt(block, inst);
  12935                 continue;
  12936             },
  12937             .save_err_ret_index => {
  12938                 try sema.zirSaveErrRetIndex(block, inst);
  12939                 continue;
  12940             },
  12941             .str => try sema.zirStr(inst),
  12942             .as_node => try sema.zirAsNode(block, inst),
  12943             .field_val => try sema.zirFieldVal(block, inst),
  12944             .@"unreachable" => {
  12945                 if (!mod.comp.formatted_panics) {
  12946                     try sema.safetyPanic(block, operand_src, .unwrap_error);
  12947                     return true;
  12948                 }
  12949 
  12950                 const panic_fn = try sema.getBuiltin("panicUnwrapError");
  12951                 const err_return_trace = try sema.getErrorReturnTrace(block);
  12952                 const args: [2]Air.Inst.Ref = .{ err_return_trace, operand };
  12953                 try sema.callBuiltin(block, operand_src, panic_fn, .auto, &args, .@"safety check");
  12954                 return true;
  12955             },
  12956             .panic => {
  12957                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  12958                 const msg_inst = try sema.resolveInst(inst_data.operand);
  12959 
  12960                 const panic_fn = try sema.getBuiltin("panic");
  12961                 const err_return_trace = try sema.getErrorReturnTrace(block);
  12962                 const args: [3]Air.Inst.Ref = .{ msg_inst, err_return_trace, .null_value };
  12963                 try sema.callBuiltin(block, operand_src, panic_fn, .auto, &args, .@"safety check");
  12964                 return true;
  12965             },
  12966             else => unreachable,
  12967         };
  12968         if (sema.typeOf(air_inst).isNoReturn(mod))
  12969             return true;
  12970         sema.inst_map.putAssumeCapacity(inst, air_inst);
  12971     }
  12972     unreachable;
  12973 }
  12974 
  12975 fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void {
  12976     const mod = sema.mod;
  12977     const index = cond.toIndex() orelse return;
  12978     if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) return;
  12979 
  12980     const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node;
  12981     const err_operand = try sema.resolveInst(err_inst_data.operand);
  12982     const operand_ty = sema.typeOf(err_operand);
  12983     if (operand_ty.zigTypeTag(mod) == .ErrorSet) {
  12984         try sema.maybeErrorUnwrapComptime(block, body, err_operand);
  12985         return;
  12986     }
  12987     if (try sema.resolveDefinedValue(block, cond_src, err_operand)) |val| {
  12988         if (!operand_ty.isError(mod)) return;
  12989         if (val.getErrorName(mod) == .none) return;
  12990         try sema.maybeErrorUnwrapComptime(block, body, err_operand);
  12991     }
  12992 }
  12993 
  12994 fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !void {
  12995     const tags = sema.code.instructions.items(.tag);
  12996     const inst = for (body) |inst| {
  12997         switch (tags[@intFromEnum(inst)]) {
  12998             .dbg_block_begin,
  12999             .dbg_block_end,
  13000             .dbg_stmt,
  13001             .save_err_ret_index,
  13002             => {},
  13003             .@"unreachable" => break inst,
  13004             else => return,
  13005         }
  13006     } else return;
  13007     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable";
  13008     const src = inst_data.src();
  13009 
  13010     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
  13011         if (val.getErrorName(sema.mod).unwrap()) |name| {
  13012             return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&sema.mod.intern_pool)});
  13013         }
  13014     }
  13015 }
  13016 
  13017 fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13018     const mod = sema.mod;
  13019     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13020     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13021     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  13022     const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  13023     const ty = try sema.resolveType(block, ty_src, extra.lhs);
  13024     const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{
  13025         .needed_comptime_reason = "field name must be comptime-known",
  13026     });
  13027     try sema.resolveTypeFields(ty);
  13028     const ip = &mod.intern_pool;
  13029 
  13030     const has_field = hf: {
  13031         switch (ip.indexToKey(ty.toIntern())) {
  13032             .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  13033                 .Slice => {
  13034                     if (ip.stringEqlSlice(field_name, "ptr")) break :hf true;
  13035                     if (ip.stringEqlSlice(field_name, "len")) break :hf true;
  13036                     break :hf false;
  13037                 },
  13038                 else => {},
  13039             },
  13040             .anon_struct_type => |anon_struct| {
  13041                 if (anon_struct.names.len != 0) {
  13042                     break :hf mem.indexOfScalar(InternPool.NullTerminatedString, anon_struct.names.get(ip), field_name) != null;
  13043                 } else {
  13044                     const field_index = field_name.toUnsigned(ip) orelse break :hf false;
  13045                     break :hf field_index < ty.structFieldCount(mod);
  13046                 }
  13047             },
  13048             .struct_type => |struct_type| {
  13049                 break :hf struct_type.nameIndex(ip, field_name) != null;
  13050             },
  13051             .union_type => |union_type| {
  13052                 const union_obj = ip.loadUnionType(union_type);
  13053                 break :hf union_obj.nameIndex(ip, field_name) != null;
  13054             },
  13055             .enum_type => |enum_type| {
  13056                 break :hf enum_type.nameIndex(ip, field_name) != null;
  13057             },
  13058             .array_type => break :hf ip.stringEqlSlice(field_name, "len"),
  13059             else => {},
  13060         }
  13061         return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{
  13062             ty.fmt(mod),
  13063         });
  13064     };
  13065     return if (has_field) .bool_true else .bool_false;
  13066 }
  13067 
  13068 fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13069     const mod = sema.mod;
  13070     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13071     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13072     const src = inst_data.src();
  13073     const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  13074     const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  13075     const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
  13076     const decl_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, .{
  13077         .needed_comptime_reason = "decl name must be comptime-known",
  13078     });
  13079 
  13080     try sema.checkNamespaceType(block, lhs_src, container_type);
  13081 
  13082     const namespace = container_type.getNamespaceIndex(mod).unwrap() orelse
  13083         return .bool_false;
  13084     if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
  13085         const decl = mod.declPtr(decl_index);
  13086         if (decl.is_pub or decl.getFileScope(mod) == block.getFileScope(mod)) {
  13087             return .bool_true;
  13088         }
  13089     }
  13090     return .bool_false;
  13091 }
  13092 
  13093 fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13094     const tracy = trace(@src());
  13095     defer tracy.end();
  13096 
  13097     const mod = sema.mod;
  13098     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
  13099     const operand_src = inst_data.src();
  13100     const operand = inst_data.get(sema.code);
  13101 
  13102     const result = mod.importFile(block.getFileScope(mod), operand) catch |err| switch (err) {
  13103         error.ImportOutsideModulePath => {
  13104             return sema.fail(block, operand_src, "import of file outside module path: '{s}'", .{operand});
  13105         },
  13106         error.ModuleNotFound => {
  13107             return sema.fail(block, operand_src, "no module named '{s}' available within module {s}", .{
  13108                 operand, block.getFileScope(mod).mod.fully_qualified_name,
  13109             });
  13110         },
  13111         else => {
  13112             // TODO: these errors are file system errors; make sure an update() will
  13113             // retry this and not cache the file system error, which may be transient.
  13114             return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });
  13115         },
  13116     };
  13117     try mod.semaFile(result.file);
  13118     const file_root_decl_index = result.file.root_decl.unwrap().?;
  13119     return sema.analyzeDeclVal(block, operand_src, file_root_decl_index);
  13120 }
  13121 
  13122 fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13123     const tracy = trace(@src());
  13124     defer tracy.end();
  13125 
  13126     const mod = sema.mod;
  13127     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  13128     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  13129     const name = try sema.resolveConstString(block, operand_src, inst_data.operand, .{
  13130         .needed_comptime_reason = "file path name must be comptime-known",
  13131     });
  13132 
  13133     if (name.len == 0) {
  13134         return sema.fail(block, operand_src, "file path name cannot be empty", .{});
  13135     }
  13136 
  13137     const src_loc = operand_src.toSrcLoc(mod.declPtr(block.src_decl), mod);
  13138     const val = mod.embedFile(block.getFileScope(mod), name, src_loc) catch |err| switch (err) {
  13139         error.ImportOutsideModulePath => {
  13140             return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name});
  13141         },
  13142         else => {
  13143             // TODO: these errors are file system errors; make sure an update() will
  13144             // retry this and not cache the file system error, which may be transient.
  13145             return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ name, @errorName(err) });
  13146         },
  13147     };
  13148 
  13149     return Air.internedToRef(val);
  13150 }
  13151 
  13152 fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13153     const mod = sema.mod;
  13154     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
  13155     const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
  13156     _ = try mod.getErrorValue(name);
  13157     const error_set_type = try mod.singleErrorSetType(name);
  13158     return Air.internedToRef((try mod.intern(.{ .err = .{
  13159         .ty = error_set_type.toIntern(),
  13160         .name = name,
  13161     } })));
  13162 }
  13163 
  13164 fn zirShl(
  13165     sema: *Sema,
  13166     block: *Block,
  13167     inst: Zir.Inst.Index,
  13168     air_tag: Air.Inst.Tag,
  13169 ) CompileError!Air.Inst.Ref {
  13170     const tracy = trace(@src());
  13171     defer tracy.end();
  13172 
  13173     const mod = sema.mod;
  13174     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13175     const src = inst_data.src();
  13176     sema.src = src;
  13177     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  13178     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  13179     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13180     const lhs = try sema.resolveInst(extra.lhs);
  13181     const rhs = try sema.resolveInst(extra.rhs);
  13182     const lhs_ty = sema.typeOf(lhs);
  13183     const rhs_ty = sema.typeOf(rhs);
  13184     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  13185 
  13186     const scalar_ty = lhs_ty.scalarType(mod);
  13187     const scalar_rhs_ty = rhs_ty.scalarType(mod);
  13188 
  13189     // TODO coerce rhs if air_tag is not shl_sat
  13190     const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, scalar_rhs_ty);
  13191 
  13192     const maybe_lhs_val = try sema.resolveValueIntable(lhs);
  13193     const maybe_rhs_val = try sema.resolveValueIntable(rhs);
  13194 
  13195     if (maybe_rhs_val) |rhs_val| {
  13196         if (rhs_val.isUndef(mod)) {
  13197             return mod.undefRef(sema.typeOf(lhs));
  13198         }
  13199         // If rhs is 0, return lhs without doing any calculations.
  13200         if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  13201             return lhs;
  13202         }
  13203         if (scalar_ty.zigTypeTag(mod) != .ComptimeInt and air_tag != .shl_sat) {
  13204             const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits);
  13205             if (rhs_ty.zigTypeTag(mod) == .Vector) {
  13206                 var i: usize = 0;
  13207                 while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
  13208                     const rhs_elem = try rhs_val.elemValue(mod, i);
  13209                     if (rhs_elem.compareHetero(.gte, bit_value, mod)) {
  13210                         return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
  13211                             rhs_elem.fmtValue(scalar_ty, mod),
  13212                             i,
  13213                             scalar_ty.fmt(mod),
  13214                         });
  13215                     }
  13216                 }
  13217             } else if (rhs_val.compareHetero(.gte, bit_value, mod)) {
  13218                 return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{
  13219                     rhs_val.fmtValue(scalar_ty, mod),
  13220                     scalar_ty.fmt(mod),
  13221                 });
  13222             }
  13223         }
  13224         if (rhs_ty.zigTypeTag(mod) == .Vector) {
  13225             var i: usize = 0;
  13226             while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
  13227                 const rhs_elem = try rhs_val.elemValue(mod, i);
  13228                 if (rhs_elem.compareHetero(.lt, try mod.intValue(scalar_rhs_ty, 0), mod)) {
  13229                     return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{
  13230                         rhs_elem.fmtValue(scalar_ty, mod),
  13231                         i,
  13232                     });
  13233                 }
  13234             }
  13235         } else if (rhs_val.compareHetero(.lt, try mod.intValue(rhs_ty, 0), mod)) {
  13236             return sema.fail(block, rhs_src, "shift by negative amount '{}'", .{
  13237                 rhs_val.fmtValue(scalar_ty, mod),
  13238             });
  13239         }
  13240     }
  13241 
  13242     const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
  13243         if (lhs_val.isUndef(mod)) return mod.undefRef(lhs_ty);
  13244         const rhs_val = maybe_rhs_val orelse {
  13245             if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
  13246                 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
  13247             }
  13248             break :rs rhs_src;
  13249         };
  13250 
  13251         const val = switch (air_tag) {
  13252             .shl_exact => val: {
  13253                 const shifted = try lhs_val.shlWithOverflow(rhs_val, lhs_ty, sema.arena, mod);
  13254                 if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
  13255                     break :val shifted.wrapped_result;
  13256                 }
  13257                 if (shifted.overflow_bit.compareAllWithZero(.eq, mod)) {
  13258                     break :val shifted.wrapped_result;
  13259                 }
  13260                 return sema.fail(block, src, "operation caused overflow", .{});
  13261             },
  13262 
  13263             .shl_sat => if (scalar_ty.zigTypeTag(mod) == .ComptimeInt)
  13264                 try lhs_val.shl(rhs_val, lhs_ty, sema.arena, mod)
  13265             else
  13266                 try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, mod),
  13267 
  13268             .shl => if (scalar_ty.zigTypeTag(mod) == .ComptimeInt)
  13269                 try lhs_val.shl(rhs_val, lhs_ty, sema.arena, mod)
  13270             else
  13271                 try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, mod),
  13272 
  13273             else => unreachable,
  13274         };
  13275 
  13276         return Air.internedToRef(val.toIntern());
  13277     } else lhs_src;
  13278 
  13279     const new_rhs = if (air_tag == .shl_sat) rhs: {
  13280         // Limit the RHS type for saturating shl to be an integer as small as the LHS.
  13281         if (rhs_is_comptime_int or
  13282             scalar_rhs_ty.intInfo(mod).bits > scalar_ty.intInfo(mod).bits)
  13283         {
  13284             const max_int = Air.internedToRef((try lhs_ty.maxInt(mod, lhs_ty)).toIntern());
  13285             const rhs_limited = try sema.analyzeMinMax(block, rhs_src, .min, &.{ rhs, max_int }, &.{ rhs_src, rhs_src });
  13286             break :rhs try sema.intCast(block, src, lhs_ty, rhs_src, rhs_limited, rhs_src, false);
  13287         } else {
  13288             break :rhs rhs;
  13289         }
  13290     } else rhs;
  13291 
  13292     try sema.requireRuntimeBlock(block, src, runtime_src);
  13293     if (block.wantSafety()) {
  13294         const bit_count = scalar_ty.intInfo(mod).bits;
  13295         if (!std.math.isPowerOfTwo(bit_count)) {
  13296             const bit_count_val = try mod.intValue(scalar_rhs_ty, bit_count);
  13297             const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: {
  13298                 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern());
  13299                 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
  13300                 break :ok try block.addInst(.{
  13301                     .tag = .reduce,
  13302                     .data = .{ .reduce = .{
  13303                         .operand = lt,
  13304                         .operation = .And,
  13305                     } },
  13306                 });
  13307             } else ok: {
  13308                 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern());
  13309                 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
  13310             };
  13311             try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big);
  13312         }
  13313 
  13314         if (air_tag == .shl_exact) {
  13315             const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty);
  13316             const op_ov = try block.addInst(.{
  13317                 .tag = .shl_with_overflow,
  13318                 .data = .{ .ty_pl = .{
  13319                     .ty = Air.internedToRef(op_ov_tuple_ty.toIntern()),
  13320                     .payload = try sema.addExtra(Air.Bin{
  13321                         .lhs = lhs,
  13322                         .rhs = rhs,
  13323                     }),
  13324                 } },
  13325             });
  13326             const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);
  13327             const any_ov_bit = if (lhs_ty.zigTypeTag(mod) == .Vector)
  13328                 try block.addInst(.{
  13329                     .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  13330                     .data = .{ .reduce = .{
  13331                         .operand = ov_bit,
  13332                         .operation = .Or,
  13333                     } },
  13334                 })
  13335             else
  13336                 ov_bit;
  13337             const zero_ov = Air.internedToRef((try mod.intValue(Type.u1, 0)).toIntern());
  13338             const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
  13339 
  13340             try sema.addSafetyCheck(block, src, no_ov, .shl_overflow);
  13341             return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
  13342         }
  13343     }
  13344     return block.addBinOp(air_tag, lhs, new_rhs);
  13345 }
  13346 
  13347 fn zirShr(
  13348     sema: *Sema,
  13349     block: *Block,
  13350     inst: Zir.Inst.Index,
  13351     air_tag: Air.Inst.Tag,
  13352 ) CompileError!Air.Inst.Ref {
  13353     const tracy = trace(@src());
  13354     defer tracy.end();
  13355 
  13356     const mod = sema.mod;
  13357     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13358     const src = inst_data.src();
  13359     sema.src = src;
  13360     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  13361     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  13362     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13363     const lhs = try sema.resolveInst(extra.lhs);
  13364     const rhs = try sema.resolveInst(extra.rhs);
  13365     const lhs_ty = sema.typeOf(lhs);
  13366     const rhs_ty = sema.typeOf(rhs);
  13367     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  13368     const scalar_ty = lhs_ty.scalarType(mod);
  13369 
  13370     const maybe_lhs_val = try sema.resolveValueIntable(lhs);
  13371     const maybe_rhs_val = try sema.resolveValueIntable(rhs);
  13372 
  13373     const runtime_src = if (maybe_rhs_val) |rhs_val| rs: {
  13374         if (rhs_val.isUndef(mod)) {
  13375             return mod.undefRef(lhs_ty);
  13376         }
  13377         // If rhs is 0, return lhs without doing any calculations.
  13378         if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  13379             return lhs;
  13380         }
  13381         if (scalar_ty.zigTypeTag(mod) != .ComptimeInt) {
  13382             const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits);
  13383             if (rhs_ty.zigTypeTag(mod) == .Vector) {
  13384                 var i: usize = 0;
  13385                 while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
  13386                     const rhs_elem = try rhs_val.elemValue(mod, i);
  13387                     if (rhs_elem.compareHetero(.gte, bit_value, mod)) {
  13388                         return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
  13389                             rhs_elem.fmtValue(scalar_ty, mod),
  13390                             i,
  13391                             scalar_ty.fmt(mod),
  13392                         });
  13393                     }
  13394                 }
  13395             } else if (rhs_val.compareHetero(.gte, bit_value, mod)) {
  13396                 return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{
  13397                     rhs_val.fmtValue(scalar_ty, mod),
  13398                     scalar_ty.fmt(mod),
  13399                 });
  13400             }
  13401         }
  13402         if (rhs_ty.zigTypeTag(mod) == .Vector) {
  13403             var i: usize = 0;
  13404             while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
  13405                 const rhs_elem = try rhs_val.elemValue(mod, i);
  13406                 if (rhs_elem.compareHetero(.lt, try mod.intValue(rhs_ty.childType(mod), 0), mod)) {
  13407                     return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{
  13408                         rhs_elem.fmtValue(scalar_ty, mod),
  13409                         i,
  13410                     });
  13411                 }
  13412             }
  13413         } else if (rhs_val.compareHetero(.lt, try mod.intValue(rhs_ty, 0), mod)) {
  13414             return sema.fail(block, rhs_src, "shift by negative amount '{}'", .{
  13415                 rhs_val.fmtValue(scalar_ty, mod),
  13416             });
  13417         }
  13418         if (maybe_lhs_val) |lhs_val| {
  13419             if (lhs_val.isUndef(mod)) {
  13420                 return mod.undefRef(lhs_ty);
  13421             }
  13422             if (air_tag == .shr_exact) {
  13423                 // Detect if any ones would be shifted out.
  13424                 const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, mod);
  13425                 if (!(try truncated.compareAllWithZeroAdvanced(.eq, sema))) {
  13426                     return sema.fail(block, src, "exact shift shifted out 1 bits", .{});
  13427                 }
  13428             }
  13429             const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, mod);
  13430             return Air.internedToRef(val.toIntern());
  13431         } else {
  13432             break :rs lhs_src;
  13433         }
  13434     } else rhs_src;
  13435 
  13436     if (maybe_rhs_val == null and scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
  13437         return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
  13438     }
  13439 
  13440     try sema.requireRuntimeBlock(block, src, runtime_src);
  13441     const result = try block.addBinOp(air_tag, lhs, rhs);
  13442     if (block.wantSafety()) {
  13443         const bit_count = scalar_ty.intInfo(mod).bits;
  13444         if (!std.math.isPowerOfTwo(bit_count)) {
  13445             const bit_count_val = try mod.intValue(rhs_ty.scalarType(mod), bit_count);
  13446 
  13447             const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: {
  13448                 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern());
  13449                 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
  13450                 break :ok try block.addInst(.{
  13451                     .tag = .reduce,
  13452                     .data = .{ .reduce = .{
  13453                         .operand = lt,
  13454                         .operation = .And,
  13455                     } },
  13456                 });
  13457             } else ok: {
  13458                 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern());
  13459                 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
  13460             };
  13461             try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big);
  13462         }
  13463 
  13464         if (air_tag == .shr_exact) {
  13465             const back = try block.addBinOp(.shl, result, rhs);
  13466 
  13467             const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: {
  13468                 const eql = try block.addCmpVector(lhs, back, .eq);
  13469                 break :ok try block.addInst(.{
  13470                     .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  13471                     .data = .{ .reduce = .{
  13472                         .operand = eql,
  13473                         .operation = .And,
  13474                     } },
  13475                 });
  13476             } else try block.addBinOp(.cmp_eq, lhs, back);
  13477             try sema.addSafetyCheck(block, src, ok, .shr_overflow);
  13478         }
  13479     }
  13480     return result;
  13481 }
  13482 
  13483 fn zirBitwise(
  13484     sema: *Sema,
  13485     block: *Block,
  13486     inst: Zir.Inst.Index,
  13487     air_tag: Air.Inst.Tag,
  13488 ) CompileError!Air.Inst.Ref {
  13489     const tracy = trace(@src());
  13490     defer tracy.end();
  13491 
  13492     const mod = sema.mod;
  13493     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13494     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  13495     sema.src = src;
  13496     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  13497     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  13498     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13499     const lhs = try sema.resolveInst(extra.lhs);
  13500     const rhs = try sema.resolveInst(extra.rhs);
  13501     const lhs_ty = sema.typeOf(lhs);
  13502     const rhs_ty = sema.typeOf(rhs);
  13503     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  13504 
  13505     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  13506     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } });
  13507     const scalar_type = resolved_type.scalarType(mod);
  13508     const scalar_tag = scalar_type.zigTypeTag(mod);
  13509 
  13510     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  13511     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  13512 
  13513     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  13514 
  13515     if (!is_int) {
  13516         return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag(mod)), @tagName(rhs_ty.zigTypeTag(mod)) });
  13517     }
  13518 
  13519     const runtime_src = runtime: {
  13520         // TODO: ask the linker what kind of relocations are available, and
  13521         // in some cases emit a Value that means "this decl's address AND'd with this operand".
  13522         if (try sema.resolveValueIntable(casted_lhs)) |lhs_val| {
  13523             if (try sema.resolveValueIntable(casted_rhs)) |rhs_val| {
  13524                 const result_val = switch (air_tag) {
  13525                     .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, mod),
  13526                     .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, mod),
  13527                     .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, mod),
  13528                     else => unreachable,
  13529                 };
  13530                 return Air.internedToRef(result_val.toIntern());
  13531             } else {
  13532                 break :runtime rhs_src;
  13533             }
  13534         } else {
  13535             break :runtime lhs_src;
  13536         }
  13537     };
  13538 
  13539     try sema.requireRuntimeBlock(block, src, runtime_src);
  13540     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  13541 }
  13542 
  13543 fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13544     const tracy = trace(@src());
  13545     defer tracy.end();
  13546 
  13547     const mod = sema.mod;
  13548     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  13549     const src = inst_data.src();
  13550     const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
  13551 
  13552     const operand = try sema.resolveInst(inst_data.operand);
  13553     const operand_type = sema.typeOf(operand);
  13554     const scalar_type = operand_type.scalarType(mod);
  13555 
  13556     if (scalar_type.zigTypeTag(mod) != .Int) {
  13557         return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{
  13558             operand_type.fmt(mod),
  13559         });
  13560     }
  13561 
  13562     if (try sema.resolveValue(operand)) |val| {
  13563         if (val.isUndef(mod)) {
  13564             return mod.undefRef(operand_type);
  13565         } else if (operand_type.zigTypeTag(mod) == .Vector) {
  13566             const vec_len = try sema.usizeCast(block, operand_src, operand_type.vectorLen(mod));
  13567             const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  13568             for (elems, 0..) |*elem, i| {
  13569                 const elem_val = try val.elemValue(mod, i);
  13570                 elem.* = try (try elem_val.bitwiseNot(scalar_type, sema.arena, mod)).intern(scalar_type, mod);
  13571             }
  13572             return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  13573                 .ty = operand_type.toIntern(),
  13574                 .storage = .{ .elems = elems },
  13575             } })));
  13576         } else {
  13577             const result_val = try val.bitwiseNot(operand_type, sema.arena, mod);
  13578             return Air.internedToRef(result_val.toIntern());
  13579         }
  13580     }
  13581 
  13582     try sema.requireRuntimeBlock(block, src, null);
  13583     return block.addTyOp(.not, operand_type, operand);
  13584 }
  13585 
  13586 fn analyzeTupleCat(
  13587     sema: *Sema,
  13588     block: *Block,
  13589     src_node: i32,
  13590     lhs: Air.Inst.Ref,
  13591     rhs: Air.Inst.Ref,
  13592 ) CompileError!Air.Inst.Ref {
  13593     const mod = sema.mod;
  13594     const lhs_ty = sema.typeOf(lhs);
  13595     const rhs_ty = sema.typeOf(rhs);
  13596     const src = LazySrcLoc.nodeOffset(src_node);
  13597 
  13598     const lhs_len = lhs_ty.structFieldCount(mod);
  13599     const rhs_len = rhs_ty.structFieldCount(mod);
  13600     const dest_fields = lhs_len + rhs_len;
  13601 
  13602     if (dest_fields == 0) {
  13603         return Air.internedToRef(Value.empty_struct.toIntern());
  13604     }
  13605     if (lhs_len == 0) {
  13606         return rhs;
  13607     }
  13608     if (rhs_len == 0) {
  13609         return lhs;
  13610     }
  13611     const final_len = try sema.usizeCast(block, src, dest_fields);
  13612 
  13613     const types = try sema.arena.alloc(InternPool.Index, final_len);
  13614     const values = try sema.arena.alloc(InternPool.Index, final_len);
  13615 
  13616     const opt_runtime_src = rs: {
  13617         var runtime_src: ?LazySrcLoc = null;
  13618         var i: u32 = 0;
  13619         while (i < lhs_len) : (i += 1) {
  13620             types[i] = lhs_ty.structFieldType(i, mod).toIntern();
  13621             const default_val = lhs_ty.structFieldDefaultValue(i, mod);
  13622             values[i] = default_val.toIntern();
  13623             const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{
  13624                 .array_cat_offset = src_node,
  13625                 .elem_index = i,
  13626             } };
  13627             if (default_val.toIntern() == .unreachable_value) {
  13628                 runtime_src = operand_src;
  13629                 values[i] = .none;
  13630             }
  13631         }
  13632         i = 0;
  13633         while (i < rhs_len) : (i += 1) {
  13634             types[i + lhs_len] = rhs_ty.structFieldType(i, mod).toIntern();
  13635             const default_val = rhs_ty.structFieldDefaultValue(i, mod);
  13636             values[i + lhs_len] = default_val.toIntern();
  13637             const operand_src: LazySrcLoc = .{ .array_cat_rhs = .{
  13638                 .array_cat_offset = src_node,
  13639                 .elem_index = i,
  13640             } };
  13641             if (default_val.toIntern() == .unreachable_value) {
  13642                 runtime_src = operand_src;
  13643                 values[i + lhs_len] = .none;
  13644             }
  13645         }
  13646         break :rs runtime_src;
  13647     };
  13648 
  13649     const tuple_ty = try mod.intern_pool.getAnonStructType(mod.gpa, .{
  13650         .types = types,
  13651         .values = values,
  13652         .names = &.{},
  13653     });
  13654 
  13655     const runtime_src = opt_runtime_src orelse {
  13656         const tuple_val = try mod.intern(.{ .aggregate = .{
  13657             .ty = tuple_ty,
  13658             .storage = .{ .elems = values },
  13659         } });
  13660         return Air.internedToRef(tuple_val);
  13661     };
  13662 
  13663     try sema.requireRuntimeBlock(block, src, runtime_src);
  13664 
  13665     const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
  13666     var i: u32 = 0;
  13667     while (i < lhs_len) : (i += 1) {
  13668         const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{
  13669             .array_cat_offset = src_node,
  13670             .elem_index = i,
  13671         } };
  13672         element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, i, lhs_ty);
  13673     }
  13674     i = 0;
  13675     while (i < rhs_len) : (i += 1) {
  13676         const operand_src: LazySrcLoc = .{ .array_cat_rhs = .{
  13677             .array_cat_offset = src_node,
  13678             .elem_index = i,
  13679         } };
  13680         element_refs[i + lhs_len] =
  13681             try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty);
  13682     }
  13683 
  13684     return block.addAggregateInit(Type.fromInterned(tuple_ty), element_refs);
  13685 }
  13686 
  13687 fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13688     const tracy = trace(@src());
  13689     defer tracy.end();
  13690 
  13691     const mod = sema.mod;
  13692     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13693     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13694     const lhs = try sema.resolveInst(extra.lhs);
  13695     const rhs = try sema.resolveInst(extra.rhs);
  13696     const lhs_ty = sema.typeOf(lhs);
  13697     const rhs_ty = sema.typeOf(rhs);
  13698     const src = inst_data.src();
  13699 
  13700     const lhs_is_tuple = lhs_ty.isTuple(mod);
  13701     const rhs_is_tuple = rhs_ty.isTuple(mod);
  13702     if (lhs_is_tuple and rhs_is_tuple) {
  13703         return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs);
  13704     }
  13705 
  13706     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  13707     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  13708 
  13709     const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: {
  13710         if (lhs_is_tuple) break :lhs_info @as(Type.ArrayInfo, undefined);
  13711         return sema.fail(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)});
  13712     };
  13713     const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse {
  13714         assert(!rhs_is_tuple);
  13715         return sema.fail(block, rhs_src, "expected indexable; found '{}'", .{rhs_ty.fmt(mod)});
  13716     };
  13717 
  13718     const resolved_elem_ty = t: {
  13719         var trash_block = block.makeSubBlock();
  13720         trash_block.is_comptime = false;
  13721         defer trash_block.instructions.deinit(sema.gpa);
  13722 
  13723         const instructions = [_]Air.Inst.Ref{
  13724             try trash_block.addBitCast(lhs_info.elem_type, .void_value),
  13725             try trash_block.addBitCast(rhs_info.elem_type, .void_value),
  13726         };
  13727         break :t try sema.resolvePeerTypes(block, src, &instructions, .{
  13728             .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  13729         });
  13730     };
  13731 
  13732     // When there is a sentinel mismatch, no sentinel on the result.
  13733     // Otherwise, use the sentinel value provided by either operand,
  13734     // coercing it to the peer-resolved element type.
  13735     const res_sent_val: ?Value = s: {
  13736         if (lhs_info.sentinel) |lhs_sent_val| {
  13737             const lhs_sent = Air.internedToRef(lhs_sent_val.toIntern());
  13738             if (rhs_info.sentinel) |rhs_sent_val| {
  13739                 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern());
  13740                 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
  13741                 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
  13742                 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?;
  13743                 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?;
  13744                 if (try sema.valuesEqual(lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) {
  13745                     break :s lhs_sent_casted_val;
  13746                 } else {
  13747                     break :s null;
  13748                 }
  13749             } else {
  13750                 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
  13751                 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?;
  13752                 break :s lhs_sent_casted_val;
  13753             }
  13754         } else {
  13755             if (rhs_info.sentinel) |rhs_sent_val| {
  13756                 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern());
  13757                 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
  13758                 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?;
  13759                 break :s rhs_sent_casted_val;
  13760             } else {
  13761                 break :s null;
  13762             }
  13763         }
  13764     };
  13765 
  13766     const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
  13767     const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len);
  13768     const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) {
  13769         error.Overflow => return sema.fail(
  13770             block,
  13771             src,
  13772             "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle",
  13773             .{ lhs_len, rhs_len },
  13774         ),
  13775     };
  13776 
  13777     const result_ty = try mod.arrayType(.{
  13778         .len = result_len,
  13779         .sentinel = if (res_sent_val) |v| v.toIntern() else .none,
  13780         .child = resolved_elem_ty.toIntern(),
  13781     });
  13782     const ptr_addrspace = p: {
  13783         if (lhs_ty.zigTypeTag(mod) == .Pointer) break :p lhs_ty.ptrAddressSpace(mod);
  13784         if (rhs_ty.zigTypeTag(mod) == .Pointer) break :p rhs_ty.ptrAddressSpace(mod);
  13785         break :p null;
  13786     };
  13787 
  13788     const runtime_src = if (switch (lhs_ty.zigTypeTag(mod)) {
  13789         .Array, .Struct => try sema.resolveValue(lhs),
  13790         .Pointer => try sema.resolveDefinedValue(block, lhs_src, lhs),
  13791         else => unreachable,
  13792     }) |lhs_val| rs: {
  13793         if (switch (rhs_ty.zigTypeTag(mod)) {
  13794             .Array, .Struct => try sema.resolveValue(rhs),
  13795             .Pointer => try sema.resolveDefinedValue(block, rhs_src, rhs),
  13796             else => unreachable,
  13797         }) |rhs_val| {
  13798             const lhs_sub_val = if (lhs_ty.isSinglePointer(mod))
  13799                 (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
  13800             else
  13801                 lhs_val;
  13802 
  13803             const rhs_sub_val = if (rhs_ty.isSinglePointer(mod))
  13804                 (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).?
  13805             else
  13806                 rhs_val;
  13807 
  13808             const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
  13809             var elem_i: u32 = 0;
  13810             while (elem_i < lhs_len) : (elem_i += 1) {
  13811                 const lhs_elem_i = elem_i;
  13812                 const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, mod) else Value.@"unreachable";
  13813                 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(mod, lhs_elem_i) else elem_default_val;
  13814                 const elem_val_inst = Air.internedToRef(elem_val.toIntern());
  13815                 const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{
  13816                     .array_cat_offset = inst_data.src_node,
  13817                     .elem_index = elem_i,
  13818                 } };
  13819                 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src);
  13820                 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined);
  13821                 element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod);
  13822             }
  13823             while (elem_i < result_len) : (elem_i += 1) {
  13824                 const rhs_elem_i = elem_i - lhs_len;
  13825                 const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, mod) else Value.@"unreachable";
  13826                 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(mod, rhs_elem_i) else elem_default_val;
  13827                 const elem_val_inst = Air.internedToRef(elem_val.toIntern());
  13828                 const operand_src: LazySrcLoc = .{ .array_cat_rhs = .{
  13829                     .array_cat_offset = inst_data.src_node,
  13830                     .elem_index = @intCast(rhs_elem_i),
  13831                 } };
  13832                 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src);
  13833                 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined);
  13834                 element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod);
  13835             }
  13836             return sema.addConstantMaybeRef(try mod.intern(.{ .aggregate = .{
  13837                 .ty = result_ty.toIntern(),
  13838                 .storage = .{ .elems = element_vals },
  13839             } }), ptr_addrspace != null);
  13840         } else break :rs rhs_src;
  13841     } else lhs_src;
  13842 
  13843     try sema.requireRuntimeBlock(block, src, runtime_src);
  13844 
  13845     if (ptr_addrspace) |ptr_as| {
  13846         const alloc_ty = try sema.ptrType(.{
  13847             .child = result_ty.toIntern(),
  13848             .flags = .{ .address_space = ptr_as },
  13849         });
  13850         const alloc = try block.addTy(.alloc, alloc_ty);
  13851         const elem_ptr_ty = try sema.ptrType(.{
  13852             .child = resolved_elem_ty.toIntern(),
  13853             .flags = .{ .address_space = ptr_as },
  13854         });
  13855 
  13856         var elem_i: u32 = 0;
  13857         while (elem_i < lhs_len) : (elem_i += 1) {
  13858             const elem_index = try mod.intRef(Type.usize, elem_i);
  13859             const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  13860             const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{
  13861                 .array_cat_offset = inst_data.src_node,
  13862                 .elem_index = elem_i,
  13863             } };
  13864             const init = try sema.elemVal(block, operand_src, lhs, elem_index, src, true);
  13865             try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store);
  13866         }
  13867         while (elem_i < result_len) : (elem_i += 1) {
  13868             const rhs_elem_i = elem_i - lhs_len;
  13869             const elem_index = try mod.intRef(Type.usize, elem_i);
  13870             const rhs_index = try mod.intRef(Type.usize, rhs_elem_i);
  13871             const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  13872             const operand_src: LazySrcLoc = .{ .array_cat_rhs = .{
  13873                 .array_cat_offset = inst_data.src_node,
  13874                 .elem_index = @intCast(rhs_elem_i),
  13875             } };
  13876             const init = try sema.elemVal(block, operand_src, rhs, rhs_index, src, true);
  13877             try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store);
  13878         }
  13879         if (res_sent_val) |sent_val| {
  13880             const elem_index = try mod.intRef(Type.usize, result_len);
  13881             const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  13882             const init = Air.internedToRef((try mod.getCoerced(sent_val, lhs_info.elem_type)).toIntern());
  13883             try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  13884         }
  13885 
  13886         return alloc;
  13887     }
  13888 
  13889     const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
  13890     {
  13891         var elem_i: u32 = 0;
  13892         while (elem_i < lhs_len) : (elem_i += 1) {
  13893             const index = try mod.intRef(Type.usize, elem_i);
  13894             const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{
  13895                 .array_cat_offset = inst_data.src_node,
  13896                 .elem_index = elem_i,
  13897             } };
  13898             const init = try sema.elemVal(block, operand_src, lhs, index, src, true);
  13899             element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src);
  13900         }
  13901         while (elem_i < result_len) : (elem_i += 1) {
  13902             const rhs_elem_i = elem_i - lhs_len;
  13903             const index = try mod.intRef(Type.usize, rhs_elem_i);
  13904             const operand_src: LazySrcLoc = .{ .array_cat_rhs = .{
  13905                 .array_cat_offset = inst_data.src_node,
  13906                 .elem_index = @intCast(rhs_elem_i),
  13907             } };
  13908             const init = try sema.elemVal(block, operand_src, rhs, index, src, true);
  13909             element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src);
  13910         }
  13911     }
  13912 
  13913     return block.addAggregateInit(result_ty, element_refs);
  13914 }
  13915 
  13916 fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo {
  13917     const mod = sema.mod;
  13918     const operand_ty = sema.typeOf(operand);
  13919     switch (operand_ty.zigTypeTag(mod)) {
  13920         .Array => return operand_ty.arrayInfo(mod),
  13921         .Pointer => {
  13922             const ptr_info = operand_ty.ptrInfo(mod);
  13923             switch (ptr_info.flags.size) {
  13924                 // TODO: in the Many case here this should only work if the type
  13925                 // has a sentinel, and this code should compute the length based
  13926                 // on the sentinel value.
  13927                 .Slice, .Many => {
  13928                     const val = try sema.resolveConstDefinedValue(block, src, operand, .{
  13929                         .needed_comptime_reason = "slice value being concatenated must be comptime-known",
  13930                     });
  13931                     return Type.ArrayInfo{
  13932                         .elem_type = Type.fromInterned(ptr_info.child),
  13933                         .sentinel = switch (ptr_info.sentinel) {
  13934                             .none => null,
  13935                             else => Value.fromInterned(ptr_info.sentinel),
  13936                         },
  13937                         .len = val.sliceLen(mod),
  13938                     };
  13939                 },
  13940                 .One => {
  13941                     if (Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Array) {
  13942                         return Type.fromInterned(ptr_info.child).arrayInfo(mod);
  13943                     }
  13944                 },
  13945                 .C => {},
  13946             }
  13947         },
  13948         .Struct => {
  13949             if (operand_ty.isTuple(mod) and peer_ty.isIndexable(mod)) {
  13950                 assert(!peer_ty.isTuple(mod));
  13951                 return .{
  13952                     .elem_type = peer_ty.elemType2(mod),
  13953                     .sentinel = null,
  13954                     .len = operand_ty.arrayLen(mod),
  13955                 };
  13956             }
  13957         },
  13958         else => {},
  13959     }
  13960     return null;
  13961 }
  13962 
  13963 fn analyzeTupleMul(
  13964     sema: *Sema,
  13965     block: *Block,
  13966     src_node: i32,
  13967     operand: Air.Inst.Ref,
  13968     factor: usize,
  13969 ) CompileError!Air.Inst.Ref {
  13970     const mod = sema.mod;
  13971     const operand_ty = sema.typeOf(operand);
  13972     const src = LazySrcLoc.nodeOffset(src_node);
  13973     const len_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
  13974 
  13975     const tuple_len = operand_ty.structFieldCount(mod);
  13976     const final_len = std.math.mul(usize, tuple_len, factor) catch
  13977         return sema.fail(block, len_src, "operation results in overflow", .{});
  13978 
  13979     if (final_len == 0) {
  13980         return Air.internedToRef(Value.empty_struct.toIntern());
  13981     }
  13982     const types = try sema.arena.alloc(InternPool.Index, final_len);
  13983     const values = try sema.arena.alloc(InternPool.Index, final_len);
  13984 
  13985     const opt_runtime_src = rs: {
  13986         var runtime_src: ?LazySrcLoc = null;
  13987         for (0..tuple_len) |i| {
  13988             types[i] = operand_ty.structFieldType(i, mod).toIntern();
  13989             values[i] = operand_ty.structFieldDefaultValue(i, mod).toIntern();
  13990             const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{
  13991                 .array_cat_offset = src_node,
  13992                 .elem_index = @intCast(i),
  13993             } };
  13994             if (values[i] == .unreachable_value) {
  13995                 runtime_src = operand_src;
  13996                 values[i] = .none; // TODO don't treat unreachable_value as special
  13997             }
  13998         }
  13999         for (0..factor) |i| {
  14000             mem.copyForwards(InternPool.Index, types[tuple_len * i ..], types[0..tuple_len]);
  14001             mem.copyForwards(InternPool.Index, values[tuple_len * i ..], values[0..tuple_len]);
  14002         }
  14003         break :rs runtime_src;
  14004     };
  14005 
  14006     const tuple_ty = try mod.intern_pool.getAnonStructType(mod.gpa, .{
  14007         .types = types,
  14008         .values = values,
  14009         .names = &.{},
  14010     });
  14011 
  14012     const runtime_src = opt_runtime_src orelse {
  14013         const tuple_val = try mod.intern(.{ .aggregate = .{
  14014             .ty = tuple_ty,
  14015             .storage = .{ .elems = values },
  14016         } });
  14017         return Air.internedToRef(tuple_val);
  14018     };
  14019 
  14020     try sema.requireRuntimeBlock(block, src, runtime_src);
  14021 
  14022     const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
  14023     var i: u32 = 0;
  14024     while (i < tuple_len) : (i += 1) {
  14025         const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{
  14026             .array_cat_offset = src_node,
  14027             .elem_index = i,
  14028         } };
  14029         element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @intCast(i), operand_ty);
  14030     }
  14031     i = 1;
  14032     while (i < factor) : (i += 1) {
  14033         @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]);
  14034     }
  14035 
  14036     return block.addAggregateInit(Type.fromInterned(tuple_ty), element_refs);
  14037 }
  14038 
  14039 fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14040     const tracy = trace(@src());
  14041     defer tracy.end();
  14042 
  14043     const mod = sema.mod;
  14044     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14045     const extra = sema.code.extraData(Zir.Inst.ArrayMul, inst_data.payload_index).data;
  14046     const uncoerced_lhs = try sema.resolveInst(extra.lhs);
  14047     const uncoerced_lhs_ty = sema.typeOf(uncoerced_lhs);
  14048     const src: LazySrcLoc = inst_data.src();
  14049     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14050     const operator_src: LazySrcLoc = .{ .node_offset_main_token = inst_data.src_node };
  14051     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14052 
  14053     const lhs, const lhs_ty = coerced_lhs: {
  14054         // If we have a result type, we might be able to do this more efficiently
  14055         // by coercing the LHS first. Specifically, if we want an array or vector
  14056         // and have a tuple, coerce the tuple immediately.
  14057         no_coerce: {
  14058             if (extra.res_ty == .none) break :no_coerce;
  14059             const res_ty_inst = try sema.resolveInst(extra.res_ty);
  14060             const res_ty = try sema.analyzeAsType(block, src, res_ty_inst);
  14061             if (res_ty.isGenericPoison()) break :no_coerce;
  14062             if (!uncoerced_lhs_ty.isTuple(mod)) break :no_coerce;
  14063             const lhs_len = uncoerced_lhs_ty.structFieldCount(mod);
  14064             const lhs_dest_ty = switch (res_ty.zigTypeTag(mod)) {
  14065                 else => break :no_coerce,
  14066                 .Array => try mod.arrayType(.{
  14067                     .child = res_ty.childType(mod).toIntern(),
  14068                     .len = lhs_len,
  14069                     .sentinel = if (res_ty.sentinel(mod)) |s| s.toIntern() else .none,
  14070                 }),
  14071                 .Vector => try mod.vectorType(.{
  14072                     .child = res_ty.childType(mod).toIntern(),
  14073                     .len = lhs_len,
  14074                 }),
  14075             };
  14076             // Attempt to coerce to this type, but don't emit an error if it fails. Instead,
  14077             // just exit out of this path and let the usual error happen later, so that error
  14078             // messages are consistent.
  14079             const coerced = sema.coerceExtra(block, lhs_dest_ty, uncoerced_lhs, lhs_src, .{ .report_err = false }) catch |err| switch (err) {
  14080                 error.NotCoercible => break :no_coerce,
  14081                 else => |e| return e,
  14082             };
  14083             break :coerced_lhs .{ coerced, lhs_dest_ty };
  14084         }
  14085         break :coerced_lhs .{ uncoerced_lhs, uncoerced_lhs_ty };
  14086     };
  14087 
  14088     if (lhs_ty.isTuple(mod)) {
  14089         // In `**` rhs must be comptime-known, but lhs can be runtime-known
  14090         const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, .{
  14091             .needed_comptime_reason = "array multiplication factor must be comptime-known",
  14092         });
  14093         const factor_casted = try sema.usizeCast(block, rhs_src, factor);
  14094         return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor_casted);
  14095     }
  14096 
  14097     // Analyze the lhs first, to catch the case that someone tried to do exponentiation
  14098     const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse {
  14099         const msg = msg: {
  14100             const msg = try sema.errMsg(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)});
  14101             errdefer msg.destroy(sema.gpa);
  14102             switch (lhs_ty.zigTypeTag(mod)) {
  14103                 .Int, .Float, .ComptimeFloat, .ComptimeInt, .Vector => {
  14104                     try sema.errNote(block, operator_src, msg, "this operator multiplies arrays; use std.math.pow for exponentiation", .{});
  14105                 },
  14106                 else => {},
  14107             }
  14108             break :msg msg;
  14109         };
  14110         return sema.failWithOwnedErrorMsg(block, msg);
  14111     };
  14112 
  14113     // In `**` rhs must be comptime-known, but lhs can be runtime-known
  14114     const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, .{
  14115         .needed_comptime_reason = "array multiplication factor must be comptime-known",
  14116     });
  14117 
  14118     const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch
  14119         return sema.fail(block, rhs_src, "operation results in overflow", .{});
  14120     const result_len = try sema.usizeCast(block, src, result_len_u64);
  14121 
  14122     const result_ty = try mod.arrayType(.{
  14123         .len = result_len,
  14124         .sentinel = if (lhs_info.sentinel) |s| s.toIntern() else .none,
  14125         .child = lhs_info.elem_type.toIntern(),
  14126     });
  14127 
  14128     const ptr_addrspace = if (lhs_ty.zigTypeTag(mod) == .Pointer) lhs_ty.ptrAddressSpace(mod) else null;
  14129     const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
  14130 
  14131     if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
  14132         const lhs_sub_val = if (lhs_ty.isSinglePointer(mod))
  14133             (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
  14134         else
  14135             lhs_val;
  14136 
  14137         const val = v: {
  14138             // Optimization for the common pattern of a single element repeated N times, such
  14139             // as zero-filling a byte array.
  14140             if (lhs_len == 1 and lhs_info.sentinel == null) {
  14141                 const elem_val = try lhs_sub_val.elemValue(mod, 0);
  14142                 break :v try mod.intern(.{ .aggregate = .{
  14143                     .ty = result_ty.toIntern(),
  14144                     .storage = .{ .repeated_elem = elem_val.toIntern() },
  14145                 } });
  14146             }
  14147 
  14148             const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
  14149             var elem_i: usize = 0;
  14150             while (elem_i < result_len) {
  14151                 var lhs_i: usize = 0;
  14152                 while (lhs_i < lhs_len) : (lhs_i += 1) {
  14153                     const elem_val = try lhs_sub_val.elemValue(mod, lhs_i);
  14154                     element_vals[elem_i] = elem_val.toIntern();
  14155                     elem_i += 1;
  14156                 }
  14157             }
  14158             break :v try mod.intern(.{ .aggregate = .{
  14159                 .ty = result_ty.toIntern(),
  14160                 .storage = .{ .elems = element_vals },
  14161             } });
  14162         };
  14163         return sema.addConstantMaybeRef(val, ptr_addrspace != null);
  14164     }
  14165 
  14166     try sema.requireRuntimeBlock(block, src, lhs_src);
  14167 
  14168     // Grab all the LHS values ahead of time, rather than repeatedly emitting instructions
  14169     // to get the same elem values.
  14170     const lhs_vals = try sema.arena.alloc(Air.Inst.Ref, lhs_len);
  14171     for (lhs_vals, 0..) |*lhs_val, idx| {
  14172         const idx_ref = try mod.intRef(Type.usize, idx);
  14173         lhs_val.* = try sema.elemVal(block, lhs_src, lhs, idx_ref, src, false);
  14174     }
  14175 
  14176     if (ptr_addrspace) |ptr_as| {
  14177         const alloc_ty = try sema.ptrType(.{
  14178             .child = result_ty.toIntern(),
  14179             .flags = .{ .address_space = ptr_as },
  14180         });
  14181         const alloc = try block.addTy(.alloc, alloc_ty);
  14182         const elem_ptr_ty = try sema.ptrType(.{
  14183             .child = lhs_info.elem_type.toIntern(),
  14184             .flags = .{ .address_space = ptr_as },
  14185         });
  14186 
  14187         var elem_i: usize = 0;
  14188         while (elem_i < result_len) {
  14189             for (lhs_vals) |lhs_val| {
  14190                 const elem_index = try mod.intRef(Type.usize, elem_i);
  14191                 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  14192                 try sema.storePtr2(block, src, elem_ptr, src, lhs_val, lhs_src, .store);
  14193                 elem_i += 1;
  14194             }
  14195         }
  14196         if (lhs_info.sentinel) |sent_val| {
  14197             const elem_index = try mod.intRef(Type.usize, result_len);
  14198             const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  14199             const init = Air.internedToRef(sent_val.toIntern());
  14200             try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  14201         }
  14202 
  14203         return alloc;
  14204     }
  14205 
  14206     const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
  14207     for (0..try sema.usizeCast(block, rhs_src, factor)) |i| {
  14208         @memcpy(element_refs[i * lhs_len ..][0..lhs_len], lhs_vals);
  14209     }
  14210     return block.addAggregateInit(result_ty, element_refs);
  14211 }
  14212 
  14213 fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14214     const mod = sema.mod;
  14215     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  14216     const src = inst_data.src();
  14217     const lhs_src = src;
  14218     const rhs_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
  14219 
  14220     const rhs = try sema.resolveInst(inst_data.operand);
  14221     const rhs_ty = sema.typeOf(rhs);
  14222     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  14223 
  14224     if (rhs_scalar_ty.isUnsignedInt(mod) or switch (rhs_scalar_ty.zigTypeTag(mod)) {
  14225         .Int, .ComptimeInt, .Float, .ComptimeFloat => false,
  14226         else => true,
  14227     }) {
  14228         return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(mod)});
  14229     }
  14230 
  14231     if (rhs_scalar_ty.isAnyFloat()) {
  14232         // We handle float negation here to ensure negative zero is represented in the bits.
  14233         if (try sema.resolveValue(rhs)) |rhs_val| {
  14234             if (rhs_val.isUndef(mod)) return mod.undefRef(rhs_ty);
  14235             return Air.internedToRef((try rhs_val.floatNeg(rhs_ty, sema.arena, mod)).toIntern());
  14236         }
  14237         try sema.requireRuntimeBlock(block, src, null);
  14238         return block.addUnOp(if (block.float_mode == .Optimized) .neg_optimized else .neg, rhs);
  14239     }
  14240 
  14241     const lhs = Air.internedToRef((try sema.splat(rhs_ty, try mod.intValue(rhs_scalar_ty, 0))).toIntern());
  14242     return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true);
  14243 }
  14244 
  14245 fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14246     const mod = sema.mod;
  14247     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  14248     const src = inst_data.src();
  14249     const lhs_src = src;
  14250     const rhs_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
  14251 
  14252     const rhs = try sema.resolveInst(inst_data.operand);
  14253     const rhs_ty = sema.typeOf(rhs);
  14254     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  14255 
  14256     switch (rhs_scalar_ty.zigTypeTag(mod)) {
  14257         .Int, .ComptimeInt, .Float, .ComptimeFloat => {},
  14258         else => return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(mod)}),
  14259     }
  14260 
  14261     const lhs = Air.internedToRef((try sema.splat(rhs_ty, try mod.intValue(rhs_scalar_ty, 0))).toIntern());
  14262     return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true);
  14263 }
  14264 
  14265 fn zirArithmetic(
  14266     sema: *Sema,
  14267     block: *Block,
  14268     inst: Zir.Inst.Index,
  14269     zir_tag: Zir.Inst.Tag,
  14270     safety: bool,
  14271 ) CompileError!Air.Inst.Ref {
  14272     const tracy = trace(@src());
  14273     defer tracy.end();
  14274 
  14275     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14276     sema.src = .{ .node_offset_bin_op = inst_data.src_node };
  14277     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14278     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14279     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14280     const lhs = try sema.resolveInst(extra.lhs);
  14281     const rhs = try sema.resolveInst(extra.rhs);
  14282 
  14283     return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src, safety);
  14284 }
  14285 
  14286 fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14287     const mod = sema.mod;
  14288     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14289     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  14290     sema.src = src;
  14291     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14292     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14293     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14294     const lhs = try sema.resolveInst(extra.lhs);
  14295     const rhs = try sema.resolveInst(extra.rhs);
  14296     const lhs_ty = sema.typeOf(lhs);
  14297     const rhs_ty = sema.typeOf(rhs);
  14298     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  14299     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  14300     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14301     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  14302 
  14303     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  14304     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  14305         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14306     });
  14307 
  14308     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14309     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14310 
  14311     const lhs_scalar_ty = lhs_ty.scalarType(mod);
  14312     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  14313     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  14314 
  14315     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  14316 
  14317     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div);
  14318 
  14319     const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs);
  14320     const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs);
  14321 
  14322     if ((lhs_ty.zigTypeTag(mod) == .ComptimeFloat and rhs_ty.zigTypeTag(mod) == .ComptimeInt) or
  14323         (lhs_ty.zigTypeTag(mod) == .ComptimeInt and rhs_ty.zigTypeTag(mod) == .ComptimeFloat))
  14324     {
  14325         // If it makes a difference whether we coerce to ints or floats before doing the division, error.
  14326         // If lhs % rhs is 0, it doesn't matter.
  14327         const lhs_val = maybe_lhs_val orelse unreachable;
  14328         const rhs_val = maybe_rhs_val orelse unreachable;
  14329         const rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod) catch unreachable;
  14330         if (!rem.compareAllWithZero(.eq, mod)) {
  14331             return sema.fail(
  14332                 block,
  14333                 src,
  14334                 "ambiguous coercion of division operands '{}' and '{}'; non-zero remainder '{}'",
  14335                 .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod), rem.fmtValue(resolved_type, mod) },
  14336             );
  14337         }
  14338     }
  14339 
  14340     // TODO: emit compile error when .div is used on integers and there would be an
  14341     // ambiguous result between div_floor and div_trunc.
  14342 
  14343     // For integers:
  14344     // If the lhs is zero, then zero is returned regardless of rhs.
  14345     // If the rhs is zero, compile error for division by zero.
  14346     // If the rhs is undefined, compile error because there is a possible
  14347     // value (zero) for which the division would be illegal behavior.
  14348     // If the lhs is undefined:
  14349     //   * if lhs type is signed:
  14350     //     * if rhs is comptime-known and not -1, result is undefined
  14351     //     * if rhs is -1 or runtime-known, compile error because there is a
  14352     //        possible value (-min_int / -1)  for which division would be
  14353     //        illegal behavior.
  14354     //   * if lhs type is unsigned, undef is returned regardless of rhs.
  14355     //
  14356     // For floats:
  14357     // If the rhs is zero:
  14358     //  * comptime_float: compile error for division by zero.
  14359     //  * other float type:
  14360     //    * if the lhs is zero: QNaN
  14361     //    * otherwise: +Inf or -Inf depending on lhs sign
  14362     // If the rhs is undefined:
  14363     //  * comptime_float: compile error because there is a possible
  14364     //    value (zero) for which the division would be illegal behavior.
  14365     //  * other float type: result is undefined
  14366     // If the lhs is undefined, result is undefined.
  14367     switch (scalar_tag) {
  14368         .Int, .ComptimeInt, .ComptimeFloat => {
  14369             if (maybe_lhs_val) |lhs_val| {
  14370                 if (!lhs_val.isUndef(mod)) {
  14371                     if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  14372                         const scalar_zero = switch (scalar_tag) {
  14373                             .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  14374                             .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  14375                             else => unreachable,
  14376                         };
  14377                         const zero_val = try sema.splat(resolved_type, scalar_zero);
  14378                         return Air.internedToRef(zero_val.toIntern());
  14379                     }
  14380                 }
  14381             }
  14382             if (maybe_rhs_val) |rhs_val| {
  14383                 if (rhs_val.isUndef(mod)) {
  14384                     return sema.failWithUseOfUndef(block, rhs_src);
  14385                 }
  14386                 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14387                     return sema.failWithDivideByZero(block, rhs_src);
  14388                 }
  14389                 // TODO: if the RHS is one, return the LHS directly
  14390             }
  14391         },
  14392         else => {},
  14393     }
  14394 
  14395     const runtime_src = rs: {
  14396         if (maybe_lhs_val) |lhs_val| {
  14397             if (lhs_val.isUndef(mod)) {
  14398                 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) {
  14399                     if (maybe_rhs_val) |rhs_val| {
  14400                         if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) {
  14401                             return mod.undefRef(resolved_type);
  14402                         }
  14403                     }
  14404                     return sema.failWithUseOfUndef(block, rhs_src);
  14405                 }
  14406                 return mod.undefRef(resolved_type);
  14407             }
  14408 
  14409             if (maybe_rhs_val) |rhs_val| {
  14410                 if (is_int) {
  14411                     var overflow_idx: ?usize = null;
  14412                     const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
  14413                     if (overflow_idx) |vec_idx| {
  14414                         return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
  14415                     }
  14416                     return Air.internedToRef(res.toIntern());
  14417                 } else {
  14418                     return Air.internedToRef((try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, mod)).toIntern());
  14419                 }
  14420             } else {
  14421                 break :rs rhs_src;
  14422             }
  14423         } else {
  14424             break :rs lhs_src;
  14425         }
  14426     };
  14427 
  14428     try sema.requireRuntimeBlock(block, src, runtime_src);
  14429 
  14430     if (block.wantSafety()) {
  14431         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  14432         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14433     }
  14434 
  14435     const air_tag = if (is_int) blk: {
  14436         if (lhs_ty.isSignedInt(mod) or rhs_ty.isSignedInt(mod)) {
  14437             return sema.fail(
  14438                 block,
  14439                 src,
  14440                 "division with '{}' and '{}': signed integers must use @divTrunc, @divFloor, or @divExact",
  14441                 .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod) },
  14442             );
  14443         }
  14444         break :blk Air.Inst.Tag.div_trunc;
  14445     } else switch (block.float_mode) {
  14446         .Optimized => Air.Inst.Tag.div_float_optimized,
  14447         .Strict => Air.Inst.Tag.div_float,
  14448     };
  14449     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  14450 }
  14451 
  14452 fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14453     const mod = sema.mod;
  14454     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14455     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  14456     sema.src = src;
  14457     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14458     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14459     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14460     const lhs = try sema.resolveInst(extra.lhs);
  14461     const rhs = try sema.resolveInst(extra.rhs);
  14462     const lhs_ty = sema.typeOf(lhs);
  14463     const rhs_ty = sema.typeOf(rhs);
  14464     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  14465     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  14466     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14467     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  14468 
  14469     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  14470     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  14471         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14472     });
  14473 
  14474     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14475     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14476 
  14477     const lhs_scalar_ty = lhs_ty.scalarType(mod);
  14478     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  14479 
  14480     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  14481 
  14482     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact);
  14483 
  14484     const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs);
  14485     const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs);
  14486 
  14487     const runtime_src = rs: {
  14488         // For integers:
  14489         // If the lhs is zero, then zero is returned regardless of rhs.
  14490         // If the rhs is zero, compile error for division by zero.
  14491         // If the rhs is undefined, compile error because there is a possible
  14492         // value (zero) for which the division would be illegal behavior.
  14493         // If the lhs is undefined, compile error because there is a possible
  14494         // value for which the division would result in a remainder.
  14495         // TODO: emit runtime safety for if there is a remainder
  14496         // TODO: emit runtime safety for division by zero
  14497         //
  14498         // For floats:
  14499         // If the rhs is zero, compile error for division by zero.
  14500         // If the rhs is undefined, compile error because there is a possible
  14501         // value (zero) for which the division would be illegal behavior.
  14502         // If the lhs is undefined, compile error because there is a possible
  14503         // value for which the division would result in a remainder.
  14504         if (maybe_lhs_val) |lhs_val| {
  14505             if (lhs_val.isUndef(mod)) {
  14506                 return sema.failWithUseOfUndef(block, rhs_src);
  14507             } else {
  14508                 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  14509                     const scalar_zero = switch (scalar_tag) {
  14510                         .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  14511                         .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  14512                         else => unreachable,
  14513                     };
  14514                     const zero_val = try sema.splat(resolved_type, scalar_zero);
  14515                     return Air.internedToRef(zero_val.toIntern());
  14516                 }
  14517             }
  14518         }
  14519         if (maybe_rhs_val) |rhs_val| {
  14520             if (rhs_val.isUndef(mod)) {
  14521                 return sema.failWithUseOfUndef(block, rhs_src);
  14522             }
  14523             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14524                 return sema.failWithDivideByZero(block, rhs_src);
  14525             }
  14526             // TODO: if the RHS is one, return the LHS directly
  14527         }
  14528         if (maybe_lhs_val) |lhs_val| {
  14529             if (maybe_rhs_val) |rhs_val| {
  14530                 if (is_int) {
  14531                     const modulus_val = try lhs_val.intMod(rhs_val, resolved_type, sema.arena, mod);
  14532                     if (!(modulus_val.compareAllWithZero(.eq, mod))) {
  14533                         return sema.fail(block, src, "exact division produced remainder", .{});
  14534                     }
  14535                     var overflow_idx: ?usize = null;
  14536                     const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
  14537                     if (overflow_idx) |vec_idx| {
  14538                         return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
  14539                     }
  14540                     return Air.internedToRef(res.toIntern());
  14541                 } else {
  14542                     const modulus_val = try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, mod);
  14543                     if (!(modulus_val.compareAllWithZero(.eq, mod))) {
  14544                         return sema.fail(block, src, "exact division produced remainder", .{});
  14545                     }
  14546                     return Air.internedToRef((try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, mod)).toIntern());
  14547                 }
  14548             } else break :rs rhs_src;
  14549         } else break :rs lhs_src;
  14550     };
  14551 
  14552     try sema.requireRuntimeBlock(block, src, runtime_src);
  14553 
  14554     // Depending on whether safety is enabled, we will have a slightly different strategy
  14555     // here. The `div_exact` AIR instruction causes undefined behavior if a remainder
  14556     // is produced, so in the safety check case, it cannot be used. Instead we do a
  14557     // div_trunc and check for remainder.
  14558 
  14559     if (block.wantSafety()) {
  14560         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  14561         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14562 
  14563         const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs);
  14564         const ok = if (!is_int) ok: {
  14565             const floored = try block.addUnOp(.floor, result);
  14566 
  14567             if (resolved_type.zigTypeTag(mod) == .Vector) {
  14568                 const eql = try block.addCmpVector(result, floored, .eq);
  14569                 break :ok try block.addInst(.{
  14570                     .tag = switch (block.float_mode) {
  14571                         .Strict => .reduce,
  14572                         .Optimized => .reduce_optimized,
  14573                     },
  14574                     .data = .{ .reduce = .{
  14575                         .operand = eql,
  14576                         .operation = .And,
  14577                     } },
  14578                 });
  14579             } else {
  14580                 const is_in_range = try block.addBinOp(switch (block.float_mode) {
  14581                     .Strict => .cmp_eq,
  14582                     .Optimized => .cmp_eq_optimized,
  14583                 }, result, floored);
  14584                 break :ok is_in_range;
  14585             }
  14586         } else ok: {
  14587             const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs);
  14588 
  14589             const scalar_zero = switch (scalar_tag) {
  14590                 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  14591                 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  14592                 else => unreachable,
  14593             };
  14594             if (resolved_type.zigTypeTag(mod) == .Vector) {
  14595                 const zero_val = try sema.splat(resolved_type, scalar_zero);
  14596                 const zero = Air.internedToRef(zero_val.toIntern());
  14597                 const eql = try block.addCmpVector(remainder, zero, .eq);
  14598                 break :ok try block.addInst(.{
  14599                     .tag = .reduce,
  14600                     .data = .{ .reduce = .{
  14601                         .operand = eql,
  14602                         .operation = .And,
  14603                     } },
  14604                 });
  14605             } else {
  14606                 const zero = Air.internedToRef(scalar_zero.toIntern());
  14607                 const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero);
  14608                 break :ok is_in_range;
  14609             }
  14610         };
  14611         try sema.addSafetyCheck(block, src, ok, .exact_division_remainder);
  14612         return result;
  14613     }
  14614 
  14615     return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs);
  14616 }
  14617 
  14618 fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14619     const mod = sema.mod;
  14620     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14621     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  14622     sema.src = src;
  14623     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14624     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14625     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14626     const lhs = try sema.resolveInst(extra.lhs);
  14627     const rhs = try sema.resolveInst(extra.rhs);
  14628     const lhs_ty = sema.typeOf(lhs);
  14629     const rhs_ty = sema.typeOf(rhs);
  14630     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  14631     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  14632     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14633     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  14634 
  14635     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  14636     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  14637         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14638     });
  14639 
  14640     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14641     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14642 
  14643     const lhs_scalar_ty = lhs_ty.scalarType(mod);
  14644     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  14645     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  14646 
  14647     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  14648 
  14649     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor);
  14650 
  14651     const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs);
  14652     const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs);
  14653 
  14654     const runtime_src = rs: {
  14655         // For integers:
  14656         // If the lhs is zero, then zero is returned regardless of rhs.
  14657         // If the rhs is zero, compile error for division by zero.
  14658         // If the rhs is undefined, compile error because there is a possible
  14659         // value (zero) for which the division would be illegal behavior.
  14660         // If the lhs is undefined:
  14661         //   * if lhs type is signed:
  14662         //     * if rhs is comptime-known and not -1, result is undefined
  14663         //     * if rhs is -1 or runtime-known, compile error because there is a
  14664         //        possible value (-min_int / -1)  for which division would be
  14665         //        illegal behavior.
  14666         //   * if lhs type is unsigned, undef is returned regardless of rhs.
  14667         // TODO: emit runtime safety for division by zero
  14668         //
  14669         // For floats:
  14670         // If the rhs is zero, compile error for division by zero.
  14671         // If the rhs is undefined, compile error because there is a possible
  14672         // value (zero) for which the division would be illegal behavior.
  14673         // If the lhs is undefined, result is undefined.
  14674         if (maybe_lhs_val) |lhs_val| {
  14675             if (!lhs_val.isUndef(mod)) {
  14676                 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  14677                     const scalar_zero = switch (scalar_tag) {
  14678                         .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  14679                         .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  14680                         else => unreachable,
  14681                     };
  14682                     const zero_val = try sema.splat(resolved_type, scalar_zero);
  14683                     return Air.internedToRef(zero_val.toIntern());
  14684                 }
  14685             }
  14686         }
  14687         if (maybe_rhs_val) |rhs_val| {
  14688             if (rhs_val.isUndef(mod)) {
  14689                 return sema.failWithUseOfUndef(block, rhs_src);
  14690             }
  14691             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14692                 return sema.failWithDivideByZero(block, rhs_src);
  14693             }
  14694             // TODO: if the RHS is one, return the LHS directly
  14695         }
  14696         if (maybe_lhs_val) |lhs_val| {
  14697             if (lhs_val.isUndef(mod)) {
  14698                 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) {
  14699                     if (maybe_rhs_val) |rhs_val| {
  14700                         if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) {
  14701                             return mod.undefRef(resolved_type);
  14702                         }
  14703                     }
  14704                     return sema.failWithUseOfUndef(block, rhs_src);
  14705                 }
  14706                 return mod.undefRef(resolved_type);
  14707             }
  14708 
  14709             if (maybe_rhs_val) |rhs_val| {
  14710                 if (is_int) {
  14711                     return Air.internedToRef((try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, mod)).toIntern());
  14712                 } else {
  14713                     return Air.internedToRef((try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, mod)).toIntern());
  14714                 }
  14715             } else break :rs rhs_src;
  14716         } else break :rs lhs_src;
  14717     };
  14718 
  14719     try sema.requireRuntimeBlock(block, src, runtime_src);
  14720 
  14721     if (block.wantSafety()) {
  14722         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  14723         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14724     }
  14725 
  14726     return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
  14727 }
  14728 
  14729 fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14730     const mod = sema.mod;
  14731     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14732     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  14733     sema.src = src;
  14734     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14735     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14736     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14737     const lhs = try sema.resolveInst(extra.lhs);
  14738     const rhs = try sema.resolveInst(extra.rhs);
  14739     const lhs_ty = sema.typeOf(lhs);
  14740     const rhs_ty = sema.typeOf(rhs);
  14741     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  14742     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  14743     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14744     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  14745 
  14746     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  14747     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  14748         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14749     });
  14750 
  14751     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14752     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14753 
  14754     const lhs_scalar_ty = lhs_ty.scalarType(mod);
  14755     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  14756     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  14757 
  14758     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  14759 
  14760     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc);
  14761 
  14762     const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs);
  14763     const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs);
  14764 
  14765     const runtime_src = rs: {
  14766         // For integers:
  14767         // If the lhs is zero, then zero is returned regardless of rhs.
  14768         // If the rhs is zero, compile error for division by zero.
  14769         // If the rhs is undefined, compile error because there is a possible
  14770         // value (zero) for which the division would be illegal behavior.
  14771         // If the lhs is undefined:
  14772         //   * if lhs type is signed:
  14773         //     * if rhs is comptime-known and not -1, result is undefined
  14774         //     * if rhs is -1 or runtime-known, compile error because there is a
  14775         //        possible value (-min_int / -1)  for which division would be
  14776         //        illegal behavior.
  14777         //   * if lhs type is unsigned, undef is returned regardless of rhs.
  14778         // TODO: emit runtime safety for division by zero
  14779         //
  14780         // For floats:
  14781         // If the rhs is zero, compile error for division by zero.
  14782         // If the rhs is undefined, compile error because there is a possible
  14783         // value (zero) for which the division would be illegal behavior.
  14784         // If the lhs is undefined, result is undefined.
  14785         if (maybe_lhs_val) |lhs_val| {
  14786             if (!lhs_val.isUndef(mod)) {
  14787                 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  14788                     const scalar_zero = switch (scalar_tag) {
  14789                         .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  14790                         .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  14791                         else => unreachable,
  14792                     };
  14793                     const zero_val = try sema.splat(resolved_type, scalar_zero);
  14794                     return Air.internedToRef(zero_val.toIntern());
  14795                 }
  14796             }
  14797         }
  14798         if (maybe_rhs_val) |rhs_val| {
  14799             if (rhs_val.isUndef(mod)) {
  14800                 return sema.failWithUseOfUndef(block, rhs_src);
  14801             }
  14802             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14803                 return sema.failWithDivideByZero(block, rhs_src);
  14804             }
  14805         }
  14806         if (maybe_lhs_val) |lhs_val| {
  14807             if (lhs_val.isUndef(mod)) {
  14808                 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) {
  14809                     if (maybe_rhs_val) |rhs_val| {
  14810                         if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) {
  14811                             return mod.undefRef(resolved_type);
  14812                         }
  14813                     }
  14814                     return sema.failWithUseOfUndef(block, rhs_src);
  14815                 }
  14816                 return mod.undefRef(resolved_type);
  14817             }
  14818 
  14819             if (maybe_rhs_val) |rhs_val| {
  14820                 if (is_int) {
  14821                     var overflow_idx: ?usize = null;
  14822                     const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
  14823                     if (overflow_idx) |vec_idx| {
  14824                         return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
  14825                     }
  14826                     return Air.internedToRef(res.toIntern());
  14827                 } else {
  14828                     return Air.internedToRef((try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, mod)).toIntern());
  14829                 }
  14830             } else break :rs rhs_src;
  14831         } else break :rs lhs_src;
  14832     };
  14833 
  14834     try sema.requireRuntimeBlock(block, src, runtime_src);
  14835 
  14836     if (block.wantSafety()) {
  14837         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  14838         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14839     }
  14840 
  14841     return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs);
  14842 }
  14843 
  14844 fn addDivIntOverflowSafety(
  14845     sema: *Sema,
  14846     block: *Block,
  14847     src: LazySrcLoc,
  14848     resolved_type: Type,
  14849     lhs_scalar_ty: Type,
  14850     maybe_lhs_val: ?Value,
  14851     maybe_rhs_val: ?Value,
  14852     casted_lhs: Air.Inst.Ref,
  14853     casted_rhs: Air.Inst.Ref,
  14854     is_int: bool,
  14855 ) CompileError!void {
  14856     const mod = sema.mod;
  14857     if (!is_int) return;
  14858 
  14859     // If the LHS is unsigned, it cannot cause overflow.
  14860     if (!lhs_scalar_ty.isSignedInt(mod)) return;
  14861 
  14862     // If the LHS is widened to a larger integer type, no overflow is possible.
  14863     if (lhs_scalar_ty.intInfo(mod).bits < resolved_type.intInfo(mod).bits) {
  14864         return;
  14865     }
  14866 
  14867     const min_int = try resolved_type.minInt(mod, resolved_type);
  14868     const neg_one_scalar = try mod.intValue(lhs_scalar_ty, -1);
  14869     const neg_one = try sema.splat(resolved_type, neg_one_scalar);
  14870 
  14871     // If the LHS is comptime-known to be not equal to the min int,
  14872     // no overflow is possible.
  14873     if (maybe_lhs_val) |lhs_val| {
  14874         if (try lhs_val.compareAll(.neq, min_int, resolved_type, mod)) return;
  14875     }
  14876 
  14877     // If the RHS is comptime-known to not be equal to -1, no overflow is possible.
  14878     if (maybe_rhs_val) |rhs_val| {
  14879         if (try rhs_val.compareAll(.neq, neg_one, resolved_type, mod)) return;
  14880     }
  14881 
  14882     var ok: Air.Inst.Ref = .none;
  14883     if (resolved_type.zigTypeTag(mod) == .Vector) {
  14884         if (maybe_lhs_val == null) {
  14885             const min_int_ref = Air.internedToRef(min_int.toIntern());
  14886             ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq);
  14887         }
  14888         if (maybe_rhs_val == null) {
  14889             const neg_one_ref = Air.internedToRef(neg_one.toIntern());
  14890             const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq);
  14891             if (ok == .none) {
  14892                 ok = rhs_ok;
  14893             } else {
  14894                 ok = try block.addBinOp(.bool_or, ok, rhs_ok);
  14895             }
  14896         }
  14897         assert(ok != .none);
  14898         ok = try block.addInst(.{
  14899             .tag = .reduce,
  14900             .data = .{ .reduce = .{
  14901                 .operand = ok,
  14902                 .operation = .And,
  14903             } },
  14904         });
  14905     } else {
  14906         if (maybe_lhs_val == null) {
  14907             const min_int_ref = Air.internedToRef(min_int.toIntern());
  14908             ok = try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref);
  14909         }
  14910         if (maybe_rhs_val == null) {
  14911             const neg_one_ref = Air.internedToRef(neg_one.toIntern());
  14912             const rhs_ok = try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref);
  14913             if (ok == .none) {
  14914                 ok = rhs_ok;
  14915             } else {
  14916                 ok = try block.addBinOp(.bool_or, ok, rhs_ok);
  14917             }
  14918         }
  14919         assert(ok != .none);
  14920     }
  14921     try sema.addSafetyCheck(block, src, ok, .integer_overflow);
  14922 }
  14923 
  14924 fn addDivByZeroSafety(
  14925     sema: *Sema,
  14926     block: *Block,
  14927     src: LazySrcLoc,
  14928     resolved_type: Type,
  14929     maybe_rhs_val: ?Value,
  14930     casted_rhs: Air.Inst.Ref,
  14931     is_int: bool,
  14932 ) CompileError!void {
  14933     // Strict IEEE floats have well-defined division by zero.
  14934     if (!is_int and block.float_mode == .Strict) return;
  14935 
  14936     // If rhs was comptime-known to be zero a compile error would have been
  14937     // emitted above.
  14938     if (maybe_rhs_val != null) return;
  14939 
  14940     const mod = sema.mod;
  14941     const scalar_zero = if (is_int)
  14942         try mod.intValue(resolved_type.scalarType(mod), 0)
  14943     else
  14944         try mod.floatValue(resolved_type.scalarType(mod), 0.0);
  14945     const ok = if (resolved_type.zigTypeTag(mod) == .Vector) ok: {
  14946         const zero_val = try sema.splat(resolved_type, scalar_zero);
  14947         const zero = Air.internedToRef(zero_val.toIntern());
  14948         const ok = try block.addCmpVector(casted_rhs, zero, .neq);
  14949         break :ok try block.addInst(.{
  14950             .tag = if (is_int) .reduce else .reduce_optimized,
  14951             .data = .{ .reduce = .{
  14952                 .operand = ok,
  14953                 .operation = .And,
  14954             } },
  14955         });
  14956     } else ok: {
  14957         const zero = Air.internedToRef(scalar_zero.toIntern());
  14958         break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero);
  14959     };
  14960     try sema.addSafetyCheck(block, src, ok, .divide_by_zero);
  14961 }
  14962 
  14963 fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag {
  14964     if (is_int) return normal;
  14965     return switch (block.float_mode) {
  14966         .Strict => normal,
  14967         .Optimized => optimized,
  14968     };
  14969 }
  14970 
  14971 fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14972     const mod = sema.mod;
  14973     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14974     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  14975     sema.src = src;
  14976     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14977     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14978     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14979     const lhs = try sema.resolveInst(extra.lhs);
  14980     const rhs = try sema.resolveInst(extra.rhs);
  14981     const lhs_ty = sema.typeOf(lhs);
  14982     const rhs_ty = sema.typeOf(rhs);
  14983     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  14984     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  14985     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14986     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  14987 
  14988     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  14989     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  14990         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14991     });
  14992 
  14993     const is_vector = resolved_type.zigTypeTag(mod) == .Vector;
  14994 
  14995     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14996     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14997 
  14998     const lhs_scalar_ty = lhs_ty.scalarType(mod);
  14999     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  15000     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  15001 
  15002     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  15003 
  15004     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem);
  15005 
  15006     const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs);
  15007     const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs);
  15008 
  15009     const runtime_src = rs: {
  15010         // For integers:
  15011         // Either operand being undef is a compile error because there exists
  15012         // a possible value (TODO what is it?) that would invoke illegal behavior.
  15013         // TODO: can lhs undef be handled better?
  15014         //
  15015         // For floats:
  15016         // If the rhs is zero, compile error for division by zero.
  15017         // If the rhs is undefined, compile error because there is a possible
  15018         // value (zero) for which the division would be illegal behavior.
  15019         // If the lhs is undefined, result is undefined.
  15020         //
  15021         // For either one: if the result would be different between @mod and @rem,
  15022         // then emit a compile error saying you have to pick one.
  15023         if (is_int) {
  15024             if (maybe_lhs_val) |lhs_val| {
  15025                 if (lhs_val.isUndef(mod)) {
  15026                     return sema.failWithUseOfUndef(block, lhs_src);
  15027                 }
  15028                 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15029                     const scalar_zero = switch (scalar_tag) {
  15030                         .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  15031                         .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  15032                         else => unreachable,
  15033                     };
  15034                     const zero_val = if (is_vector) Value.fromInterned((try mod.intern(.{ .aggregate = .{
  15035                         .ty = resolved_type.toIntern(),
  15036                         .storage = .{ .repeated_elem = scalar_zero.toIntern() },
  15037                     } }))) else scalar_zero;
  15038                     return Air.internedToRef(zero_val.toIntern());
  15039                 }
  15040             } else if (lhs_scalar_ty.isSignedInt(mod)) {
  15041                 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
  15042             }
  15043             if (maybe_rhs_val) |rhs_val| {
  15044                 if (rhs_val.isUndef(mod)) {
  15045                     return sema.failWithUseOfUndef(block, rhs_src);
  15046                 }
  15047                 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  15048                     return sema.failWithDivideByZero(block, rhs_src);
  15049                 }
  15050                 if (!(try rhs_val.compareAllWithZeroAdvanced(.gte, sema))) {
  15051                     return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
  15052                 }
  15053                 if (maybe_lhs_val) |lhs_val| {
  15054                     const rem_result = try sema.intRem(resolved_type, lhs_val, rhs_val);
  15055                     // If this answer could possibly be different by doing `intMod`,
  15056                     // we must emit a compile error. Otherwise, it's OK.
  15057                     if (!(try lhs_val.compareAllWithZeroAdvanced(.gte, sema)) and
  15058                         !(try rem_result.compareAllWithZeroAdvanced(.eq, sema)))
  15059                     {
  15060                         return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
  15061                     }
  15062                     return Air.internedToRef(rem_result.toIntern());
  15063                 }
  15064                 break :rs lhs_src;
  15065             } else if (rhs_scalar_ty.isSignedInt(mod)) {
  15066                 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
  15067             } else {
  15068                 break :rs rhs_src;
  15069             }
  15070         }
  15071         // float operands
  15072         if (maybe_rhs_val) |rhs_val| {
  15073             if (rhs_val.isUndef(mod)) {
  15074                 return sema.failWithUseOfUndef(block, rhs_src);
  15075             }
  15076             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  15077                 return sema.failWithDivideByZero(block, rhs_src);
  15078             }
  15079             if (!(try rhs_val.compareAllWithZeroAdvanced(.gte, sema))) {
  15080                 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
  15081             }
  15082             if (maybe_lhs_val) |lhs_val| {
  15083                 if (lhs_val.isUndef(mod) or !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema))) {
  15084                     return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
  15085                 }
  15086                 return Air.internedToRef((try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod)).toIntern());
  15087             } else {
  15088                 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
  15089             }
  15090         } else {
  15091             return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
  15092         }
  15093     };
  15094 
  15095     try sema.requireRuntimeBlock(block, src, runtime_src);
  15096 
  15097     if (block.wantSafety()) {
  15098         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15099     }
  15100 
  15101     const air_tag = airTag(block, is_int, .rem, .rem_optimized);
  15102     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  15103 }
  15104 
  15105 fn intRem(
  15106     sema: *Sema,
  15107     ty: Type,
  15108     lhs: Value,
  15109     rhs: Value,
  15110 ) CompileError!Value {
  15111     const mod = sema.mod;
  15112     if (ty.zigTypeTag(mod) == .Vector) {
  15113         const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
  15114         const scalar_ty = ty.scalarType(mod);
  15115         for (result_data, 0..) |*scalar, i| {
  15116             const lhs_elem = try lhs.elemValue(mod, i);
  15117             const rhs_elem = try rhs.elemValue(mod, i);
  15118             scalar.* = try (try sema.intRemScalar(lhs_elem, rhs_elem, scalar_ty)).intern(scalar_ty, mod);
  15119         }
  15120         return Value.fromInterned((try mod.intern(.{ .aggregate = .{
  15121             .ty = ty.toIntern(),
  15122             .storage = .{ .elems = result_data },
  15123         } })));
  15124     }
  15125     return sema.intRemScalar(lhs, rhs, ty);
  15126 }
  15127 
  15128 fn intRemScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) CompileError!Value {
  15129     const mod = sema.mod;
  15130     // TODO is this a performance issue? maybe we should try the operation without
  15131     // resorting to BigInt first.
  15132     var lhs_space: Value.BigIntSpace = undefined;
  15133     var rhs_space: Value.BigIntSpace = undefined;
  15134     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  15135     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  15136     const limbs_q = try sema.arena.alloc(
  15137         math.big.Limb,
  15138         lhs_bigint.limbs.len,
  15139     );
  15140     const limbs_r = try sema.arena.alloc(
  15141         math.big.Limb,
  15142         // TODO: consider reworking Sema to re-use Values rather than
  15143         // always producing new Value objects.
  15144         rhs_bigint.limbs.len,
  15145     );
  15146     const limbs_buffer = try sema.arena.alloc(
  15147         math.big.Limb,
  15148         math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
  15149     );
  15150     var result_q = math.big.int.Mutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
  15151     var result_r = math.big.int.Mutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
  15152     result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
  15153     return mod.intValue_big(scalar_ty, result_r.toConst());
  15154 }
  15155 
  15156 fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15157     const mod = sema.mod;
  15158     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15159     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  15160     sema.src = src;
  15161     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  15162     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  15163     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15164     const lhs = try sema.resolveInst(extra.lhs);
  15165     const rhs = try sema.resolveInst(extra.rhs);
  15166     const lhs_ty = sema.typeOf(lhs);
  15167     const rhs_ty = sema.typeOf(rhs);
  15168     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  15169     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  15170     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15171     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  15172 
  15173     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  15174     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  15175         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  15176     });
  15177 
  15178     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15179     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15180 
  15181     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  15182 
  15183     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  15184 
  15185     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod);
  15186 
  15187     const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs);
  15188     const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs);
  15189 
  15190     const runtime_src = rs: {
  15191         // For integers:
  15192         // Either operand being undef is a compile error because there exists
  15193         // a possible value (TODO what is it?) that would invoke illegal behavior.
  15194         // TODO: can lhs zero be handled better?
  15195         // TODO: can lhs undef be handled better?
  15196         //
  15197         // For floats:
  15198         // If the rhs is zero, compile error for division by zero.
  15199         // If the rhs is undefined, compile error because there is a possible
  15200         // value (zero) for which the division would be illegal behavior.
  15201         // If the lhs is undefined, result is undefined.
  15202         if (is_int) {
  15203             if (maybe_lhs_val) |lhs_val| {
  15204                 if (lhs_val.isUndef(mod)) {
  15205                     return sema.failWithUseOfUndef(block, lhs_src);
  15206                 }
  15207             }
  15208             if (maybe_rhs_val) |rhs_val| {
  15209                 if (rhs_val.isUndef(mod)) {
  15210                     return sema.failWithUseOfUndef(block, rhs_src);
  15211                 }
  15212                 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  15213                     return sema.failWithDivideByZero(block, rhs_src);
  15214                 }
  15215                 if (maybe_lhs_val) |lhs_val| {
  15216                     return Air.internedToRef((try lhs_val.intMod(rhs_val, resolved_type, sema.arena, mod)).toIntern());
  15217                 }
  15218                 break :rs lhs_src;
  15219             } else {
  15220                 break :rs rhs_src;
  15221             }
  15222         }
  15223         // float operands
  15224         if (maybe_rhs_val) |rhs_val| {
  15225             if (rhs_val.isUndef(mod)) {
  15226                 return sema.failWithUseOfUndef(block, rhs_src);
  15227             }
  15228             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  15229                 return sema.failWithDivideByZero(block, rhs_src);
  15230             }
  15231         }
  15232         if (maybe_lhs_val) |lhs_val| {
  15233             if (lhs_val.isUndef(mod)) {
  15234                 return mod.undefRef(resolved_type);
  15235             }
  15236             if (maybe_rhs_val) |rhs_val| {
  15237                 return Air.internedToRef((try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, mod)).toIntern());
  15238             } else break :rs rhs_src;
  15239         } else break :rs lhs_src;
  15240     };
  15241 
  15242     try sema.requireRuntimeBlock(block, src, runtime_src);
  15243 
  15244     if (block.wantSafety()) {
  15245         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15246     }
  15247 
  15248     const air_tag = airTag(block, is_int, .mod, .mod_optimized);
  15249     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  15250 }
  15251 
  15252 fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15253     const mod = sema.mod;
  15254     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15255     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  15256     sema.src = src;
  15257     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  15258     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  15259     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15260     const lhs = try sema.resolveInst(extra.lhs);
  15261     const rhs = try sema.resolveInst(extra.rhs);
  15262     const lhs_ty = sema.typeOf(lhs);
  15263     const rhs_ty = sema.typeOf(rhs);
  15264     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  15265     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  15266     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15267     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  15268 
  15269     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  15270     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  15271         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  15272     });
  15273 
  15274     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15275     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15276 
  15277     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  15278 
  15279     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  15280 
  15281     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem);
  15282 
  15283     const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs);
  15284     const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs);
  15285 
  15286     const runtime_src = rs: {
  15287         // For integers:
  15288         // Either operand being undef is a compile error because there exists
  15289         // a possible value (TODO what is it?) that would invoke illegal behavior.
  15290         // TODO: can lhs zero be handled better?
  15291         // TODO: can lhs undef be handled better?
  15292         //
  15293         // For floats:
  15294         // If the rhs is zero, compile error for division by zero.
  15295         // If the rhs is undefined, compile error because there is a possible
  15296         // value (zero) for which the division would be illegal behavior.
  15297         // If the lhs is undefined, result is undefined.
  15298         if (is_int) {
  15299             if (maybe_lhs_val) |lhs_val| {
  15300                 if (lhs_val.isUndef(mod)) {
  15301                     return sema.failWithUseOfUndef(block, lhs_src);
  15302                 }
  15303             }
  15304             if (maybe_rhs_val) |rhs_val| {
  15305                 if (rhs_val.isUndef(mod)) {
  15306                     return sema.failWithUseOfUndef(block, rhs_src);
  15307                 }
  15308                 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  15309                     return sema.failWithDivideByZero(block, rhs_src);
  15310                 }
  15311                 if (maybe_lhs_val) |lhs_val| {
  15312                     return Air.internedToRef((try sema.intRem(resolved_type, lhs_val, rhs_val)).toIntern());
  15313                 }
  15314                 break :rs lhs_src;
  15315             } else {
  15316                 break :rs rhs_src;
  15317             }
  15318         }
  15319         // float operands
  15320         if (maybe_rhs_val) |rhs_val| {
  15321             if (rhs_val.isUndef(mod)) {
  15322                 return sema.failWithUseOfUndef(block, rhs_src);
  15323             }
  15324             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  15325                 return sema.failWithDivideByZero(block, rhs_src);
  15326             }
  15327         }
  15328         if (maybe_lhs_val) |lhs_val| {
  15329             if (lhs_val.isUndef(mod)) {
  15330                 return mod.undefRef(resolved_type);
  15331             }
  15332             if (maybe_rhs_val) |rhs_val| {
  15333                 return Air.internedToRef((try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod)).toIntern());
  15334             } else break :rs rhs_src;
  15335         } else break :rs lhs_src;
  15336     };
  15337 
  15338     try sema.requireRuntimeBlock(block, src, runtime_src);
  15339 
  15340     if (block.wantSafety()) {
  15341         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15342     }
  15343 
  15344     const air_tag = airTag(block, is_int, .rem, .rem_optimized);
  15345     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  15346 }
  15347 
  15348 fn zirOverflowArithmetic(
  15349     sema: *Sema,
  15350     block: *Block,
  15351     extended: Zir.Inst.Extended.InstData,
  15352     zir_tag: Zir.Inst.Extended,
  15353 ) CompileError!Air.Inst.Ref {
  15354     const tracy = trace(@src());
  15355     defer tracy.end();
  15356 
  15357     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  15358     const src = LazySrcLoc.nodeOffset(extra.node);
  15359 
  15360     const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  15361     const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  15362 
  15363     const uncasted_lhs = try sema.resolveInst(extra.lhs);
  15364     const uncasted_rhs = try sema.resolveInst(extra.rhs);
  15365 
  15366     const lhs_ty = sema.typeOf(uncasted_lhs);
  15367     const rhs_ty = sema.typeOf(uncasted_rhs);
  15368     const mod = sema.mod;
  15369     const ip = &mod.intern_pool;
  15370 
  15371     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15372 
  15373     const instructions = &[_]Air.Inst.Ref{ uncasted_lhs, uncasted_rhs };
  15374     const dest_ty = if (zir_tag == .shl_with_overflow)
  15375         lhs_ty
  15376     else
  15377         try sema.resolvePeerTypes(block, src, instructions, .{
  15378             .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  15379         });
  15380 
  15381     const rhs_dest_ty = if (zir_tag == .shl_with_overflow)
  15382         try sema.log2IntType(block, lhs_ty, src)
  15383     else
  15384         dest_ty;
  15385 
  15386     const lhs = try sema.coerce(block, dest_ty, uncasted_lhs, lhs_src);
  15387     const rhs = try sema.coerce(block, rhs_dest_ty, uncasted_rhs, rhs_src);
  15388 
  15389     if (dest_ty.scalarType(mod).zigTypeTag(mod) != .Int) {
  15390         return sema.fail(block, src, "expected vector of integers or integer tag type, found '{}'", .{dest_ty.fmt(mod)});
  15391     }
  15392 
  15393     const maybe_lhs_val = try sema.resolveValue(lhs);
  15394     const maybe_rhs_val = try sema.resolveValue(rhs);
  15395 
  15396     const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty);
  15397     const overflow_ty = Type.fromInterned(ip.indexToKey(tuple_ty.toIntern()).anon_struct_type.types.get(ip)[1]);
  15398 
  15399     var result: struct {
  15400         inst: Air.Inst.Ref = .none,
  15401         wrapped: Value = Value.@"unreachable",
  15402         overflow_bit: Value,
  15403     } = result: {
  15404         const zero_bit = try mod.intValue(Type.u1, 0);
  15405         switch (zir_tag) {
  15406             .add_with_overflow => {
  15407                 // If either of the arguments is zero, `false` is returned and the other is stored
  15408                 // to the result, even if it is undefined..
  15409                 // Otherwise, if either of the argument is undefined, undefined is returned.
  15410                 if (maybe_lhs_val) |lhs_val| {
  15411                     if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15412                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs };
  15413                     }
  15414                 }
  15415                 if (maybe_rhs_val) |rhs_val| {
  15416                     if (!rhs_val.isUndef(mod) and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15417                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  15418                     }
  15419                 }
  15420                 if (maybe_lhs_val) |lhs_val| {
  15421                     if (maybe_rhs_val) |rhs_val| {
  15422                         if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  15423                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  15424                         }
  15425 
  15426                         const result = try sema.intAddWithOverflow(lhs_val, rhs_val, dest_ty);
  15427                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15428                     }
  15429                 }
  15430             },
  15431             .sub_with_overflow => {
  15432                 // If the rhs is zero, then the result is lhs and no overflow occured.
  15433                 // Otherwise, if either result is undefined, both results are undefined.
  15434                 if (maybe_rhs_val) |rhs_val| {
  15435                     if (rhs_val.isUndef(mod)) {
  15436                         break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  15437                     } else if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15438                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  15439                     } else if (maybe_lhs_val) |lhs_val| {
  15440                         if (lhs_val.isUndef(mod)) {
  15441                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  15442                         }
  15443 
  15444                         const result = try sema.intSubWithOverflow(lhs_val, rhs_val, dest_ty);
  15445                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15446                     }
  15447                 }
  15448             },
  15449             .mul_with_overflow => {
  15450                 // If either of the arguments is zero, the result is zero and no overflow occured.
  15451                 // If either of the arguments is one, the result is the other and no overflow occured.
  15452                 // Otherwise, if either of the arguments is undefined, both results are undefined.
  15453                 const scalar_one = try mod.intValue(dest_ty.scalarType(mod), 1);
  15454                 if (maybe_lhs_val) |lhs_val| {
  15455                     if (!lhs_val.isUndef(mod)) {
  15456                         if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15457                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  15458                         } else if (try sema.compareAll(lhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) {
  15459                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs };
  15460                         }
  15461                     }
  15462                 }
  15463 
  15464                 if (maybe_rhs_val) |rhs_val| {
  15465                     if (!rhs_val.isUndef(mod)) {
  15466                         if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15467                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs };
  15468                         } else if (try sema.compareAll(rhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) {
  15469                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  15470                         }
  15471                     }
  15472                 }
  15473 
  15474                 if (maybe_lhs_val) |lhs_val| {
  15475                     if (maybe_rhs_val) |rhs_val| {
  15476                         if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  15477                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  15478                         }
  15479 
  15480                         const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, mod);
  15481                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15482                     }
  15483                 }
  15484             },
  15485             .shl_with_overflow => {
  15486                 // If lhs is zero, the result is zero and no overflow occurred.
  15487                 // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred.
  15488                 // Oterhwise if either of the arguments is undefined, both results are undefined.
  15489                 if (maybe_lhs_val) |lhs_val| {
  15490                     if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15491                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  15492                     }
  15493                 }
  15494                 if (maybe_rhs_val) |rhs_val| {
  15495                     if (!rhs_val.isUndef(mod) and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15496                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  15497                     }
  15498                 }
  15499                 if (maybe_lhs_val) |lhs_val| {
  15500                     if (maybe_rhs_val) |rhs_val| {
  15501                         if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  15502                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  15503                         }
  15504 
  15505                         const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, mod);
  15506                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15507                     }
  15508                 }
  15509             },
  15510             else => unreachable,
  15511         }
  15512 
  15513         const air_tag: Air.Inst.Tag = switch (zir_tag) {
  15514             .add_with_overflow => .add_with_overflow,
  15515             .mul_with_overflow => .mul_with_overflow,
  15516             .sub_with_overflow => .sub_with_overflow,
  15517             .shl_with_overflow => .shl_with_overflow,
  15518             else => unreachable,
  15519         };
  15520 
  15521         const runtime_src = if (maybe_lhs_val == null) lhs_src else rhs_src;
  15522         try sema.requireRuntimeBlock(block, src, runtime_src);
  15523 
  15524         return block.addInst(.{
  15525             .tag = air_tag,
  15526             .data = .{ .ty_pl = .{
  15527                 .ty = Air.internedToRef(tuple_ty.toIntern()),
  15528                 .payload = try block.sema.addExtra(Air.Bin{
  15529                     .lhs = lhs,
  15530                     .rhs = rhs,
  15531                 }),
  15532             } },
  15533         });
  15534     };
  15535 
  15536     if (result.inst != .none) {
  15537         if (try sema.resolveValue(result.inst)) |some| {
  15538             result.wrapped = some;
  15539             result.inst = .none;
  15540         }
  15541     }
  15542 
  15543     if (result.inst == .none) {
  15544         return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  15545             .ty = tuple_ty.toIntern(),
  15546             .storage = .{ .elems = &.{
  15547                 result.wrapped.toIntern(),
  15548                 result.overflow_bit.toIntern(),
  15549             } },
  15550         } })));
  15551     }
  15552 
  15553     const element_refs = try sema.arena.alloc(Air.Inst.Ref, 2);
  15554     element_refs[0] = result.inst;
  15555     element_refs[1] = Air.internedToRef(result.overflow_bit.toIntern());
  15556     return block.addAggregateInit(tuple_ty, element_refs);
  15557 }
  15558 
  15559 fn splat(sema: *Sema, ty: Type, val: Value) !Value {
  15560     const mod = sema.mod;
  15561     if (ty.zigTypeTag(mod) != .Vector) return val;
  15562     const repeated = try mod.intern(.{ .aggregate = .{
  15563         .ty = ty.toIntern(),
  15564         .storage = .{ .repeated_elem = val.toIntern() },
  15565     } });
  15566     return Value.fromInterned(repeated);
  15567 }
  15568 
  15569 fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type {
  15570     const mod = sema.mod;
  15571     const ip = &mod.intern_pool;
  15572     const ov_ty = if (ty.zigTypeTag(mod) == .Vector) try mod.vectorType(.{
  15573         .len = ty.vectorLen(mod),
  15574         .child = .u1_type,
  15575     }) else Type.u1;
  15576 
  15577     const types = [2]InternPool.Index{ ty.toIntern(), ov_ty.toIntern() };
  15578     const values = [2]InternPool.Index{ .none, .none };
  15579     const tuple_ty = try ip.getAnonStructType(mod.gpa, .{
  15580         .types = &types,
  15581         .values = &values,
  15582         .names = &.{},
  15583     });
  15584     return Type.fromInterned(tuple_ty);
  15585 }
  15586 
  15587 fn analyzeArithmetic(
  15588     sema: *Sema,
  15589     block: *Block,
  15590     /// TODO performance investigation: make this comptime?
  15591     zir_tag: Zir.Inst.Tag,
  15592     lhs: Air.Inst.Ref,
  15593     rhs: Air.Inst.Ref,
  15594     src: LazySrcLoc,
  15595     lhs_src: LazySrcLoc,
  15596     rhs_src: LazySrcLoc,
  15597     want_safety: bool,
  15598 ) CompileError!Air.Inst.Ref {
  15599     const mod = sema.mod;
  15600     const lhs_ty = sema.typeOf(lhs);
  15601     const rhs_ty = sema.typeOf(rhs);
  15602     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  15603     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  15604     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15605 
  15606     if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize(mod)) {
  15607         .One, .Slice => {},
  15608         .Many, .C => {
  15609             const air_tag: Air.Inst.Tag = switch (zir_tag) {
  15610                 .add => .ptr_add,
  15611                 .sub => .ptr_sub,
  15612                 else => return sema.fail(block, src, "invalid pointer arithmetic operator", .{}),
  15613             };
  15614             return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
  15615         },
  15616     };
  15617 
  15618     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  15619     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  15620         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  15621     });
  15622 
  15623     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15624     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15625 
  15626     const scalar_type = resolved_type.scalarType(mod);
  15627     const scalar_tag = scalar_type.zigTypeTag(mod);
  15628 
  15629     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  15630 
  15631     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag);
  15632 
  15633     const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs);
  15634     const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs);
  15635     const runtime_src: LazySrcLoc, const air_tag: Air.Inst.Tag, const air_tag_safe: Air.Inst.Tag = rs: {
  15636         switch (zir_tag) {
  15637             .add, .add_unsafe => {
  15638                 // For integers:intAddSat
  15639                 // If either of the operands are zero, then the other operand is
  15640                 // returned, even if it is undefined.
  15641                 // If either of the operands are undefined, it's a compile error
  15642                 // because there is a possible value for which the addition would
  15643                 // overflow (max_int), causing illegal behavior.
  15644                 // For floats: either operand being undef makes the result undef.
  15645                 if (maybe_lhs_val) |lhs_val| {
  15646                     if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15647                         return casted_rhs;
  15648                     }
  15649                 }
  15650                 if (maybe_rhs_val) |rhs_val| {
  15651                     if (rhs_val.isUndef(mod)) {
  15652                         if (is_int) {
  15653                             return sema.failWithUseOfUndef(block, rhs_src);
  15654                         } else {
  15655                             return mod.undefRef(resolved_type);
  15656                         }
  15657                     }
  15658                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15659                         return casted_lhs;
  15660                     }
  15661                 }
  15662                 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .add_optimized else .add;
  15663                 if (maybe_lhs_val) |lhs_val| {
  15664                     if (lhs_val.isUndef(mod)) {
  15665                         if (is_int) {
  15666                             return sema.failWithUseOfUndef(block, lhs_src);
  15667                         } else {
  15668                             return mod.undefRef(resolved_type);
  15669                         }
  15670                     }
  15671                     if (maybe_rhs_val) |rhs_val| {
  15672                         if (is_int) {
  15673                             var overflow_idx: ?usize = null;
  15674                             const sum = try sema.intAdd(lhs_val, rhs_val, resolved_type, &overflow_idx);
  15675                             if (overflow_idx) |vec_idx| {
  15676                                 return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vec_idx);
  15677                             }
  15678                             return Air.internedToRef(sum.toIntern());
  15679                         } else {
  15680                             return Air.internedToRef((try Value.floatAdd(lhs_val, rhs_val, resolved_type, sema.arena, mod)).toIntern());
  15681                         }
  15682                     } else break :rs .{ rhs_src, air_tag, .add_safe };
  15683                 } else break :rs .{ lhs_src, air_tag, .add_safe };
  15684             },
  15685             .addwrap => {
  15686                 // Integers only; floats are checked above.
  15687                 // If either of the operands are zero, the other operand is returned.
  15688                 // If either of the operands are undefined, the result is undefined.
  15689                 if (maybe_lhs_val) |lhs_val| {
  15690                     if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15691                         return casted_rhs;
  15692                     }
  15693                 }
  15694                 if (maybe_rhs_val) |rhs_val| {
  15695                     if (rhs_val.isUndef(mod)) {
  15696                         return mod.undefRef(resolved_type);
  15697                     }
  15698                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15699                         return casted_lhs;
  15700                     }
  15701                     if (maybe_lhs_val) |lhs_val| {
  15702                         return Air.internedToRef((try sema.numberAddWrapScalar(lhs_val, rhs_val, resolved_type)).toIntern());
  15703                     } else break :rs .{ lhs_src, .add_wrap, .add_wrap };
  15704                 } else break :rs .{ rhs_src, .add_wrap, .add_wrap };
  15705             },
  15706             .add_sat => {
  15707                 // Integers only; floats are checked above.
  15708                 // If either of the operands are zero, then the other operand is returned.
  15709                 // If either of the operands are undefined, the result is undefined.
  15710                 if (maybe_lhs_val) |lhs_val| {
  15711                     if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15712                         return casted_rhs;
  15713                     }
  15714                 }
  15715                 if (maybe_rhs_val) |rhs_val| {
  15716                     if (rhs_val.isUndef(mod)) {
  15717                         return mod.undefRef(resolved_type);
  15718                     }
  15719                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15720                         return casted_lhs;
  15721                     }
  15722                     if (maybe_lhs_val) |lhs_val| {
  15723                         const val = if (scalar_tag == .ComptimeInt)
  15724                             try sema.intAdd(lhs_val, rhs_val, resolved_type, undefined)
  15725                         else
  15726                             try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, mod);
  15727 
  15728                         return Air.internedToRef(val.toIntern());
  15729                     } else break :rs .{
  15730                         lhs_src,
  15731                         .add_sat,
  15732                         .add_sat,
  15733                     };
  15734                 } else break :rs .{
  15735                     rhs_src,
  15736                     .add_sat,
  15737                     .add_sat,
  15738                 };
  15739             },
  15740             .sub => {
  15741                 // For integers:
  15742                 // If the rhs is zero, then the other operand is
  15743                 // returned, even if it is undefined.
  15744                 // If either of the operands are undefined, it's a compile error
  15745                 // because there is a possible value for which the subtraction would
  15746                 // overflow, causing illegal behavior.
  15747                 // For floats: either operand being undef makes the result undef.
  15748                 if (maybe_rhs_val) |rhs_val| {
  15749                     if (rhs_val.isUndef(mod)) {
  15750                         if (is_int) {
  15751                             return sema.failWithUseOfUndef(block, rhs_src);
  15752                         } else {
  15753                             return mod.undefRef(resolved_type);
  15754                         }
  15755                     }
  15756                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15757                         return casted_lhs;
  15758                     }
  15759                 }
  15760                 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .sub_optimized else .sub;
  15761                 if (maybe_lhs_val) |lhs_val| {
  15762                     if (lhs_val.isUndef(mod)) {
  15763                         if (is_int) {
  15764                             return sema.failWithUseOfUndef(block, lhs_src);
  15765                         } else {
  15766                             return mod.undefRef(resolved_type);
  15767                         }
  15768                     }
  15769                     if (maybe_rhs_val) |rhs_val| {
  15770                         if (is_int) {
  15771                             var overflow_idx: ?usize = null;
  15772                             const diff = try sema.intSub(lhs_val, rhs_val, resolved_type, &overflow_idx);
  15773                             if (overflow_idx) |vec_idx| {
  15774                                 return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vec_idx);
  15775                             }
  15776                             return Air.internedToRef(diff.toIntern());
  15777                         } else {
  15778                             return Air.internedToRef((try Value.floatSub(lhs_val, rhs_val, resolved_type, sema.arena, mod)).toIntern());
  15779                         }
  15780                     } else break :rs .{ rhs_src, air_tag, .sub_safe };
  15781                 } else break :rs .{ lhs_src, air_tag, .sub_safe };
  15782             },
  15783             .subwrap => {
  15784                 // Integers only; floats are checked above.
  15785                 // If the RHS is zero, then the other operand is returned, even if it is undefined.
  15786                 // If either of the operands are undefined, the result is undefined.
  15787                 if (maybe_rhs_val) |rhs_val| {
  15788                     if (rhs_val.isUndef(mod)) {
  15789                         return mod.undefRef(resolved_type);
  15790                     }
  15791                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15792                         return casted_lhs;
  15793                     }
  15794                 }
  15795                 if (maybe_lhs_val) |lhs_val| {
  15796                     if (lhs_val.isUndef(mod)) {
  15797                         return mod.undefRef(resolved_type);
  15798                     }
  15799                     if (maybe_rhs_val) |rhs_val| {
  15800                         return Air.internedToRef((try sema.numberSubWrapScalar(lhs_val, rhs_val, resolved_type)).toIntern());
  15801                     } else break :rs .{ rhs_src, .sub_wrap, .sub_wrap };
  15802                 } else break :rs .{ lhs_src, .sub_wrap, .sub_wrap };
  15803             },
  15804             .sub_sat => {
  15805                 // Integers only; floats are checked above.
  15806                 // If the RHS is zero, result is LHS.
  15807                 // If either of the operands are undefined, result is undefined.
  15808                 if (maybe_rhs_val) |rhs_val| {
  15809                     if (rhs_val.isUndef(mod)) {
  15810                         return mod.undefRef(resolved_type);
  15811                     }
  15812                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15813                         return casted_lhs;
  15814                     }
  15815                 }
  15816                 if (maybe_lhs_val) |lhs_val| {
  15817                     if (lhs_val.isUndef(mod)) {
  15818                         return mod.undefRef(resolved_type);
  15819                     }
  15820                     if (maybe_rhs_val) |rhs_val| {
  15821                         const val = if (scalar_tag == .ComptimeInt)
  15822                             try sema.intSub(lhs_val, rhs_val, resolved_type, undefined)
  15823                         else
  15824                             try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, mod);
  15825 
  15826                         return Air.internedToRef(val.toIntern());
  15827                     } else break :rs .{ rhs_src, .sub_sat, .sub_sat };
  15828                 } else break :rs .{ lhs_src, .sub_sat, .sub_sat };
  15829             },
  15830             .mul => {
  15831                 // For integers:
  15832                 // If either of the operands are zero, the result is zero.
  15833                 // If either of the operands are one, the result is the other
  15834                 // operand, even if it is undefined.
  15835                 // If either of the operands are undefined, it's a compile error
  15836                 // because there is a possible value for which the addition would
  15837                 // overflow (max_int), causing illegal behavior.
  15838                 // For floats: either operand being undef makes the result undef.
  15839                 // If either of the operands are inf, and the other operand is zero,
  15840                 // the result is nan.
  15841                 // If either of the operands are nan, the result is nan.
  15842                 const scalar_zero = switch (scalar_tag) {
  15843                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0),
  15844                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 0),
  15845                     else => unreachable,
  15846                 };
  15847                 const scalar_one = switch (scalar_tag) {
  15848                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0),
  15849                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 1),
  15850                     else => unreachable,
  15851                 };
  15852                 if (maybe_lhs_val) |lhs_val| {
  15853                     if (!lhs_val.isUndef(mod)) {
  15854                         if (lhs_val.isNan(mod)) {
  15855                             return Air.internedToRef(lhs_val.toIntern());
  15856                         }
  15857                         if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) lz: {
  15858                             if (maybe_rhs_val) |rhs_val| {
  15859                                 if (rhs_val.isNan(mod)) {
  15860                                     return Air.internedToRef(rhs_val.toIntern());
  15861                                 }
  15862                                 if (rhs_val.isInf(mod)) {
  15863                                     return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan(f128))).toIntern());
  15864                                 }
  15865                             } else if (resolved_type.isAnyFloat()) {
  15866                                 break :lz;
  15867                             }
  15868                             const zero_val = try sema.splat(resolved_type, scalar_zero);
  15869                             return Air.internedToRef(zero_val.toIntern());
  15870                         }
  15871                         if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  15872                             return casted_rhs;
  15873                         }
  15874                     }
  15875                 }
  15876                 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .mul_optimized else .mul;
  15877                 if (maybe_rhs_val) |rhs_val| {
  15878                     if (rhs_val.isUndef(mod)) {
  15879                         if (is_int) {
  15880                             return sema.failWithUseOfUndef(block, rhs_src);
  15881                         } else {
  15882                             return mod.undefRef(resolved_type);
  15883                         }
  15884                     }
  15885                     if (rhs_val.isNan(mod)) {
  15886                         return Air.internedToRef(rhs_val.toIntern());
  15887                     }
  15888                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) rz: {
  15889                         if (maybe_lhs_val) |lhs_val| {
  15890                             if (lhs_val.isInf(mod)) {
  15891                                 return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan(f128))).toIntern());
  15892                             }
  15893                         } else if (resolved_type.isAnyFloat()) {
  15894                             break :rz;
  15895                         }
  15896                         const zero_val = try sema.splat(resolved_type, scalar_zero);
  15897                         return Air.internedToRef(zero_val.toIntern());
  15898                     }
  15899                     if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  15900                         return casted_lhs;
  15901                     }
  15902                     if (maybe_lhs_val) |lhs_val| {
  15903                         if (lhs_val.isUndef(mod)) {
  15904                             if (is_int) {
  15905                                 return sema.failWithUseOfUndef(block, lhs_src);
  15906                             } else {
  15907                                 return mod.undefRef(resolved_type);
  15908                             }
  15909                         }
  15910                         if (is_int) {
  15911                             var overflow_idx: ?usize = null;
  15912                             const product = try lhs_val.intMul(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
  15913                             if (overflow_idx) |vec_idx| {
  15914                                 return sema.failWithIntegerOverflow(block, src, resolved_type, product, vec_idx);
  15915                             }
  15916                             return Air.internedToRef(product.toIntern());
  15917                         } else {
  15918                             return Air.internedToRef((try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, mod)).toIntern());
  15919                         }
  15920                     } else break :rs .{ lhs_src, air_tag, .mul_safe };
  15921                 } else break :rs .{ rhs_src, air_tag, .mul_safe };
  15922             },
  15923             .mulwrap => {
  15924                 // Integers only; floats are handled above.
  15925                 // If either of the operands are zero, result is zero.
  15926                 // If either of the operands are one, result is the other operand.
  15927                 // If either of the operands are undefined, result is undefined.
  15928                 const scalar_zero = switch (scalar_tag) {
  15929                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0),
  15930                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 0),
  15931                     else => unreachable,
  15932                 };
  15933                 const scalar_one = switch (scalar_tag) {
  15934                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0),
  15935                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 1),
  15936                     else => unreachable,
  15937                 };
  15938                 if (maybe_lhs_val) |lhs_val| {
  15939                     if (!lhs_val.isUndef(mod)) {
  15940                         if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15941                             const zero_val = try sema.splat(resolved_type, scalar_zero);
  15942                             return Air.internedToRef(zero_val.toIntern());
  15943                         }
  15944                         if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  15945                             return casted_rhs;
  15946                         }
  15947                     }
  15948                 }
  15949                 if (maybe_rhs_val) |rhs_val| {
  15950                     if (rhs_val.isUndef(mod)) {
  15951                         return mod.undefRef(resolved_type);
  15952                     }
  15953                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15954                         const zero_val = try sema.splat(resolved_type, scalar_zero);
  15955                         return Air.internedToRef(zero_val.toIntern());
  15956                     }
  15957                     if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  15958                         return casted_lhs;
  15959                     }
  15960                     if (maybe_lhs_val) |lhs_val| {
  15961                         if (lhs_val.isUndef(mod)) {
  15962                             return mod.undefRef(resolved_type);
  15963                         }
  15964                         return Air.internedToRef((try lhs_val.numberMulWrap(rhs_val, resolved_type, sema.arena, mod)).toIntern());
  15965                     } else break :rs .{ lhs_src, .mul_wrap, .mul_wrap };
  15966                 } else break :rs .{ rhs_src, .mul_wrap, .mul_wrap };
  15967             },
  15968             .mul_sat => {
  15969                 // Integers only; floats are checked above.
  15970                 // If either of the operands are zero, result is zero.
  15971                 // If either of the operands are one, result is the other operand.
  15972                 // If either of the operands are undefined, result is undefined.
  15973                 const scalar_zero = switch (scalar_tag) {
  15974                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0),
  15975                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 0),
  15976                     else => unreachable,
  15977                 };
  15978                 const scalar_one = switch (scalar_tag) {
  15979                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0),
  15980                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 1),
  15981                     else => unreachable,
  15982                 };
  15983                 if (maybe_lhs_val) |lhs_val| {
  15984                     if (!lhs_val.isUndef(mod)) {
  15985                         if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15986                             const zero_val = try sema.splat(resolved_type, scalar_zero);
  15987                             return Air.internedToRef(zero_val.toIntern());
  15988                         }
  15989                         if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  15990                             return casted_rhs;
  15991                         }
  15992                     }
  15993                 }
  15994                 if (maybe_rhs_val) |rhs_val| {
  15995                     if (rhs_val.isUndef(mod)) {
  15996                         return mod.undefRef(resolved_type);
  15997                     }
  15998                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15999                         const zero_val = try sema.splat(resolved_type, scalar_zero);
  16000                         return Air.internedToRef(zero_val.toIntern());
  16001                     }
  16002                     if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  16003                         return casted_lhs;
  16004                     }
  16005                     if (maybe_lhs_val) |lhs_val| {
  16006                         if (lhs_val.isUndef(mod)) {
  16007                             return mod.undefRef(resolved_type);
  16008                         }
  16009 
  16010                         const val = if (scalar_tag == .ComptimeInt)
  16011                             try lhs_val.intMul(rhs_val, resolved_type, undefined, sema.arena, mod)
  16012                         else
  16013                             try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, mod);
  16014 
  16015                         return Air.internedToRef(val.toIntern());
  16016                     } else break :rs .{ lhs_src, .mul_sat, .mul_sat };
  16017                 } else break :rs .{ rhs_src, .mul_sat, .mul_sat };
  16018             },
  16019             else => unreachable,
  16020         }
  16021     };
  16022 
  16023     try sema.requireRuntimeBlock(block, src, runtime_src);
  16024 
  16025     if (block.wantSafety() and want_safety and scalar_tag == .Int) {
  16026         if (mod.backendSupportsFeature(.safety_checked_instructions)) {
  16027             if (air_tag != air_tag_safe) {
  16028                 _ = try sema.preparePanicId(block, .integer_overflow);
  16029             }
  16030             return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs);
  16031         } else {
  16032             const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) {
  16033                 .add => .add_with_overflow,
  16034                 .sub => .sub_with_overflow,
  16035                 .mul => .mul_with_overflow,
  16036                 else => null,
  16037             };
  16038             if (maybe_op_ov) |op_ov_tag| {
  16039                 const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(resolved_type);
  16040                 const op_ov = try block.addInst(.{
  16041                     .tag = op_ov_tag,
  16042                     .data = .{ .ty_pl = .{
  16043                         .ty = Air.internedToRef(op_ov_tuple_ty.toIntern()),
  16044                         .payload = try sema.addExtra(Air.Bin{
  16045                             .lhs = casted_lhs,
  16046                             .rhs = casted_rhs,
  16047                         }),
  16048                     } },
  16049                 });
  16050                 const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);
  16051                 const any_ov_bit = if (resolved_type.zigTypeTag(mod) == .Vector)
  16052                     try block.addInst(.{
  16053                         .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  16054                         .data = .{ .reduce = .{
  16055                             .operand = ov_bit,
  16056                             .operation = .Or,
  16057                         } },
  16058                     })
  16059                 else
  16060                     ov_bit;
  16061                 const zero_ov = Air.internedToRef((try mod.intValue(Type.u1, 0)).toIntern());
  16062                 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
  16063 
  16064                 try sema.addSafetyCheck(block, src, no_ov, .integer_overflow);
  16065                 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
  16066             }
  16067         }
  16068     }
  16069     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  16070 }
  16071 
  16072 fn analyzePtrArithmetic(
  16073     sema: *Sema,
  16074     block: *Block,
  16075     op_src: LazySrcLoc,
  16076     ptr: Air.Inst.Ref,
  16077     uncasted_offset: Air.Inst.Ref,
  16078     air_tag: Air.Inst.Tag,
  16079     ptr_src: LazySrcLoc,
  16080     offset_src: LazySrcLoc,
  16081 ) CompileError!Air.Inst.Ref {
  16082     // TODO if the operand is comptime-known to be negative, or is a negative int,
  16083     // coerce to isize instead of usize.
  16084     const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src);
  16085     const mod = sema.mod;
  16086     const opt_ptr_val = try sema.resolveValue(ptr);
  16087     const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset);
  16088     const ptr_ty = sema.typeOf(ptr);
  16089     const ptr_info = ptr_ty.ptrInfo(mod);
  16090     assert(ptr_info.flags.size == .Many or ptr_info.flags.size == .C);
  16091 
  16092     const new_ptr_ty = t: {
  16093         // Calculate the new pointer alignment.
  16094         // This code is duplicated in `elemPtrType`.
  16095         if (ptr_info.flags.alignment == .none) {
  16096             // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
  16097             break :t ptr_ty;
  16098         }
  16099         // If the addend is not a comptime-known value we can still count on
  16100         // it being a multiple of the type size.
  16101         const elem_size = Type.fromInterned(ptr_info.child).abiSize(mod);
  16102         const addend = if (opt_off_val) |off_val| a: {
  16103             const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(mod));
  16104             break :a elem_size * off_int;
  16105         } else elem_size;
  16106 
  16107         // The resulting pointer is aligned to the lcd between the offset (an
  16108         // arbitrary number) and the alignment factor (always a power of two,
  16109         // non zero).
  16110         const new_align: Alignment = @enumFromInt(@min(
  16111             @ctz(addend),
  16112             @intFromEnum(ptr_info.flags.alignment),
  16113         ));
  16114         assert(new_align != .none);
  16115 
  16116         break :t try sema.ptrType(.{
  16117             .child = ptr_info.child,
  16118             .sentinel = ptr_info.sentinel,
  16119             .flags = .{
  16120                 .size = ptr_info.flags.size,
  16121                 .alignment = new_align,
  16122                 .is_const = ptr_info.flags.is_const,
  16123                 .is_volatile = ptr_info.flags.is_volatile,
  16124                 .is_allowzero = ptr_info.flags.is_allowzero,
  16125                 .address_space = ptr_info.flags.address_space,
  16126             },
  16127         });
  16128     };
  16129 
  16130     const runtime_src = rs: {
  16131         if (opt_ptr_val) |ptr_val| {
  16132             if (opt_off_val) |offset_val| {
  16133                 if (ptr_val.isUndef(mod)) return mod.undefRef(new_ptr_ty);
  16134 
  16135                 const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(mod));
  16136                 if (offset_int == 0) return ptr;
  16137                 if (try ptr_val.getUnsignedIntAdvanced(mod, sema)) |addr| {
  16138                     const elem_size = Type.fromInterned(ptr_info.child).abiSize(mod);
  16139                     const new_addr = switch (air_tag) {
  16140                         .ptr_add => addr + elem_size * offset_int,
  16141                         .ptr_sub => addr - elem_size * offset_int,
  16142                         else => unreachable,
  16143                     };
  16144                     const new_ptr_val = try mod.ptrIntValue(new_ptr_ty, new_addr);
  16145                     return Air.internedToRef(new_ptr_val.toIntern());
  16146                 }
  16147                 if (air_tag == .ptr_sub) {
  16148                     return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{});
  16149                 }
  16150                 const new_ptr_val = try ptr_val.elemPtr(new_ptr_ty, offset_int, mod);
  16151                 return Air.internedToRef(new_ptr_val.toIntern());
  16152             } else break :rs offset_src;
  16153         } else break :rs ptr_src;
  16154     };
  16155 
  16156     try sema.requireRuntimeBlock(block, op_src, runtime_src);
  16157     return block.addInst(.{
  16158         .tag = air_tag,
  16159         .data = .{ .ty_pl = .{
  16160             .ty = Air.internedToRef(new_ptr_ty.toIntern()),
  16161             .payload = try sema.addExtra(Air.Bin{
  16162                 .lhs = ptr,
  16163                 .rhs = offset,
  16164             }),
  16165         } },
  16166     });
  16167 }
  16168 
  16169 fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16170     const tracy = trace(@src());
  16171     defer tracy.end();
  16172 
  16173     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  16174     const src = inst_data.src();
  16175     const ptr_src = src; // TODO better source location
  16176     const ptr = try sema.resolveInst(inst_data.operand);
  16177     return sema.analyzeLoad(block, src, ptr, ptr_src);
  16178 }
  16179 
  16180 fn zirAsm(
  16181     sema: *Sema,
  16182     block: *Block,
  16183     extended: Zir.Inst.Extended.InstData,
  16184     tmpl_is_expr: bool,
  16185 ) CompileError!Air.Inst.Ref {
  16186     const tracy = trace(@src());
  16187     defer tracy.end();
  16188 
  16189     const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);
  16190     const src = LazySrcLoc.nodeOffset(extra.data.src_node);
  16191     const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node };
  16192     const outputs_len: u5 = @truncate(extended.small);
  16193     const inputs_len: u5 = @truncate(extended.small >> 5);
  16194     const clobbers_len: u5 = @truncate(extended.small >> 10);
  16195     const is_volatile = @as(u1, @truncate(extended.small >> 15)) != 0;
  16196     const is_global_assembly = sema.func_index == .none;
  16197 
  16198     const asm_source: []const u8 = if (tmpl_is_expr) blk: {
  16199         const tmpl: Zir.Inst.Ref = @enumFromInt(extra.data.asm_source);
  16200         const s: []const u8 = try sema.resolveConstString(block, src, tmpl, .{
  16201             .needed_comptime_reason = "assembly code must be comptime-known",
  16202         });
  16203         break :blk s;
  16204     } else sema.code.nullTerminatedString(extra.data.asm_source);
  16205 
  16206     if (is_global_assembly) {
  16207         if (outputs_len != 0) {
  16208             return sema.fail(block, src, "module-level assembly does not support outputs", .{});
  16209         }
  16210         if (inputs_len != 0) {
  16211             return sema.fail(block, src, "module-level assembly does not support inputs", .{});
  16212         }
  16213         if (clobbers_len != 0) {
  16214             return sema.fail(block, src, "module-level assembly does not support clobbers", .{});
  16215         }
  16216         if (is_volatile) {
  16217             return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{});
  16218         }
  16219         try sema.mod.addGlobalAssembly(sema.owner_decl_index, asm_source);
  16220         return .void_value;
  16221     }
  16222 
  16223     if (block.is_comptime) {
  16224         try sema.requireRuntimeBlock(block, src, null);
  16225     }
  16226 
  16227     var extra_i = extra.end;
  16228     var output_type_bits = extra.data.output_type_bits;
  16229     var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len;
  16230 
  16231     const ConstraintName = struct { c: []const u8, n: []const u8 };
  16232     const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len);
  16233     const outputs = try sema.arena.alloc(ConstraintName, outputs_len);
  16234     var expr_ty = Air.Inst.Ref.void_type;
  16235 
  16236     for (out_args, 0..) |*arg, out_i| {
  16237         const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i);
  16238         extra_i = output.end;
  16239 
  16240         const is_type = @as(u1, @truncate(output_type_bits)) != 0;
  16241         output_type_bits >>= 1;
  16242 
  16243         if (is_type) {
  16244             // Indicate the output is the asm instruction return value.
  16245             arg.* = .none;
  16246             const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand);
  16247             try sema.queueFullTypeResolution(out_ty);
  16248             expr_ty = Air.internedToRef(out_ty.toIntern());
  16249         } else {
  16250             arg.* = try sema.resolveInst(output.data.operand);
  16251         }
  16252 
  16253         const constraint = sema.code.nullTerminatedString(output.data.constraint);
  16254         const name = sema.code.nullTerminatedString(output.data.name);
  16255         needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
  16256 
  16257         outputs[out_i] = .{ .c = constraint, .n = name };
  16258     }
  16259 
  16260     const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len);
  16261     const inputs = try sema.arena.alloc(ConstraintName, inputs_len);
  16262     const mod = sema.mod;
  16263 
  16264     for (args, 0..) |*arg, arg_i| {
  16265         const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i);
  16266         extra_i = input.end;
  16267 
  16268         const uncasted_arg = try sema.resolveInst(input.data.operand);
  16269         const uncasted_arg_ty = sema.typeOf(uncasted_arg);
  16270         switch (uncasted_arg_ty.zigTypeTag(mod)) {
  16271             .ComptimeInt => arg.* = try sema.coerce(block, Type.usize, uncasted_arg, src),
  16272             .ComptimeFloat => arg.* = try sema.coerce(block, Type.f64, uncasted_arg, src),
  16273             else => {
  16274                 arg.* = uncasted_arg;
  16275                 try sema.queueFullTypeResolution(uncasted_arg_ty);
  16276             },
  16277         }
  16278 
  16279         const constraint = sema.code.nullTerminatedString(input.data.constraint);
  16280         const name = sema.code.nullTerminatedString(input.data.name);
  16281         needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
  16282         inputs[arg_i] = .{ .c = constraint, .n = name };
  16283     }
  16284 
  16285     const clobbers = try sema.arena.alloc([]const u8, clobbers_len);
  16286     for (clobbers) |*name| {
  16287         name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
  16288         extra_i += 1;
  16289 
  16290         needed_capacity += name.*.len / 4 + 1;
  16291     }
  16292 
  16293     needed_capacity += (asm_source.len + 3) / 4;
  16294 
  16295     const gpa = sema.gpa;
  16296     try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity);
  16297     const asm_air = try block.addInst(.{
  16298         .tag = .assembly,
  16299         .data = .{ .ty_pl = .{
  16300             .ty = expr_ty,
  16301             .payload = sema.addExtraAssumeCapacity(Air.Asm{
  16302                 .source_len = @intCast(asm_source.len),
  16303                 .outputs_len = outputs_len,
  16304                 .inputs_len = @intCast(args.len),
  16305                 .flags = (@as(u32, @intFromBool(is_volatile)) << 31) | @as(u32, @intCast(clobbers.len)),
  16306             }),
  16307         } },
  16308     });
  16309     sema.appendRefsAssumeCapacity(out_args);
  16310     sema.appendRefsAssumeCapacity(args);
  16311     for (outputs) |o| {
  16312         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  16313         @memcpy(buffer[0..o.c.len], o.c);
  16314         buffer[o.c.len] = 0;
  16315         @memcpy(buffer[o.c.len + 1 ..][0..o.n.len], o.n);
  16316         buffer[o.c.len + 1 + o.n.len] = 0;
  16317         sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4;
  16318     }
  16319     for (inputs) |input| {
  16320         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  16321         @memcpy(buffer[0..input.c.len], input.c);
  16322         buffer[input.c.len] = 0;
  16323         @memcpy(buffer[input.c.len + 1 ..][0..input.n.len], input.n);
  16324         buffer[input.c.len + 1 + input.n.len] = 0;
  16325         sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4;
  16326     }
  16327     for (clobbers) |clobber| {
  16328         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  16329         @memcpy(buffer[0..clobber.len], clobber);
  16330         buffer[clobber.len] = 0;
  16331         sema.air_extra.items.len += clobber.len / 4 + 1;
  16332     }
  16333     {
  16334         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  16335         @memcpy(buffer[0..asm_source.len], asm_source);
  16336         sema.air_extra.items.len += (asm_source.len + 3) / 4;
  16337     }
  16338     return asm_air;
  16339 }
  16340 
  16341 /// Only called for equality operators. See also `zirCmp`.
  16342 fn zirCmpEq(
  16343     sema: *Sema,
  16344     block: *Block,
  16345     inst: Zir.Inst.Index,
  16346     op: std.math.CompareOperator,
  16347     air_tag: Air.Inst.Tag,
  16348 ) CompileError!Air.Inst.Ref {
  16349     const tracy = trace(@src());
  16350     defer tracy.end();
  16351 
  16352     const mod = sema.mod;
  16353     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  16354     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  16355     const src: LazySrcLoc = inst_data.src();
  16356     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  16357     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  16358     const lhs = try sema.resolveInst(extra.lhs);
  16359     const rhs = try sema.resolveInst(extra.rhs);
  16360 
  16361     const lhs_ty = sema.typeOf(lhs);
  16362     const rhs_ty = sema.typeOf(rhs);
  16363     const lhs_ty_tag = lhs_ty.zigTypeTag(mod);
  16364     const rhs_ty_tag = rhs_ty.zigTypeTag(mod);
  16365     if (lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
  16366         // null == null, null != null
  16367         return if (op == .eq) .bool_true else .bool_false;
  16368     }
  16369 
  16370     // comparing null with optionals
  16371     if (lhs_ty_tag == .Null and (rhs_ty_tag == .Optional or rhs_ty.isCPtr(mod))) {
  16372         return sema.analyzeIsNull(block, src, rhs, op == .neq);
  16373     }
  16374     if (rhs_ty_tag == .Null and (lhs_ty_tag == .Optional or lhs_ty.isCPtr(mod))) {
  16375         return sema.analyzeIsNull(block, src, lhs, op == .neq);
  16376     }
  16377 
  16378     if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
  16379         const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty;
  16380         return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(mod)});
  16381     }
  16382 
  16383     if (lhs_ty_tag == .Union and (rhs_ty_tag == .EnumLiteral or rhs_ty_tag == .Enum)) {
  16384         return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op);
  16385     }
  16386     if (rhs_ty_tag == .Union and (lhs_ty_tag == .EnumLiteral or lhs_ty_tag == .Enum)) {
  16387         return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op);
  16388     }
  16389 
  16390     if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
  16391         const runtime_src: LazySrcLoc = src: {
  16392             if (try sema.resolveValue(lhs)) |lval| {
  16393                 if (try sema.resolveValue(rhs)) |rval| {
  16394                     if (lval.isUndef(mod) or rval.isUndef(mod)) {
  16395                         return mod.undefRef(Type.bool);
  16396                     }
  16397                     const lkey = mod.intern_pool.indexToKey(lval.toIntern());
  16398                     const rkey = mod.intern_pool.indexToKey(rval.toIntern());
  16399                     return if ((lkey.err.name == rkey.err.name) == (op == .eq))
  16400                         .bool_true
  16401                     else
  16402                         .bool_false;
  16403                 } else {
  16404                     break :src rhs_src;
  16405                 }
  16406             } else {
  16407                 break :src lhs_src;
  16408             }
  16409         };
  16410         try sema.requireRuntimeBlock(block, src, runtime_src);
  16411         return block.addBinOp(air_tag, lhs, rhs);
  16412     }
  16413     if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) {
  16414         const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs);
  16415         const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs);
  16416         return if (lhs_as_type.eql(rhs_as_type, mod) == (op == .eq)) .bool_true else .bool_false;
  16417     }
  16418     return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true);
  16419 }
  16420 
  16421 fn analyzeCmpUnionTag(
  16422     sema: *Sema,
  16423     block: *Block,
  16424     src: LazySrcLoc,
  16425     un: Air.Inst.Ref,
  16426     un_src: LazySrcLoc,
  16427     tag: Air.Inst.Ref,
  16428     tag_src: LazySrcLoc,
  16429     op: std.math.CompareOperator,
  16430 ) CompileError!Air.Inst.Ref {
  16431     const mod = sema.mod;
  16432     const union_ty = sema.typeOf(un);
  16433     try sema.resolveTypeFields(union_ty);
  16434     const union_tag_ty = union_ty.unionTagType(mod) orelse {
  16435         const msg = msg: {
  16436             const msg = try sema.errMsg(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{});
  16437             errdefer msg.destroy(sema.gpa);
  16438             try mod.errNoteNonLazy(union_ty.declSrcLoc(mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(mod)});
  16439             break :msg msg;
  16440         };
  16441         return sema.failWithOwnedErrorMsg(block, msg);
  16442     };
  16443     // Coerce both the union and the tag to the union's tag type, and then execute the
  16444     // enum comparison codepath.
  16445     const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src);
  16446     const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src);
  16447 
  16448     if (try sema.resolveValue(coerced_tag)) |enum_val| {
  16449         if (enum_val.isUndef(mod)) return mod.undefRef(Type.bool);
  16450         const field_ty = union_ty.unionFieldType(enum_val, mod).?;
  16451         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  16452             return .bool_false;
  16453         }
  16454     }
  16455 
  16456     return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src);
  16457 }
  16458 
  16459 /// Only called for non-equality operators. See also `zirCmpEq`.
  16460 fn zirCmp(
  16461     sema: *Sema,
  16462     block: *Block,
  16463     inst: Zir.Inst.Index,
  16464     op: std.math.CompareOperator,
  16465 ) CompileError!Air.Inst.Ref {
  16466     const tracy = trace(@src());
  16467     defer tracy.end();
  16468 
  16469     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  16470     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  16471     const src: LazySrcLoc = inst_data.src();
  16472     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  16473     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  16474     const lhs = try sema.resolveInst(extra.lhs);
  16475     const rhs = try sema.resolveInst(extra.rhs);
  16476     return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false);
  16477 }
  16478 
  16479 fn analyzeCmp(
  16480     sema: *Sema,
  16481     block: *Block,
  16482     src: LazySrcLoc,
  16483     lhs: Air.Inst.Ref,
  16484     rhs: Air.Inst.Ref,
  16485     op: std.math.CompareOperator,
  16486     lhs_src: LazySrcLoc,
  16487     rhs_src: LazySrcLoc,
  16488     is_equality_cmp: bool,
  16489 ) CompileError!Air.Inst.Ref {
  16490     const mod = sema.mod;
  16491     const lhs_ty = sema.typeOf(lhs);
  16492     const rhs_ty = sema.typeOf(rhs);
  16493     if (lhs_ty.zigTypeTag(mod) != .Optional and rhs_ty.zigTypeTag(mod) != .Optional) {
  16494         try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  16495     }
  16496 
  16497     if (lhs_ty.zigTypeTag(mod) == .Vector and rhs_ty.zigTypeTag(mod) == .Vector) {
  16498         return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src);
  16499     }
  16500     if (lhs_ty.isNumeric(mod) and rhs_ty.isNumeric(mod)) {
  16501         // This operation allows any combination of integer and float types, regardless of the
  16502         // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
  16503         // numeric types.
  16504         return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src);
  16505     }
  16506     if (is_equality_cmp and lhs_ty.zigTypeTag(mod) == .ErrorUnion and rhs_ty.zigTypeTag(mod) == .ErrorSet) {
  16507         const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs);
  16508         return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src);
  16509     }
  16510     if (is_equality_cmp and lhs_ty.zigTypeTag(mod) == .ErrorSet and rhs_ty.zigTypeTag(mod) == .ErrorUnion) {
  16511         const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs);
  16512         return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src);
  16513     }
  16514     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  16515     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } });
  16516     if (!resolved_type.isSelfComparable(mod, is_equality_cmp)) {
  16517         return sema.fail(block, src, "operator {s} not allowed for type '{}'", .{
  16518             compareOperatorName(op), resolved_type.fmt(mod),
  16519         });
  16520     }
  16521     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  16522     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  16523     return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src);
  16524 }
  16525 
  16526 fn compareOperatorName(comp: std.math.CompareOperator) []const u8 {
  16527     return switch (comp) {
  16528         .lt => "<",
  16529         .lte => "<=",
  16530         .eq => "==",
  16531         .gte => ">=",
  16532         .gt => ">",
  16533         .neq => "!=",
  16534     };
  16535 }
  16536 
  16537 fn cmpSelf(
  16538     sema: *Sema,
  16539     block: *Block,
  16540     src: LazySrcLoc,
  16541     casted_lhs: Air.Inst.Ref,
  16542     casted_rhs: Air.Inst.Ref,
  16543     op: std.math.CompareOperator,
  16544     lhs_src: LazySrcLoc,
  16545     rhs_src: LazySrcLoc,
  16546 ) CompileError!Air.Inst.Ref {
  16547     const mod = sema.mod;
  16548     const resolved_type = sema.typeOf(casted_lhs);
  16549     const runtime_src: LazySrcLoc = src: {
  16550         if (try sema.resolveValue(casted_lhs)) |lhs_val| {
  16551             if (lhs_val.isUndef(mod)) return mod.undefRef(Type.bool);
  16552             if (try sema.resolveValue(casted_rhs)) |rhs_val| {
  16553                 if (rhs_val.isUndef(mod)) return mod.undefRef(Type.bool);
  16554 
  16555                 if (resolved_type.zigTypeTag(mod) == .Vector) {
  16556                     const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type);
  16557                     return Air.internedToRef(cmp_val.toIntern());
  16558                 }
  16559 
  16560                 return if (try sema.compareAll(lhs_val, op, rhs_val, resolved_type))
  16561                     .bool_true
  16562                 else
  16563                     .bool_false;
  16564             } else {
  16565                 if (resolved_type.zigTypeTag(mod) == .Bool) {
  16566                     // We can lower bool eq/neq more efficiently.
  16567                     return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src);
  16568                 }
  16569                 break :src rhs_src;
  16570             }
  16571         } else {
  16572             // For bools, we still check the other operand, because we can lower
  16573             // bool eq/neq more efficiently.
  16574             if (resolved_type.zigTypeTag(mod) == .Bool) {
  16575                 if (try sema.resolveValue(casted_rhs)) |rhs_val| {
  16576                     if (rhs_val.isUndef(mod)) return mod.undefRef(Type.bool);
  16577                     return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);
  16578                 }
  16579             }
  16580             break :src lhs_src;
  16581         }
  16582     };
  16583     try sema.requireRuntimeBlock(block, src, runtime_src);
  16584     if (resolved_type.zigTypeTag(mod) == .Vector) {
  16585         return block.addCmpVector(casted_lhs, casted_rhs, op);
  16586     }
  16587     const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized);
  16588     return block.addBinOp(tag, casted_lhs, casted_rhs);
  16589 }
  16590 
  16591 /// cmp_eq (x, false) => not(x)
  16592 /// cmp_eq (x, true ) => x
  16593 /// cmp_neq(x, false) => x
  16594 /// cmp_neq(x, true ) => not(x)
  16595 fn runtimeBoolCmp(
  16596     sema: *Sema,
  16597     block: *Block,
  16598     src: LazySrcLoc,
  16599     op: std.math.CompareOperator,
  16600     lhs: Air.Inst.Ref,
  16601     rhs: bool,
  16602     runtime_src: LazySrcLoc,
  16603 ) CompileError!Air.Inst.Ref {
  16604     if ((op == .neq) == rhs) {
  16605         try sema.requireRuntimeBlock(block, src, runtime_src);
  16606         return block.addTyOp(.not, Type.bool, lhs);
  16607     } else {
  16608         return lhs;
  16609     }
  16610 }
  16611 
  16612 fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16613     const mod = sema.mod;
  16614     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  16615     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  16616     const ty = try sema.resolveType(block, operand_src, inst_data.operand);
  16617     switch (ty.zigTypeTag(mod)) {
  16618         .Fn,
  16619         .NoReturn,
  16620         .Undefined,
  16621         .Null,
  16622         .Opaque,
  16623         => return sema.fail(block, operand_src, "no size available for type '{}'", .{ty.fmt(mod)}),
  16624 
  16625         .Type,
  16626         .EnumLiteral,
  16627         .ComptimeFloat,
  16628         .ComptimeInt,
  16629         .Void,
  16630         => return mod.intRef(Type.comptime_int, 0),
  16631 
  16632         .Bool,
  16633         .Int,
  16634         .Float,
  16635         .Pointer,
  16636         .Array,
  16637         .Struct,
  16638         .Optional,
  16639         .ErrorUnion,
  16640         .ErrorSet,
  16641         .Enum,
  16642         .Union,
  16643         .Vector,
  16644         .Frame,
  16645         .AnyFrame,
  16646         => {},
  16647     }
  16648     const val = try ty.lazyAbiSize(mod);
  16649     if (val.isLazySize(mod)) {
  16650         try sema.queueFullTypeResolution(ty);
  16651     }
  16652     return Air.internedToRef(val.toIntern());
  16653 }
  16654 
  16655 fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16656     const mod = sema.mod;
  16657     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  16658     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  16659     const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
  16660     switch (operand_ty.zigTypeTag(mod)) {
  16661         .Fn,
  16662         .NoReturn,
  16663         .Undefined,
  16664         .Null,
  16665         .Opaque,
  16666         => return sema.fail(block, operand_src, "no size available for type '{}'", .{operand_ty.fmt(mod)}),
  16667 
  16668         .Type,
  16669         .EnumLiteral,
  16670         .ComptimeFloat,
  16671         .ComptimeInt,
  16672         .Void,
  16673         => return mod.intRef(Type.comptime_int, 0),
  16674 
  16675         .Bool,
  16676         .Int,
  16677         .Float,
  16678         .Pointer,
  16679         .Array,
  16680         .Struct,
  16681         .Optional,
  16682         .ErrorUnion,
  16683         .ErrorSet,
  16684         .Enum,
  16685         .Union,
  16686         .Vector,
  16687         .Frame,
  16688         .AnyFrame,
  16689         => {},
  16690     }
  16691     const bit_size = try operand_ty.bitSizeAdvanced(mod, sema);
  16692     return mod.intRef(Type.comptime_int, bit_size);
  16693 }
  16694 
  16695 fn zirThis(
  16696     sema: *Sema,
  16697     block: *Block,
  16698     extended: Zir.Inst.Extended.InstData,
  16699 ) CompileError!Air.Inst.Ref {
  16700     const mod = sema.mod;
  16701     const this_decl_index = mod.namespaceDeclIndex(block.namespace);
  16702     const src = LazySrcLoc.nodeOffset(@bitCast(extended.operand));
  16703     return sema.analyzeDeclVal(block, src, this_decl_index);
  16704 }
  16705 
  16706 fn zirClosureCapture(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  16707     const mod = sema.mod;
  16708     const gpa = sema.gpa;
  16709     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
  16710     // Closures are not necessarily constant values. For example, the
  16711     // code might do something like this:
  16712     // fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; }
  16713     // ...in which case the closure_capture instruction has access to a runtime
  16714     // value only. In such case only the type is saved into the scope.
  16715     const operand = try sema.resolveInst(inst_data.operand);
  16716     const ty = sema.typeOf(operand);
  16717     const key: CaptureScope.Key = .{
  16718         .zir_index = inst,
  16719         .index = block.wip_capture_scope,
  16720     };
  16721     if (try sema.resolveValue(operand)) |val| {
  16722         try mod.comptime_capture_scopes.put(gpa, key, try val.intern(ty, mod));
  16723     } else {
  16724         try mod.runtime_capture_scopes.put(gpa, key, ty.toIntern());
  16725     }
  16726 }
  16727 
  16728 fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16729     const mod = sema.mod;
  16730     //const ip = &mod.intern_pool;
  16731     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].inst_node;
  16732     var scope: CaptureScope.Index = mod.declPtr(block.src_decl).src_scope;
  16733     assert(scope != .none);
  16734     // Note: The target closure must be in this scope list.
  16735     // If it's not here, the zir is invalid, or the list is broken.
  16736     const capture_ty = while (true) {
  16737         // Note: We don't need to add a dependency here, because
  16738         // decls always depend on their lexical parents.
  16739         const key: CaptureScope.Key = .{
  16740             .zir_index = inst_data.inst,
  16741             .index = scope,
  16742         };
  16743         if (mod.comptime_capture_scopes.get(key)) |val|
  16744             return Air.internedToRef(val);
  16745         if (mod.runtime_capture_scopes.get(key)) |ty|
  16746             break ty;
  16747         scope = scope.parent(mod);
  16748         assert(scope != .none);
  16749     };
  16750 
  16751     // The comptime case is handled already above. Runtime case below.
  16752 
  16753     if (!block.is_typeof and sema.func_index == .none) {
  16754         const msg = msg: {
  16755             const name = name: {
  16756                 const file = sema.owner_decl.getFileScope(mod);
  16757                 const tree = file.getTree(sema.gpa) catch |err| {
  16758                     // In this case we emit a warning + a less precise source location.
  16759                     log.warn("unable to load {s}: {s}", .{
  16760                         file.sub_file_path, @errorName(err),
  16761                     });
  16762                     break :name null;
  16763                 };
  16764                 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node);
  16765                 const token = tree.nodes.items(.main_token)[node];
  16766                 break :name tree.tokenSlice(token);
  16767             };
  16768 
  16769             const msg = if (name) |some|
  16770                 try sema.errMsg(block, inst_data.src(), "'{s}' not accessible outside function scope", .{some})
  16771             else
  16772                 try sema.errMsg(block, inst_data.src(), "variable not accessible outside function scope", .{});
  16773             errdefer msg.destroy(sema.gpa);
  16774 
  16775             // TODO add "declared here" note
  16776             break :msg msg;
  16777         };
  16778         return sema.failWithOwnedErrorMsg(block, msg);
  16779     }
  16780 
  16781     if (!block.is_typeof and !block.is_comptime and sema.func_index != .none) {
  16782         const msg = msg: {
  16783             const name = name: {
  16784                 const file = sema.owner_decl.getFileScope(mod);
  16785                 const tree = file.getTree(sema.gpa) catch |err| {
  16786                     // In this case we emit a warning + a less precise source location.
  16787                     log.warn("unable to load {s}: {s}", .{
  16788                         file.sub_file_path, @errorName(err),
  16789                     });
  16790                     break :name null;
  16791                 };
  16792                 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node);
  16793                 const token = tree.nodes.items(.main_token)[node];
  16794                 break :name tree.tokenSlice(token);
  16795             };
  16796 
  16797             const msg = if (name) |some|
  16798                 try sema.errMsg(block, inst_data.src(), "'{s}' not accessible from inner function", .{some})
  16799             else
  16800                 try sema.errMsg(block, inst_data.src(), "variable not accessible from inner function", .{});
  16801             errdefer msg.destroy(sema.gpa);
  16802 
  16803             try sema.errNote(block, LazySrcLoc.nodeOffset(0), msg, "crossed function definition here", .{});
  16804 
  16805             // TODO add "declared here" note
  16806             break :msg msg;
  16807         };
  16808         return sema.failWithOwnedErrorMsg(block, msg);
  16809     }
  16810 
  16811     assert(block.is_typeof);
  16812     // We need a dummy runtime instruction with the correct type.
  16813     return block.addTy(.alloc, Type.fromInterned(capture_ty));
  16814 }
  16815 
  16816 fn zirRetAddr(
  16817     sema: *Sema,
  16818     block: *Block,
  16819     extended: Zir.Inst.Extended.InstData,
  16820 ) CompileError!Air.Inst.Ref {
  16821     _ = extended;
  16822     if (block.is_comptime) {
  16823         // TODO: we could give a meaningful lazy value here. #14938
  16824         return sema.mod.intRef(Type.usize, 0);
  16825     } else {
  16826         return block.addNoOp(.ret_addr);
  16827     }
  16828 }
  16829 
  16830 fn zirFrameAddress(
  16831     sema: *Sema,
  16832     block: *Block,
  16833     extended: Zir.Inst.Extended.InstData,
  16834 ) CompileError!Air.Inst.Ref {
  16835     const src = LazySrcLoc.nodeOffset(@bitCast(extended.operand));
  16836     try sema.requireRuntimeBlock(block, src, null);
  16837     return try block.addNoOp(.frame_addr);
  16838 }
  16839 
  16840 fn zirBuiltinSrc(
  16841     sema: *Sema,
  16842     block: *Block,
  16843     extended: Zir.Inst.Extended.InstData,
  16844 ) CompileError!Air.Inst.Ref {
  16845     _ = block;
  16846     const tracy = trace(@src());
  16847     defer tracy.end();
  16848 
  16849     const mod = sema.mod;
  16850     const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data;
  16851     const fn_owner_decl = mod.funcOwnerDeclPtr(sema.func_index);
  16852     const ip = &mod.intern_pool;
  16853     const gpa = sema.gpa;
  16854 
  16855     const func_name_val = v: {
  16856         // This dupe prevents InternPool string pool memory from being reallocated
  16857         // while a reference exists.
  16858         const bytes = try sema.arena.dupe(u8, ip.stringToSlice(fn_owner_decl.name));
  16859         const array_ty = try ip.get(gpa, .{ .array_type = .{
  16860             .len = bytes.len,
  16861             .sentinel = .zero_u8,
  16862             .child = .u8_type,
  16863         } });
  16864         break :v try ip.get(gpa, .{ .ptr = .{
  16865             .ty = .slice_const_u8_sentinel_0_type,
  16866             .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(),
  16867             .addr = .{ .anon_decl = .{
  16868                 .orig_ty = .slice_const_u8_sentinel_0_type,
  16869                 .val = try ip.get(gpa, .{ .aggregate = .{
  16870                     .ty = array_ty,
  16871                     .storage = .{ .bytes = bytes },
  16872                 } }),
  16873             } },
  16874         } });
  16875     };
  16876 
  16877     const file_name_val = v: {
  16878         // The compiler must not call realpath anywhere.
  16879         const bytes = try fn_owner_decl.getFileScope(mod).fullPathZ(sema.arena);
  16880         const array_ty = try ip.get(gpa, .{ .array_type = .{
  16881             .len = bytes.len,
  16882             .sentinel = .zero_u8,
  16883             .child = .u8_type,
  16884         } });
  16885         break :v try ip.get(gpa, .{ .ptr = .{
  16886             .ty = .slice_const_u8_sentinel_0_type,
  16887             .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(),
  16888             .addr = .{ .anon_decl = .{
  16889                 .orig_ty = .slice_const_u8_sentinel_0_type,
  16890                 .val = try ip.get(gpa, .{ .aggregate = .{
  16891                     .ty = array_ty,
  16892                     .storage = .{ .bytes = bytes },
  16893                 } }),
  16894             } },
  16895         } });
  16896     };
  16897 
  16898     const src_loc_ty = try sema.getBuiltinType("SourceLocation");
  16899     const fields = .{
  16900         // file: [:0]const u8,
  16901         file_name_val,
  16902         // fn_name: [:0]const u8,
  16903         func_name_val,
  16904         // line: u32,
  16905         (try mod.intValue(Type.u32, extra.line + 1)).toIntern(),
  16906         // column: u32,
  16907         (try mod.intValue(Type.u32, extra.column + 1)).toIntern(),
  16908     };
  16909     return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  16910         .ty = src_loc_ty.toIntern(),
  16911         .storage = .{ .elems = &fields },
  16912     } })));
  16913 }
  16914 
  16915 fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16916     const mod = sema.mod;
  16917     const gpa = sema.gpa;
  16918     const ip = &mod.intern_pool;
  16919     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  16920     const src = inst_data.src();
  16921     const ty = try sema.resolveType(block, src, inst_data.operand);
  16922     const type_info_ty = try sema.getBuiltinType("Type");
  16923     const type_info_tag_ty = type_info_ty.unionTagType(mod).?;
  16924 
  16925     switch (ty.zigTypeTag(mod)) {
  16926         .Type,
  16927         .Void,
  16928         .Bool,
  16929         .NoReturn,
  16930         .ComptimeFloat,
  16931         .ComptimeInt,
  16932         .Undefined,
  16933         .Null,
  16934         .EnumLiteral,
  16935         => |type_info_tag| return Air.internedToRef((try mod.intern(.{ .un = .{
  16936             .ty = type_info_ty.toIntern(),
  16937             .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(type_info_tag))).toIntern(),
  16938             .val = .void_value,
  16939         } }))),
  16940         .Fn => {
  16941             const fn_info_decl_index = (try sema.namespaceLookup(
  16942                 block,
  16943                 src,
  16944                 type_info_ty.getNamespaceIndex(mod).unwrap().?,
  16945                 try ip.getOrPutString(gpa, "Fn"),
  16946             )).?;
  16947             try sema.ensureDeclAnalyzed(fn_info_decl_index);
  16948             const fn_info_decl = mod.declPtr(fn_info_decl_index);
  16949             const fn_info_ty = fn_info_decl.val.toType();
  16950 
  16951             const param_info_decl_index = (try sema.namespaceLookup(
  16952                 block,
  16953                 src,
  16954                 fn_info_ty.getNamespaceIndex(mod).unwrap().?,
  16955                 try ip.getOrPutString(gpa, "Param"),
  16956             )).?;
  16957             try sema.ensureDeclAnalyzed(param_info_decl_index);
  16958             const param_info_decl = mod.declPtr(param_info_decl_index);
  16959             const param_info_ty = param_info_decl.val.toType();
  16960 
  16961             const func_ty_info = mod.typeToFunc(ty).?;
  16962             const param_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len);
  16963             for (param_vals, 0..) |*param_val, i| {
  16964                 const param_ty = func_ty_info.param_types.get(ip)[i];
  16965                 const is_generic = param_ty == .generic_poison_type;
  16966                 const param_ty_val = try ip.get(gpa, .{ .opt = .{
  16967                     .ty = try ip.get(gpa, .{ .opt_type = .type_type }),
  16968                     .val = if (is_generic) .none else param_ty,
  16969                 } });
  16970 
  16971                 const is_noalias = blk: {
  16972                     const index = std.math.cast(u5, i) orelse break :blk false;
  16973                     break :blk @as(u1, @truncate(func_ty_info.noalias_bits >> index)) != 0;
  16974                 };
  16975 
  16976                 const param_fields = .{
  16977                     // is_generic: bool,
  16978                     Value.makeBool(is_generic).toIntern(),
  16979                     // is_noalias: bool,
  16980                     Value.makeBool(is_noalias).toIntern(),
  16981                     // type: ?type,
  16982                     param_ty_val,
  16983                 };
  16984                 param_val.* = try mod.intern(.{ .aggregate = .{
  16985                     .ty = param_info_ty.toIntern(),
  16986                     .storage = .{ .elems = &param_fields },
  16987                 } });
  16988             }
  16989 
  16990             const args_val = v: {
  16991                 const new_decl_ty = try mod.arrayType(.{
  16992                     .len = param_vals.len,
  16993                     .child = param_info_ty.toIntern(),
  16994                 });
  16995                 const new_decl_val = try mod.intern(.{ .aggregate = .{
  16996                     .ty = new_decl_ty.toIntern(),
  16997                     .storage = .{ .elems = param_vals },
  16998                 } });
  16999                 const ptr_ty = (try sema.ptrType(.{
  17000                     .child = param_info_ty.toIntern(),
  17001                     .flags = .{
  17002                         .size = .Slice,
  17003                         .is_const = true,
  17004                     },
  17005                 })).toIntern();
  17006                 break :v try mod.intern(.{ .ptr = .{
  17007                     .ty = ptr_ty,
  17008                     .addr = .{ .anon_decl = .{
  17009                         .orig_ty = ptr_ty,
  17010                         .val = new_decl_val,
  17011                     } },
  17012                     .len = (try mod.intValue(Type.usize, param_vals.len)).toIntern(),
  17013                 } });
  17014             };
  17015 
  17016             const ret_ty_opt = try mod.intern(.{ .opt = .{
  17017                 .ty = try ip.get(gpa, .{ .opt_type = .type_type }),
  17018                 .val = if (func_ty_info.return_type == .generic_poison_type)
  17019                     .none
  17020                 else
  17021                     func_ty_info.return_type,
  17022             } });
  17023 
  17024             const callconv_ty = try sema.getBuiltinType("CallingConvention");
  17025 
  17026             const field_values = .{
  17027                 // calling_convention: CallingConvention,
  17028                 (try mod.enumValueFieldIndex(callconv_ty, @intFromEnum(func_ty_info.cc))).toIntern(),
  17029                 // alignment: comptime_int,
  17030                 (try mod.intValue(Type.comptime_int, ty.abiAlignment(mod).toByteUnits(0))).toIntern(),
  17031                 // is_generic: bool,
  17032                 Value.makeBool(func_ty_info.is_generic).toIntern(),
  17033                 // is_var_args: bool,
  17034                 Value.makeBool(func_ty_info.is_var_args).toIntern(),
  17035                 // return_type: ?type,
  17036                 ret_ty_opt,
  17037                 // args: []const Fn.Param,
  17038                 args_val,
  17039             };
  17040             return Air.internedToRef((try mod.intern(.{ .un = .{
  17041                 .ty = type_info_ty.toIntern(),
  17042                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Fn))).toIntern(),
  17043                 .val = try mod.intern(.{ .aggregate = .{
  17044                     .ty = fn_info_ty.toIntern(),
  17045                     .storage = .{ .elems = &field_values },
  17046                 } }),
  17047             } })));
  17048         },
  17049         .Int => {
  17050             const int_info_decl_index = (try sema.namespaceLookup(
  17051                 block,
  17052                 src,
  17053                 type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17054                 try ip.getOrPutString(gpa, "Int"),
  17055             )).?;
  17056             try sema.ensureDeclAnalyzed(int_info_decl_index);
  17057             const int_info_decl = mod.declPtr(int_info_decl_index);
  17058             const int_info_ty = int_info_decl.val.toType();
  17059 
  17060             const signedness_ty = try sema.getBuiltinType("Signedness");
  17061             const info = ty.intInfo(mod);
  17062             const field_values = .{
  17063                 // signedness: Signedness,
  17064                 try (try mod.enumValueFieldIndex(signedness_ty, @intFromEnum(info.signedness))).intern(signedness_ty, mod),
  17065                 // bits: u16,
  17066                 (try mod.intValue(Type.u16, info.bits)).toIntern(),
  17067             };
  17068             return Air.internedToRef((try mod.intern(.{ .un = .{
  17069                 .ty = type_info_ty.toIntern(),
  17070                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Int))).toIntern(),
  17071                 .val = try mod.intern(.{ .aggregate = .{
  17072                     .ty = int_info_ty.toIntern(),
  17073                     .storage = .{ .elems = &field_values },
  17074                 } }),
  17075             } })));
  17076         },
  17077         .Float => {
  17078             const float_info_decl_index = (try sema.namespaceLookup(
  17079                 block,
  17080                 src,
  17081                 type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17082                 try ip.getOrPutString(gpa, "Float"),
  17083             )).?;
  17084             try sema.ensureDeclAnalyzed(float_info_decl_index);
  17085             const float_info_decl = mod.declPtr(float_info_decl_index);
  17086             const float_info_ty = float_info_decl.val.toType();
  17087 
  17088             const field_vals = .{
  17089                 // bits: u16,
  17090                 (try mod.intValue(Type.u16, ty.bitSize(mod))).toIntern(),
  17091             };
  17092             return Air.internedToRef((try mod.intern(.{ .un = .{
  17093                 .ty = type_info_ty.toIntern(),
  17094                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Float))).toIntern(),
  17095                 .val = try mod.intern(.{ .aggregate = .{
  17096                     .ty = float_info_ty.toIntern(),
  17097                     .storage = .{ .elems = &field_vals },
  17098                 } }),
  17099             } })));
  17100         },
  17101         .Pointer => {
  17102             const info = ty.ptrInfo(mod);
  17103             const alignment = if (info.flags.alignment.toByteUnitsOptional()) |alignment|
  17104                 try mod.intValue(Type.comptime_int, alignment)
  17105             else
  17106                 try Type.fromInterned(info.child).lazyAbiAlignment(mod);
  17107 
  17108             const addrspace_ty = try sema.getBuiltinType("AddressSpace");
  17109             const pointer_ty = t: {
  17110                 const decl_index = (try sema.namespaceLookup(
  17111                     block,
  17112                     src,
  17113                     (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?,
  17114                     try ip.getOrPutString(gpa, "Pointer"),
  17115                 )).?;
  17116                 try sema.ensureDeclAnalyzed(decl_index);
  17117                 const decl = mod.declPtr(decl_index);
  17118                 break :t decl.val.toType();
  17119             };
  17120             const ptr_size_ty = t: {
  17121                 const decl_index = (try sema.namespaceLookup(
  17122                     block,
  17123                     src,
  17124                     pointer_ty.getNamespaceIndex(mod).unwrap().?,
  17125                     try ip.getOrPutString(gpa, "Size"),
  17126                 )).?;
  17127                 try sema.ensureDeclAnalyzed(decl_index);
  17128                 const decl = mod.declPtr(decl_index);
  17129                 break :t decl.val.toType();
  17130             };
  17131 
  17132             const field_values = .{
  17133                 // size: Size,
  17134                 try (try mod.enumValueFieldIndex(ptr_size_ty, @intFromEnum(info.flags.size))).intern(ptr_size_ty, mod),
  17135                 // is_const: bool,
  17136                 Value.makeBool(info.flags.is_const).toIntern(),
  17137                 // is_volatile: bool,
  17138                 Value.makeBool(info.flags.is_volatile).toIntern(),
  17139                 // alignment: comptime_int,
  17140                 alignment.toIntern(),
  17141                 // address_space: AddressSpace
  17142                 try (try mod.enumValueFieldIndex(addrspace_ty, @intFromEnum(info.flags.address_space))).intern(addrspace_ty, mod),
  17143                 // child: type,
  17144                 info.child,
  17145                 // is_allowzero: bool,
  17146                 Value.makeBool(info.flags.is_allowzero).toIntern(),
  17147                 // sentinel: ?*const anyopaque,
  17148                 (try sema.optRefValue(switch (info.sentinel) {
  17149                     .none => null,
  17150                     else => Value.fromInterned(info.sentinel),
  17151                 })).toIntern(),
  17152             };
  17153             return Air.internedToRef((try mod.intern(.{ .un = .{
  17154                 .ty = type_info_ty.toIntern(),
  17155                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Pointer))).toIntern(),
  17156                 .val = try mod.intern(.{ .aggregate = .{
  17157                     .ty = pointer_ty.toIntern(),
  17158                     .storage = .{ .elems = &field_values },
  17159                 } }),
  17160             } })));
  17161         },
  17162         .Array => {
  17163             const array_field_ty = t: {
  17164                 const array_field_ty_decl_index = (try sema.namespaceLookup(
  17165                     block,
  17166                     src,
  17167                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17168                     try ip.getOrPutString(gpa, "Array"),
  17169                 )).?;
  17170                 try sema.ensureDeclAnalyzed(array_field_ty_decl_index);
  17171                 const array_field_ty_decl = mod.declPtr(array_field_ty_decl_index);
  17172                 break :t array_field_ty_decl.val.toType();
  17173             };
  17174 
  17175             const info = ty.arrayInfo(mod);
  17176             const field_values = .{
  17177                 // len: comptime_int,
  17178                 (try mod.intValue(Type.comptime_int, info.len)).toIntern(),
  17179                 // child: type,
  17180                 info.elem_type.toIntern(),
  17181                 // sentinel: ?*const anyopaque,
  17182                 (try sema.optRefValue(info.sentinel)).toIntern(),
  17183             };
  17184             return Air.internedToRef((try mod.intern(.{ .un = .{
  17185                 .ty = type_info_ty.toIntern(),
  17186                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Array))).toIntern(),
  17187                 .val = try mod.intern(.{ .aggregate = .{
  17188                     .ty = array_field_ty.toIntern(),
  17189                     .storage = .{ .elems = &field_values },
  17190                 } }),
  17191             } })));
  17192         },
  17193         .Vector => {
  17194             const vector_field_ty = t: {
  17195                 const vector_field_ty_decl_index = (try sema.namespaceLookup(
  17196                     block,
  17197                     src,
  17198                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17199                     try ip.getOrPutString(gpa, "Vector"),
  17200                 )).?;
  17201                 try sema.ensureDeclAnalyzed(vector_field_ty_decl_index);
  17202                 const vector_field_ty_decl = mod.declPtr(vector_field_ty_decl_index);
  17203                 break :t vector_field_ty_decl.val.toType();
  17204             };
  17205 
  17206             const info = ty.arrayInfo(mod);
  17207             const field_values = .{
  17208                 // len: comptime_int,
  17209                 (try mod.intValue(Type.comptime_int, info.len)).toIntern(),
  17210                 // child: type,
  17211                 info.elem_type.toIntern(),
  17212             };
  17213             return Air.internedToRef((try mod.intern(.{ .un = .{
  17214                 .ty = type_info_ty.toIntern(),
  17215                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Vector))).toIntern(),
  17216                 .val = try mod.intern(.{ .aggregate = .{
  17217                     .ty = vector_field_ty.toIntern(),
  17218                     .storage = .{ .elems = &field_values },
  17219                 } }),
  17220             } })));
  17221         },
  17222         .Optional => {
  17223             const optional_field_ty = t: {
  17224                 const optional_field_ty_decl_index = (try sema.namespaceLookup(
  17225                     block,
  17226                     src,
  17227                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17228                     try ip.getOrPutString(gpa, "Optional"),
  17229                 )).?;
  17230                 try sema.ensureDeclAnalyzed(optional_field_ty_decl_index);
  17231                 const optional_field_ty_decl = mod.declPtr(optional_field_ty_decl_index);
  17232                 break :t optional_field_ty_decl.val.toType();
  17233             };
  17234 
  17235             const field_values = .{
  17236                 // child: type,
  17237                 ty.optionalChild(mod).toIntern(),
  17238             };
  17239             return Air.internedToRef((try mod.intern(.{ .un = .{
  17240                 .ty = type_info_ty.toIntern(),
  17241                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Optional))).toIntern(),
  17242                 .val = try mod.intern(.{ .aggregate = .{
  17243                     .ty = optional_field_ty.toIntern(),
  17244                     .storage = .{ .elems = &field_values },
  17245                 } }),
  17246             } })));
  17247         },
  17248         .ErrorSet => {
  17249             // Get the Error type
  17250             const error_field_ty = t: {
  17251                 const set_field_ty_decl_index = (try sema.namespaceLookup(
  17252                     block,
  17253                     src,
  17254                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17255                     try ip.getOrPutString(gpa, "Error"),
  17256                 )).?;
  17257                 try sema.ensureDeclAnalyzed(set_field_ty_decl_index);
  17258                 const set_field_ty_decl = mod.declPtr(set_field_ty_decl_index);
  17259                 break :t set_field_ty_decl.val.toType();
  17260             };
  17261 
  17262             try sema.queueFullTypeResolution(error_field_ty);
  17263 
  17264             // Build our list of Error values
  17265             // Optional value is only null if anyerror
  17266             // Value can be zero-length slice otherwise
  17267             const error_field_vals = switch (try sema.resolveInferredErrorSetTy(block, src, ty.toIntern())) {
  17268                 .anyerror_type => null,
  17269                 else => |err_set_ty_index| blk: {
  17270                     const names = ip.indexToKey(err_set_ty_index).error_set_type.names;
  17271                     const vals = try sema.arena.alloc(InternPool.Index, names.len);
  17272                     for (vals, 0..) |*field_val, i| {
  17273                         // TODO: write something like getCoercedInts to avoid needing to dupe
  17274                         const name = try sema.arena.dupe(u8, ip.stringToSlice(names.get(ip)[i]));
  17275                         const name_val = v: {
  17276                             const new_decl_ty = try mod.arrayType(.{
  17277                                 .len = name.len,
  17278                                 .child = .u8_type,
  17279                             });
  17280                             const new_decl_val = try mod.intern(.{ .aggregate = .{
  17281                                 .ty = new_decl_ty.toIntern(),
  17282                                 .storage = .{ .bytes = name },
  17283                             } });
  17284                             break :v try mod.intern(.{ .ptr = .{
  17285                                 .ty = .slice_const_u8_type,
  17286                                 .addr = .{ .anon_decl = .{
  17287                                     .val = new_decl_val,
  17288                                     .orig_ty = .slice_const_u8_type,
  17289                                 } },
  17290                                 .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  17291                             } });
  17292                         };
  17293 
  17294                         const error_field_fields = .{
  17295                             // name: []const u8,
  17296                             name_val,
  17297                         };
  17298                         field_val.* = try mod.intern(.{ .aggregate = .{
  17299                             .ty = error_field_ty.toIntern(),
  17300                             .storage = .{ .elems = &error_field_fields },
  17301                         } });
  17302                     }
  17303 
  17304                     break :blk vals;
  17305                 },
  17306             };
  17307 
  17308             // Build our ?[]const Error value
  17309             const slice_errors_ty = try sema.ptrType(.{
  17310                 .child = error_field_ty.toIntern(),
  17311                 .flags = .{
  17312                     .size = .Slice,
  17313                     .is_const = true,
  17314                 },
  17315             });
  17316             const opt_slice_errors_ty = try mod.optionalType(slice_errors_ty.toIntern());
  17317             const errors_payload_val: InternPool.Index = if (error_field_vals) |vals| v: {
  17318                 const array_errors_ty = try mod.arrayType(.{
  17319                     .len = vals.len,
  17320                     .child = error_field_ty.toIntern(),
  17321                 });
  17322                 const new_decl_val = try mod.intern(.{ .aggregate = .{
  17323                     .ty = array_errors_ty.toIntern(),
  17324                     .storage = .{ .elems = vals },
  17325                 } });
  17326                 break :v try mod.intern(.{ .ptr = .{
  17327                     .ty = slice_errors_ty.toIntern(),
  17328                     .addr = .{ .anon_decl = .{
  17329                         .orig_ty = slice_errors_ty.toIntern(),
  17330                         .val = new_decl_val,
  17331                     } },
  17332                     .len = (try mod.intValue(Type.usize, vals.len)).toIntern(),
  17333                 } });
  17334             } else .none;
  17335             const errors_val = try mod.intern(.{ .opt = .{
  17336                 .ty = opt_slice_errors_ty.toIntern(),
  17337                 .val = errors_payload_val,
  17338             } });
  17339 
  17340             // Construct Type{ .ErrorSet = errors_val }
  17341             return Air.internedToRef((try mod.intern(.{ .un = .{
  17342                 .ty = type_info_ty.toIntern(),
  17343                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.ErrorSet))).toIntern(),
  17344                 .val = errors_val,
  17345             } })));
  17346         },
  17347         .ErrorUnion => {
  17348             const error_union_field_ty = t: {
  17349                 const error_union_field_ty_decl_index = (try sema.namespaceLookup(
  17350                     block,
  17351                     src,
  17352                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17353                     try ip.getOrPutString(gpa, "ErrorUnion"),
  17354                 )).?;
  17355                 try sema.ensureDeclAnalyzed(error_union_field_ty_decl_index);
  17356                 const error_union_field_ty_decl = mod.declPtr(error_union_field_ty_decl_index);
  17357                 break :t error_union_field_ty_decl.val.toType();
  17358             };
  17359 
  17360             const field_values = .{
  17361                 // error_set: type,
  17362                 ty.errorUnionSet(mod).toIntern(),
  17363                 // payload: type,
  17364                 ty.errorUnionPayload(mod).toIntern(),
  17365             };
  17366             return Air.internedToRef((try mod.intern(.{ .un = .{
  17367                 .ty = type_info_ty.toIntern(),
  17368                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.ErrorUnion))).toIntern(),
  17369                 .val = try mod.intern(.{ .aggregate = .{
  17370                     .ty = error_union_field_ty.toIntern(),
  17371                     .storage = .{ .elems = &field_values },
  17372                 } }),
  17373             } })));
  17374         },
  17375         .Enum => {
  17376             const is_exhaustive = Value.makeBool(ip.indexToKey(ty.toIntern()).enum_type.tag_mode != .nonexhaustive);
  17377 
  17378             const enum_field_ty = t: {
  17379                 const enum_field_ty_decl_index = (try sema.namespaceLookup(
  17380                     block,
  17381                     src,
  17382                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17383                     try ip.getOrPutString(gpa, "EnumField"),
  17384                 )).?;
  17385                 try sema.ensureDeclAnalyzed(enum_field_ty_decl_index);
  17386                 const enum_field_ty_decl = mod.declPtr(enum_field_ty_decl_index);
  17387                 break :t enum_field_ty_decl.val.toType();
  17388             };
  17389 
  17390             const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.indexToKey(ty.toIntern()).enum_type.names.len);
  17391             for (enum_field_vals, 0..) |*field_val, i| {
  17392                 const enum_type = ip.indexToKey(ty.toIntern()).enum_type;
  17393                 const value_val = if (enum_type.values.len > 0)
  17394                     try mod.intern_pool.getCoercedInts(
  17395                         mod.gpa,
  17396                         mod.intern_pool.indexToKey(enum_type.values.get(ip)[i]).int,
  17397                         .comptime_int_type,
  17398                     )
  17399                 else
  17400                     (try mod.intValue(Type.comptime_int, i)).toIntern();
  17401                 // TODO: write something like getCoercedInts to avoid needing to dupe
  17402                 const name = try sema.arena.dupe(u8, ip.stringToSlice(enum_type.names.get(ip)[i]));
  17403                 const name_val = v: {
  17404                     const new_decl_ty = try mod.arrayType(.{
  17405                         .len = name.len,
  17406                         .child = .u8_type,
  17407                     });
  17408                     const new_decl_val = try mod.intern(.{ .aggregate = .{
  17409                         .ty = new_decl_ty.toIntern(),
  17410                         .storage = .{ .bytes = name },
  17411                     } });
  17412                     break :v try mod.intern(.{ .ptr = .{
  17413                         .ty = .slice_const_u8_type,
  17414                         .addr = .{ .anon_decl = .{
  17415                             .val = new_decl_val,
  17416                             .orig_ty = .slice_const_u8_type,
  17417                         } },
  17418                         .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  17419                     } });
  17420                 };
  17421 
  17422                 const enum_field_fields = .{
  17423                     // name: []const u8,
  17424                     name_val,
  17425                     // value: comptime_int,
  17426                     value_val,
  17427                 };
  17428                 field_val.* = try mod.intern(.{ .aggregate = .{
  17429                     .ty = enum_field_ty.toIntern(),
  17430                     .storage = .{ .elems = &enum_field_fields },
  17431                 } });
  17432             }
  17433 
  17434             const fields_val = v: {
  17435                 const fields_array_ty = try mod.arrayType(.{
  17436                     .len = enum_field_vals.len,
  17437                     .child = enum_field_ty.toIntern(),
  17438                 });
  17439                 const new_decl_val = try mod.intern(.{ .aggregate = .{
  17440                     .ty = fields_array_ty.toIntern(),
  17441                     .storage = .{ .elems = enum_field_vals },
  17442                 } });
  17443                 const ptr_ty = (try sema.ptrType(.{
  17444                     .child = enum_field_ty.toIntern(),
  17445                     .flags = .{
  17446                         .size = .Slice,
  17447                         .is_const = true,
  17448                     },
  17449                 })).toIntern();
  17450                 break :v try mod.intern(.{ .ptr = .{
  17451                     .ty = ptr_ty,
  17452                     .addr = .{ .anon_decl = .{
  17453                         .val = new_decl_val,
  17454                         .orig_ty = ptr_ty,
  17455                     } },
  17456                     .len = (try mod.intValue(Type.usize, enum_field_vals.len)).toIntern(),
  17457                 } });
  17458             };
  17459 
  17460             const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ip.indexToKey(ty.toIntern()).enum_type.namespace);
  17461 
  17462             const type_enum_ty = t: {
  17463                 const type_enum_ty_decl_index = (try sema.namespaceLookup(
  17464                     block,
  17465                     src,
  17466                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17467                     try ip.getOrPutString(gpa, "Enum"),
  17468                 )).?;
  17469                 try sema.ensureDeclAnalyzed(type_enum_ty_decl_index);
  17470                 const type_enum_ty_decl = mod.declPtr(type_enum_ty_decl_index);
  17471                 break :t type_enum_ty_decl.val.toType();
  17472             };
  17473 
  17474             const field_values = .{
  17475                 // tag_type: type,
  17476                 ip.indexToKey(ty.toIntern()).enum_type.tag_ty,
  17477                 // fields: []const EnumField,
  17478                 fields_val,
  17479                 // decls: []const Declaration,
  17480                 decls_val,
  17481                 // is_exhaustive: bool,
  17482                 is_exhaustive.toIntern(),
  17483             };
  17484             return Air.internedToRef((try mod.intern(.{ .un = .{
  17485                 .ty = type_info_ty.toIntern(),
  17486                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Enum))).toIntern(),
  17487                 .val = try mod.intern(.{ .aggregate = .{
  17488                     .ty = type_enum_ty.toIntern(),
  17489                     .storage = .{ .elems = &field_values },
  17490                 } }),
  17491             } })));
  17492         },
  17493         .Union => {
  17494             const type_union_ty = t: {
  17495                 const type_union_ty_decl_index = (try sema.namespaceLookup(
  17496                     block,
  17497                     src,
  17498                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17499                     try ip.getOrPutString(gpa, "Union"),
  17500                 )).?;
  17501                 try sema.ensureDeclAnalyzed(type_union_ty_decl_index);
  17502                 const type_union_ty_decl = mod.declPtr(type_union_ty_decl_index);
  17503                 break :t type_union_ty_decl.val.toType();
  17504             };
  17505 
  17506             const union_field_ty = t: {
  17507                 const union_field_ty_decl_index = (try sema.namespaceLookup(
  17508                     block,
  17509                     src,
  17510                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17511                     try ip.getOrPutString(gpa, "UnionField"),
  17512                 )).?;
  17513                 try sema.ensureDeclAnalyzed(union_field_ty_decl_index);
  17514                 const union_field_ty_decl = mod.declPtr(union_field_ty_decl_index);
  17515                 break :t union_field_ty_decl.val.toType();
  17516             };
  17517 
  17518             try sema.resolveTypeLayout(ty); // Getting alignment requires type layout
  17519             const union_obj = mod.typeToUnion(ty).?;
  17520             const layout = union_obj.getLayout(ip);
  17521 
  17522             const union_field_vals = try gpa.alloc(InternPool.Index, union_obj.field_names.len);
  17523             defer gpa.free(union_field_vals);
  17524 
  17525             for (union_field_vals, 0..) |*field_val, i| {
  17526                 // TODO: write something like getCoercedInts to avoid needing to dupe
  17527                 const name = try sema.arena.dupe(u8, ip.stringToSlice(union_obj.field_names.get(ip)[i]));
  17528                 const name_val = v: {
  17529                     const new_decl_ty = try mod.arrayType(.{
  17530                         .len = name.len,
  17531                         .child = .u8_type,
  17532                     });
  17533                     const new_decl_val = try mod.intern(.{ .aggregate = .{
  17534                         .ty = new_decl_ty.toIntern(),
  17535                         .storage = .{ .bytes = name },
  17536                     } });
  17537                     break :v try mod.intern(.{ .ptr = .{
  17538                         .ty = .slice_const_u8_type,
  17539                         .addr = .{ .anon_decl = .{
  17540                             .val = new_decl_val,
  17541                             .orig_ty = .slice_const_u8_type,
  17542                         } },
  17543                         .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  17544                     } });
  17545                 };
  17546 
  17547                 const alignment = switch (layout) {
  17548                     .Auto, .Extern => try sema.unionFieldAlignment(union_obj, @intCast(i)),
  17549                     .Packed => .none,
  17550                 };
  17551 
  17552                 const field_ty = union_obj.field_types.get(ip)[i];
  17553                 const union_field_fields = .{
  17554                     // name: []const u8,
  17555                     name_val,
  17556                     // type: type,
  17557                     field_ty,
  17558                     // alignment: comptime_int,
  17559                     (try mod.intValue(Type.comptime_int, alignment.toByteUnits(0))).toIntern(),
  17560                 };
  17561                 field_val.* = try mod.intern(.{ .aggregate = .{
  17562                     .ty = union_field_ty.toIntern(),
  17563                     .storage = .{ .elems = &union_field_fields },
  17564                 } });
  17565             }
  17566 
  17567             const fields_val = v: {
  17568                 const array_fields_ty = try mod.arrayType(.{
  17569                     .len = union_field_vals.len,
  17570                     .child = union_field_ty.toIntern(),
  17571                 });
  17572                 const new_decl_val = try mod.intern(.{ .aggregate = .{
  17573                     .ty = array_fields_ty.toIntern(),
  17574                     .storage = .{ .elems = union_field_vals },
  17575                 } });
  17576                 const ptr_ty = (try sema.ptrType(.{
  17577                     .child = union_field_ty.toIntern(),
  17578                     .flags = .{
  17579                         .size = .Slice,
  17580                         .is_const = true,
  17581                     },
  17582                 })).toIntern();
  17583                 break :v try mod.intern(.{ .ptr = .{
  17584                     .ty = ptr_ty,
  17585                     .addr = .{ .anon_decl = .{
  17586                         .orig_ty = ptr_ty,
  17587                         .val = new_decl_val,
  17588                     } },
  17589                     .len = (try mod.intValue(Type.usize, union_field_vals.len)).toIntern(),
  17590                 } });
  17591             };
  17592 
  17593             const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespaceIndex(mod));
  17594 
  17595             const enum_tag_ty_val = try mod.intern(.{ .opt = .{
  17596                 .ty = (try mod.optionalType(.type_type)).toIntern(),
  17597                 .val = if (ty.unionTagType(mod)) |tag_ty| tag_ty.toIntern() else .none,
  17598             } });
  17599 
  17600             const container_layout_ty = t: {
  17601                 const decl_index = (try sema.namespaceLookup(
  17602                     block,
  17603                     src,
  17604                     (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?,
  17605                     try ip.getOrPutString(gpa, "ContainerLayout"),
  17606                 )).?;
  17607                 try sema.ensureDeclAnalyzed(decl_index);
  17608                 const decl = mod.declPtr(decl_index);
  17609                 break :t decl.val.toType();
  17610             };
  17611 
  17612             const field_values = .{
  17613                 // layout: ContainerLayout,
  17614                 (try mod.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(),
  17615 
  17616                 // tag_type: ?type,
  17617                 enum_tag_ty_val,
  17618                 // fields: []const UnionField,
  17619                 fields_val,
  17620                 // decls: []const Declaration,
  17621                 decls_val,
  17622             };
  17623             return Air.internedToRef((try mod.intern(.{ .un = .{
  17624                 .ty = type_info_ty.toIntern(),
  17625                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Union))).toIntern(),
  17626                 .val = try mod.intern(.{ .aggregate = .{
  17627                     .ty = type_union_ty.toIntern(),
  17628                     .storage = .{ .elems = &field_values },
  17629                 } }),
  17630             } })));
  17631         },
  17632         .Struct => {
  17633             const type_struct_ty = t: {
  17634                 const type_struct_ty_decl_index = (try sema.namespaceLookup(
  17635                     block,
  17636                     src,
  17637                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17638                     try ip.getOrPutString(gpa, "Struct"),
  17639                 )).?;
  17640                 try sema.ensureDeclAnalyzed(type_struct_ty_decl_index);
  17641                 const type_struct_ty_decl = mod.declPtr(type_struct_ty_decl_index);
  17642                 break :t type_struct_ty_decl.val.toType();
  17643             };
  17644 
  17645             const struct_field_ty = t: {
  17646                 const struct_field_ty_decl_index = (try sema.namespaceLookup(
  17647                     block,
  17648                     src,
  17649                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17650                     try ip.getOrPutString(gpa, "StructField"),
  17651                 )).?;
  17652                 try sema.ensureDeclAnalyzed(struct_field_ty_decl_index);
  17653                 const struct_field_ty_decl = mod.declPtr(struct_field_ty_decl_index);
  17654                 break :t struct_field_ty_decl.val.toType();
  17655             };
  17656 
  17657             try sema.resolveTypeLayout(ty); // Getting alignment requires type layout
  17658 
  17659             var struct_field_vals: []InternPool.Index = &.{};
  17660             defer gpa.free(struct_field_vals);
  17661             fv: {
  17662                 const struct_type = switch (ip.indexToKey(ty.toIntern())) {
  17663                     .anon_struct_type => |tuple| {
  17664                         struct_field_vals = try gpa.alloc(InternPool.Index, tuple.types.len);
  17665                         for (struct_field_vals, 0..) |*struct_field_val, i| {
  17666                             const anon_struct_type = ip.indexToKey(ty.toIntern()).anon_struct_type;
  17667                             const field_ty = anon_struct_type.types.get(ip)[i];
  17668                             const field_val = anon_struct_type.values.get(ip)[i];
  17669                             const name_val = v: {
  17670                                 // TODO: write something like getCoercedInts to avoid needing to dupe
  17671                                 const bytes = if (tuple.names.len != 0)
  17672                                     // https://github.com/ziglang/zig/issues/15709
  17673                                     try sema.arena.dupe(u8, ip.stringToSlice(ip.indexToKey(ty.toIntern()).anon_struct_type.names.get(ip)[i]))
  17674                                 else
  17675                                     try std.fmt.allocPrint(sema.arena, "{d}", .{i});
  17676                                 const new_decl_ty = try mod.arrayType(.{
  17677                                     .len = bytes.len,
  17678                                     .child = .u8_type,
  17679                                 });
  17680                                 const new_decl_val = try mod.intern(.{ .aggregate = .{
  17681                                     .ty = new_decl_ty.toIntern(),
  17682                                     .storage = .{ .bytes = bytes },
  17683                                 } });
  17684                                 break :v try mod.intern(.{ .ptr = .{
  17685                                     .ty = .slice_const_u8_type,
  17686                                     .addr = .{ .anon_decl = .{
  17687                                         .val = new_decl_val,
  17688                                         .orig_ty = .slice_const_u8_type,
  17689                                     } },
  17690                                     .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(),
  17691                                 } });
  17692                             };
  17693 
  17694                             try sema.resolveTypeLayout(Type.fromInterned(field_ty));
  17695 
  17696                             const is_comptime = field_val != .none;
  17697                             const opt_default_val = if (is_comptime) Value.fromInterned(field_val) else null;
  17698                             const default_val_ptr = try sema.optRefValue(opt_default_val);
  17699                             const struct_field_fields = .{
  17700                                 // name: []const u8,
  17701                                 name_val,
  17702                                 // type: type,
  17703                                 field_ty,
  17704                                 // default_value: ?*const anyopaque,
  17705                                 default_val_ptr.toIntern(),
  17706                                 // is_comptime: bool,
  17707                                 Value.makeBool(is_comptime).toIntern(),
  17708                                 // alignment: comptime_int,
  17709                                 (try mod.intValue(Type.comptime_int, Type.fromInterned(field_ty).abiAlignment(mod).toByteUnits(0))).toIntern(),
  17710                             };
  17711                             struct_field_val.* = try mod.intern(.{ .aggregate = .{
  17712                                 .ty = struct_field_ty.toIntern(),
  17713                                 .storage = .{ .elems = &struct_field_fields },
  17714                             } });
  17715                         }
  17716                         break :fv;
  17717                     },
  17718                     .struct_type => |s| s,
  17719                     else => unreachable,
  17720                 };
  17721                 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);
  17722 
  17723                 try sema.resolveStructFieldInits(ty);
  17724 
  17725                 for (struct_field_vals, 0..) |*field_val, i| {
  17726                     // TODO: write something like getCoercedInts to avoid needing to dupe
  17727                     const name = if (struct_type.fieldName(ip, i).unwrap()) |name_nts|
  17728                         try sema.arena.dupe(u8, ip.stringToSlice(name_nts))
  17729                     else
  17730                         try std.fmt.allocPrintZ(sema.arena, "{d}", .{i});
  17731                     const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  17732                     const field_init = struct_type.fieldInit(ip, i);
  17733                     const field_is_comptime = struct_type.fieldIsComptime(ip, i);
  17734                     const name_val = v: {
  17735                         const new_decl_ty = try mod.arrayType(.{
  17736                             .len = name.len,
  17737                             .child = .u8_type,
  17738                         });
  17739                         const new_decl_val = try mod.intern(.{ .aggregate = .{
  17740                             .ty = new_decl_ty.toIntern(),
  17741                             .storage = .{ .bytes = name },
  17742                         } });
  17743                         break :v try mod.intern(.{ .ptr = .{
  17744                             .ty = .slice_const_u8_type,
  17745                             .addr = .{ .anon_decl = .{
  17746                                 .val = new_decl_val,
  17747                                 .orig_ty = .slice_const_u8_type,
  17748                             } },
  17749                             .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  17750                         } });
  17751                     };
  17752 
  17753                     const opt_default_val = if (field_init == .none) null else Value.fromInterned(field_init);
  17754                     const default_val_ptr = try sema.optRefValue(opt_default_val);
  17755                     const alignment = switch (struct_type.layout) {
  17756                         .Packed => .none,
  17757                         else => try sema.structFieldAlignment(
  17758                             struct_type.fieldAlign(ip, i),
  17759                             field_ty,
  17760                             struct_type.layout,
  17761                         ),
  17762                     };
  17763 
  17764                     const struct_field_fields = .{
  17765                         // name: []const u8,
  17766                         name_val,
  17767                         // type: type,
  17768                         field_ty.toIntern(),
  17769                         // default_value: ?*const anyopaque,
  17770                         default_val_ptr.toIntern(),
  17771                         // is_comptime: bool,
  17772                         Value.makeBool(field_is_comptime).toIntern(),
  17773                         // alignment: comptime_int,
  17774                         (try mod.intValue(Type.comptime_int, alignment.toByteUnits(0))).toIntern(),
  17775                     };
  17776                     field_val.* = try mod.intern(.{ .aggregate = .{
  17777                         .ty = struct_field_ty.toIntern(),
  17778                         .storage = .{ .elems = &struct_field_fields },
  17779                     } });
  17780                 }
  17781             }
  17782 
  17783             const fields_val = v: {
  17784                 const array_fields_ty = try mod.arrayType(.{
  17785                     .len = struct_field_vals.len,
  17786                     .child = struct_field_ty.toIntern(),
  17787                 });
  17788                 const new_decl_val = try mod.intern(.{ .aggregate = .{
  17789                     .ty = array_fields_ty.toIntern(),
  17790                     .storage = .{ .elems = struct_field_vals },
  17791                 } });
  17792                 const ptr_ty = (try sema.ptrType(.{
  17793                     .child = struct_field_ty.toIntern(),
  17794                     .flags = .{
  17795                         .size = .Slice,
  17796                         .is_const = true,
  17797                     },
  17798                 })).toIntern();
  17799                 break :v try mod.intern(.{ .ptr = .{
  17800                     .ty = ptr_ty,
  17801                     .addr = .{ .anon_decl = .{
  17802                         .orig_ty = ptr_ty,
  17803                         .val = new_decl_val,
  17804                     } },
  17805                     .len = (try mod.intValue(Type.usize, struct_field_vals.len)).toIntern(),
  17806                 } });
  17807             };
  17808 
  17809             const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespaceIndex(mod));
  17810 
  17811             const backing_integer_val = try mod.intern(.{ .opt = .{
  17812                 .ty = (try mod.optionalType(.type_type)).toIntern(),
  17813                 .val = if (mod.typeToPackedStruct(ty)) |packed_struct| val: {
  17814                     assert(Type.fromInterned(packed_struct.backingIntType(ip).*).isInt(mod));
  17815                     break :val packed_struct.backingIntType(ip).*;
  17816                 } else .none,
  17817             } });
  17818 
  17819             const container_layout_ty = t: {
  17820                 const decl_index = (try sema.namespaceLookup(
  17821                     block,
  17822                     src,
  17823                     (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?,
  17824                     try ip.getOrPutString(gpa, "ContainerLayout"),
  17825                 )).?;
  17826                 try sema.ensureDeclAnalyzed(decl_index);
  17827                 const decl = mod.declPtr(decl_index);
  17828                 break :t decl.val.toType();
  17829             };
  17830 
  17831             const layout = ty.containerLayout(mod);
  17832 
  17833             const field_values = [_]InternPool.Index{
  17834                 // layout: ContainerLayout,
  17835                 (try mod.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(),
  17836                 // backing_integer: ?type,
  17837                 backing_integer_val,
  17838                 // fields: []const StructField,
  17839                 fields_val,
  17840                 // decls: []const Declaration,
  17841                 decls_val,
  17842                 // is_tuple: bool,
  17843                 Value.makeBool(ty.isTuple(mod)).toIntern(),
  17844             };
  17845             return Air.internedToRef((try mod.intern(.{ .un = .{
  17846                 .ty = type_info_ty.toIntern(),
  17847                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Struct))).toIntern(),
  17848                 .val = try mod.intern(.{ .aggregate = .{
  17849                     .ty = type_struct_ty.toIntern(),
  17850                     .storage = .{ .elems = &field_values },
  17851                 } }),
  17852             } })));
  17853         },
  17854         .Opaque => {
  17855             const type_opaque_ty = t: {
  17856                 const type_opaque_ty_decl_index = (try sema.namespaceLookup(
  17857                     block,
  17858                     src,
  17859                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17860                     try ip.getOrPutString(gpa, "Opaque"),
  17861                 )).?;
  17862                 try sema.ensureDeclAnalyzed(type_opaque_ty_decl_index);
  17863                 const type_opaque_ty_decl = mod.declPtr(type_opaque_ty_decl_index);
  17864                 break :t type_opaque_ty_decl.val.toType();
  17865             };
  17866 
  17867             try sema.resolveTypeFields(ty);
  17868             const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespaceIndex(mod));
  17869 
  17870             const field_values = .{
  17871                 // decls: []const Declaration,
  17872                 decls_val,
  17873             };
  17874             return Air.internedToRef((try mod.intern(.{ .un = .{
  17875                 .ty = type_info_ty.toIntern(),
  17876                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Opaque))).toIntern(),
  17877                 .val = try mod.intern(.{ .aggregate = .{
  17878                     .ty = type_opaque_ty.toIntern(),
  17879                     .storage = .{ .elems = &field_values },
  17880                 } }),
  17881             } })));
  17882         },
  17883         .Frame => return sema.failWithUseOfAsync(block, src),
  17884         .AnyFrame => return sema.failWithUseOfAsync(block, src),
  17885     }
  17886 }
  17887 
  17888 fn typeInfoDecls(
  17889     sema: *Sema,
  17890     block: *Block,
  17891     src: LazySrcLoc,
  17892     type_info_ty: Type,
  17893     opt_namespace: InternPool.OptionalNamespaceIndex,
  17894 ) CompileError!InternPool.Index {
  17895     const mod = sema.mod;
  17896     const gpa = sema.gpa;
  17897 
  17898     const declaration_ty = t: {
  17899         const declaration_ty_decl_index = (try sema.namespaceLookup(
  17900             block,
  17901             src,
  17902             type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17903             try mod.intern_pool.getOrPutString(gpa, "Declaration"),
  17904         )).?;
  17905         try sema.ensureDeclAnalyzed(declaration_ty_decl_index);
  17906         const declaration_ty_decl = mod.declPtr(declaration_ty_decl_index);
  17907         break :t declaration_ty_decl.val.toType();
  17908     };
  17909     try sema.queueFullTypeResolution(declaration_ty);
  17910 
  17911     var decl_vals = std.ArrayList(InternPool.Index).init(gpa);
  17912     defer decl_vals.deinit();
  17913 
  17914     var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa);
  17915     defer seen_namespaces.deinit();
  17916 
  17917     if (opt_namespace.unwrap()) |namespace_index| {
  17918         const namespace = mod.namespacePtr(namespace_index);
  17919         try sema.typeInfoNamespaceDecls(block, namespace, declaration_ty, &decl_vals, &seen_namespaces);
  17920     }
  17921 
  17922     const array_decl_ty = try mod.arrayType(.{
  17923         .len = decl_vals.items.len,
  17924         .child = declaration_ty.toIntern(),
  17925     });
  17926     const new_decl_val = try mod.intern(.{ .aggregate = .{
  17927         .ty = array_decl_ty.toIntern(),
  17928         .storage = .{ .elems = decl_vals.items },
  17929     } });
  17930     const ptr_ty = (try sema.ptrType(.{
  17931         .child = declaration_ty.toIntern(),
  17932         .flags = .{
  17933             .size = .Slice,
  17934             .is_const = true,
  17935         },
  17936     })).toIntern();
  17937     return try mod.intern(.{ .ptr = .{
  17938         .ty = ptr_ty,
  17939         .addr = .{ .anon_decl = .{
  17940             .orig_ty = ptr_ty,
  17941             .val = new_decl_val,
  17942         } },
  17943         .len = (try mod.intValue(Type.usize, decl_vals.items.len)).toIntern(),
  17944     } });
  17945 }
  17946 
  17947 fn typeInfoNamespaceDecls(
  17948     sema: *Sema,
  17949     block: *Block,
  17950     namespace: *Namespace,
  17951     declaration_ty: Type,
  17952     decl_vals: *std.ArrayList(InternPool.Index),
  17953     seen_namespaces: *std.AutoHashMap(*Namespace, void),
  17954 ) !void {
  17955     const mod = sema.mod;
  17956     const ip = &mod.intern_pool;
  17957     const gop = try seen_namespaces.getOrPut(namespace);
  17958     if (gop.found_existing) return;
  17959     const decls = namespace.decls.keys();
  17960     for (decls) |decl_index| {
  17961         const decl = mod.declPtr(decl_index);
  17962         if (decl.kind == .@"usingnamespace") {
  17963             if (decl.analysis == .in_progress) continue;
  17964             try mod.ensureDeclAnalyzed(decl_index);
  17965             const new_ns = decl.val.toType().getNamespace(mod).?;
  17966             try sema.typeInfoNamespaceDecls(block, new_ns, declaration_ty, decl_vals, seen_namespaces);
  17967             continue;
  17968         }
  17969         if (decl.kind != .named or !decl.is_pub) continue;
  17970         const name_val = v: {
  17971             // TODO: write something like getCoercedInts to avoid needing to dupe
  17972             const name = try sema.arena.dupe(u8, ip.stringToSlice(decl.name));
  17973             const new_decl_ty = try mod.arrayType(.{
  17974                 .len = name.len,
  17975                 .child = .u8_type,
  17976             });
  17977             const new_decl_val = try mod.intern(.{ .aggregate = .{
  17978                 .ty = new_decl_ty.toIntern(),
  17979                 .storage = .{ .bytes = name },
  17980             } });
  17981             break :v try mod.intern(.{ .ptr = .{
  17982                 .ty = .slice_const_u8_type,
  17983                 .addr = .{ .anon_decl = .{
  17984                     .orig_ty = .slice_const_u8_type,
  17985                     .val = new_decl_val,
  17986                 } },
  17987                 .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  17988             } });
  17989         };
  17990 
  17991         const fields = .{
  17992             //name: []const u8,
  17993             name_val,
  17994         };
  17995         try decl_vals.append(try mod.intern(.{ .aggregate = .{
  17996             .ty = declaration_ty.toIntern(),
  17997             .storage = .{ .elems = &fields },
  17998         } }));
  17999     }
  18000 }
  18001 
  18002 fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18003     _ = block;
  18004     const zir_datas = sema.code.instructions.items(.data);
  18005     const inst_data = zir_datas[@intFromEnum(inst)].un_node;
  18006     const operand = try sema.resolveInst(inst_data.operand);
  18007     const operand_ty = sema.typeOf(operand);
  18008     return Air.internedToRef(operand_ty.toIntern());
  18009 }
  18010 
  18011 fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18012     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  18013     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
  18014     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  18015 
  18016     var child_block: Block = .{
  18017         .parent = block,
  18018         .sema = sema,
  18019         .src_decl = block.src_decl,
  18020         .namespace = block.namespace,
  18021         .wip_capture_scope = block.wip_capture_scope,
  18022         .instructions = .{},
  18023         .inlining = block.inlining,
  18024         .is_comptime = false,
  18025         .is_typeof = true,
  18026         .want_safety = false,
  18027         .error_return_trace_index = block.error_return_trace_index,
  18028     };
  18029     defer child_block.instructions.deinit(sema.gpa);
  18030 
  18031     const operand = try sema.resolveBody(&child_block, body, inst);
  18032     const operand_ty = sema.typeOf(operand);
  18033     if (operand_ty.isGenericPoison()) return error.GenericPoison;
  18034     return Air.internedToRef(operand_ty.toIntern());
  18035 }
  18036 
  18037 fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18038     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18039     const src = inst_data.src();
  18040     const operand = try sema.resolveInst(inst_data.operand);
  18041     const operand_ty = sema.typeOf(operand);
  18042     const res_ty = try sema.log2IntType(block, operand_ty, src);
  18043     return Air.internedToRef(res_ty.toIntern());
  18044 }
  18045 
  18046 fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type {
  18047     const mod = sema.mod;
  18048     switch (operand.zigTypeTag(mod)) {
  18049         .ComptimeInt => return Type.comptime_int,
  18050         .Int => {
  18051             const bits = operand.bitSize(mod);
  18052             const count = if (bits == 0)
  18053                 0
  18054             else blk: {
  18055                 var count: u16 = 0;
  18056                 var s = bits - 1;
  18057                 while (s != 0) : (s >>= 1) {
  18058                     count += 1;
  18059                 }
  18060                 break :blk count;
  18061             };
  18062             return mod.intType(.unsigned, count);
  18063         },
  18064         .Vector => {
  18065             const elem_ty = operand.elemType2(mod);
  18066             const log2_elem_ty = try sema.log2IntType(block, elem_ty, src);
  18067             return mod.vectorType(.{
  18068                 .len = operand.vectorLen(mod),
  18069                 .child = log2_elem_ty.toIntern(),
  18070             });
  18071         },
  18072         else => {},
  18073     }
  18074     return sema.fail(
  18075         block,
  18076         src,
  18077         "bit shifting operation expected integer type, found '{}'",
  18078         .{operand.fmt(mod)},
  18079     );
  18080 }
  18081 
  18082 fn zirTypeofPeer(
  18083     sema: *Sema,
  18084     block: *Block,
  18085     extended: Zir.Inst.Extended.InstData,
  18086 ) CompileError!Air.Inst.Ref {
  18087     const tracy = trace(@src());
  18088     defer tracy.end();
  18089 
  18090     const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand);
  18091     const src = LazySrcLoc.nodeOffset(extra.data.src_node);
  18092     const body = sema.code.bodySlice(extra.data.body_index, extra.data.body_len);
  18093 
  18094     var child_block: Block = .{
  18095         .parent = block,
  18096         .sema = sema,
  18097         .src_decl = block.src_decl,
  18098         .namespace = block.namespace,
  18099         .wip_capture_scope = block.wip_capture_scope,
  18100         .instructions = .{},
  18101         .inlining = block.inlining,
  18102         .is_comptime = false,
  18103         .is_typeof = true,
  18104         .runtime_cond = block.runtime_cond,
  18105         .runtime_loop = block.runtime_loop,
  18106         .runtime_index = block.runtime_index,
  18107     };
  18108     defer child_block.instructions.deinit(sema.gpa);
  18109     // Ignore the result, we only care about the instructions in `args`.
  18110     _ = try sema.analyzeBodyBreak(&child_block, body);
  18111 
  18112     const args = sema.code.refSlice(extra.end, extended.small);
  18113 
  18114     const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len);
  18115     defer sema.gpa.free(inst_list);
  18116 
  18117     for (args, 0..) |arg_ref, i| {
  18118         inst_list[i] = try sema.resolveInst(arg_ref);
  18119     }
  18120 
  18121     const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node });
  18122     return Air.internedToRef(result_type.toIntern());
  18123 }
  18124 
  18125 fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18126     const tracy = trace(@src());
  18127     defer tracy.end();
  18128 
  18129     const mod = sema.mod;
  18130     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18131     const src = inst_data.src();
  18132     const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
  18133     const uncasted_operand = try sema.resolveInst(inst_data.operand);
  18134 
  18135     const operand = try sema.coerce(block, Type.bool, uncasted_operand, operand_src);
  18136     if (try sema.resolveValue(operand)) |val| {
  18137         return if (val.isUndef(mod))
  18138             mod.undefRef(Type.bool)
  18139         else if (val.toBool()) .bool_false else .bool_true;
  18140     }
  18141     try sema.requireRuntimeBlock(block, src, null);
  18142     return block.addTyOp(.not, Type.bool, operand);
  18143 }
  18144 
  18145 fn zirBoolBr(
  18146     sema: *Sema,
  18147     parent_block: *Block,
  18148     inst: Zir.Inst.Index,
  18149     is_bool_or: bool,
  18150 ) CompileError!Air.Inst.Ref {
  18151     const tracy = trace(@src());
  18152     defer tracy.end();
  18153 
  18154     const mod = sema.mod;
  18155     const datas = sema.code.instructions.items(.data);
  18156     const inst_data = datas[@intFromEnum(inst)].bool_br;
  18157     const lhs = try sema.resolveInst(inst_data.lhs);
  18158     const lhs_src = sema.src;
  18159     const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
  18160     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  18161     const gpa = sema.gpa;
  18162 
  18163     if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| {
  18164         if (is_bool_or and lhs_val.toBool()) {
  18165             return .bool_true;
  18166         } else if (!is_bool_or and !lhs_val.toBool()) {
  18167             return .bool_false;
  18168         }
  18169         // comptime-known left-hand side. No need for a block here; the result
  18170         // is simply the rhs expression. Here we rely on there only being 1
  18171         // break instruction (`break_inline`).
  18172         return sema.resolveBody(parent_block, body, inst);
  18173     }
  18174 
  18175     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  18176     try sema.air_instructions.append(gpa, .{
  18177         .tag = .block,
  18178         .data = .{ .ty_pl = .{
  18179             .ty = .bool_type,
  18180             .payload = undefined,
  18181         } },
  18182     });
  18183 
  18184     var child_block = parent_block.makeSubBlock();
  18185     child_block.runtime_loop = null;
  18186     child_block.runtime_cond = lhs_src;
  18187     child_block.runtime_index.increment();
  18188     defer child_block.instructions.deinit(gpa);
  18189 
  18190     var then_block = child_block.makeSubBlock();
  18191     defer then_block.instructions.deinit(gpa);
  18192 
  18193     var else_block = child_block.makeSubBlock();
  18194     defer else_block.instructions.deinit(gpa);
  18195 
  18196     const lhs_block = if (is_bool_or) &then_block else &else_block;
  18197     const rhs_block = if (is_bool_or) &else_block else &then_block;
  18198 
  18199     const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false;
  18200     _ = try lhs_block.addBr(block_inst, lhs_result);
  18201 
  18202     const rhs_result = try sema.resolveBody(rhs_block, body, inst);
  18203     if (!sema.typeOf(rhs_result).isNoReturn(mod)) {
  18204         _ = try rhs_block.addBr(block_inst, rhs_result);
  18205     }
  18206 
  18207     const result = sema.finishCondBr(parent_block, &child_block, &then_block, &else_block, lhs, block_inst);
  18208     if (!sema.typeOf(rhs_result).isNoReturn(mod)) {
  18209         if (try sema.resolveDefinedValue(rhs_block, sema.src, rhs_result)) |rhs_val| {
  18210             if (is_bool_or and rhs_val.toBool()) {
  18211                 return .bool_true;
  18212             } else if (!is_bool_or and !rhs_val.toBool()) {
  18213                 return .bool_false;
  18214             }
  18215         }
  18216     }
  18217 
  18218     return result;
  18219 }
  18220 
  18221 fn finishCondBr(
  18222     sema: *Sema,
  18223     parent_block: *Block,
  18224     child_block: *Block,
  18225     then_block: *Block,
  18226     else_block: *Block,
  18227     cond: Air.Inst.Ref,
  18228     block_inst: Air.Inst.Index,
  18229 ) !Air.Inst.Ref {
  18230     const gpa = sema.gpa;
  18231 
  18232     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
  18233         then_block.instructions.items.len + else_block.instructions.items.len +
  18234         @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len + 1);
  18235 
  18236     const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18237         .then_body_len = @intCast(then_block.instructions.items.len),
  18238         .else_body_len = @intCast(else_block.instructions.items.len),
  18239     });
  18240     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
  18241     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
  18242 
  18243     _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
  18244         .operand = cond,
  18245         .payload = cond_br_payload,
  18246     } } });
  18247 
  18248     sema.air_instructions.items(.data)[@intFromEnum(block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(
  18249         Air.Block{ .body_len = @intCast(child_block.instructions.items.len) },
  18250     );
  18251     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
  18252 
  18253     try parent_block.instructions.append(gpa, block_inst);
  18254     return block_inst.toRef();
  18255 }
  18256 
  18257 fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  18258     const mod = sema.mod;
  18259     switch (ty.zigTypeTag(mod)) {
  18260         .Optional, .Null, .Undefined => return,
  18261         .Pointer => if (ty.isPtrLikeOptional(mod)) return,
  18262         else => {},
  18263     }
  18264     return sema.failWithExpectedOptionalType(block, src, ty);
  18265 }
  18266 
  18267 fn zirIsNonNull(
  18268     sema: *Sema,
  18269     block: *Block,
  18270     inst: Zir.Inst.Index,
  18271 ) CompileError!Air.Inst.Ref {
  18272     const tracy = trace(@src());
  18273     defer tracy.end();
  18274 
  18275     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18276     const src = inst_data.src();
  18277     const operand = try sema.resolveInst(inst_data.operand);
  18278     try sema.checkNullableType(block, src, sema.typeOf(operand));
  18279     return sema.analyzeIsNull(block, src, operand, true);
  18280 }
  18281 
  18282 fn zirIsNonNullPtr(
  18283     sema: *Sema,
  18284     block: *Block,
  18285     inst: Zir.Inst.Index,
  18286 ) CompileError!Air.Inst.Ref {
  18287     const tracy = trace(@src());
  18288     defer tracy.end();
  18289 
  18290     const mod = sema.mod;
  18291     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18292     const src = inst_data.src();
  18293     const ptr = try sema.resolveInst(inst_data.operand);
  18294     try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(mod));
  18295     if ((try sema.resolveValue(ptr)) == null) {
  18296         return block.addUnOp(.is_non_null_ptr, ptr);
  18297     }
  18298     const loaded = try sema.analyzeLoad(block, src, ptr, src);
  18299     return sema.analyzeIsNull(block, src, loaded, true);
  18300 }
  18301 
  18302 fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  18303     const mod = sema.mod;
  18304     switch (ty.zigTypeTag(mod)) {
  18305         .ErrorSet, .ErrorUnion, .Undefined => return,
  18306         else => return sema.fail(block, src, "expected error union type, found '{}'", .{
  18307             ty.fmt(mod),
  18308         }),
  18309     }
  18310 }
  18311 
  18312 fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18313     const tracy = trace(@src());
  18314     defer tracy.end();
  18315 
  18316     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18317     const src = inst_data.src();
  18318     const operand = try sema.resolveInst(inst_data.operand);
  18319     try sema.checkErrorType(block, src, sema.typeOf(operand));
  18320     return sema.analyzeIsNonErr(block, src, operand);
  18321 }
  18322 
  18323 fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18324     const tracy = trace(@src());
  18325     defer tracy.end();
  18326 
  18327     const mod = sema.mod;
  18328     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18329     const src = inst_data.src();
  18330     const ptr = try sema.resolveInst(inst_data.operand);
  18331     try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(mod));
  18332     const loaded = try sema.analyzeLoad(block, src, ptr, src);
  18333     return sema.analyzeIsNonErr(block, src, loaded);
  18334 }
  18335 
  18336 fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18337     const tracy = trace(@src());
  18338     defer tracy.end();
  18339 
  18340     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18341     const src = inst_data.src();
  18342     const operand = try sema.resolveInst(inst_data.operand);
  18343     return sema.analyzeIsNonErr(block, src, operand);
  18344 }
  18345 
  18346 fn zirCondbr(
  18347     sema: *Sema,
  18348     parent_block: *Block,
  18349     inst: Zir.Inst.Index,
  18350 ) CompileError!Zir.Inst.Index {
  18351     const tracy = trace(@src());
  18352     defer tracy.end();
  18353 
  18354     const mod = sema.mod;
  18355     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  18356     const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
  18357     const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
  18358 
  18359     const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
  18360     const else_body = sema.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
  18361 
  18362     const uncasted_cond = try sema.resolveInst(extra.data.condition);
  18363     const cond = try sema.coerce(parent_block, Type.bool, uncasted_cond, cond_src);
  18364 
  18365     if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| {
  18366         const body = if (cond_val.toBool()) then_body else else_body;
  18367 
  18368         try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src);
  18369         // We use `analyzeBodyInner` since we want to propagate any possible
  18370         // `error.ComptimeBreak` to the caller.
  18371         return sema.analyzeBodyInner(parent_block, body);
  18372     }
  18373 
  18374     const gpa = sema.gpa;
  18375 
  18376     // We'll re-use the sub block to save on memory bandwidth, and yank out the
  18377     // instructions array in between using it for the then block and else block.
  18378     var sub_block = parent_block.makeSubBlock();
  18379     sub_block.runtime_loop = null;
  18380     sub_block.runtime_cond = cond_src;
  18381     sub_block.runtime_index.increment();
  18382     defer sub_block.instructions.deinit(gpa);
  18383 
  18384     try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
  18385     const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
  18386     defer gpa.free(true_instructions);
  18387 
  18388     const err_cond = blk: {
  18389         const index = extra.data.condition.toIndex() orelse break :blk null;
  18390         if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) break :blk null;
  18391 
  18392         const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node;
  18393         const err_operand = try sema.resolveInst(err_inst_data.operand);
  18394         const operand_ty = sema.typeOf(err_operand);
  18395         assert(operand_ty.zigTypeTag(mod) == .ErrorUnion);
  18396         const result_ty = operand_ty.errorUnionSet(mod);
  18397         break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand);
  18398     };
  18399 
  18400     if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src)) {
  18401         // nothing to do
  18402     } else {
  18403         try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
  18404     }
  18405     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
  18406         true_instructions.len + sub_block.instructions.items.len);
  18407     _ = try parent_block.addInst(.{
  18408         .tag = .cond_br,
  18409         .data = .{ .pl_op = .{
  18410             .operand = cond,
  18411             .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18412                 .then_body_len = @intCast(true_instructions.len),
  18413                 .else_body_len = @intCast(sub_block.instructions.items.len),
  18414             }),
  18415         } },
  18416     });
  18417     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
  18418     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
  18419     return always_noreturn;
  18420 }
  18421 
  18422 fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18423     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  18424     const src = inst_data.src();
  18425     const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  18426     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
  18427     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  18428     const err_union = try sema.resolveInst(extra.data.operand);
  18429     const err_union_ty = sema.typeOf(err_union);
  18430     const mod = sema.mod;
  18431     if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
  18432         return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
  18433             err_union_ty.fmt(mod),
  18434         });
  18435     }
  18436     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
  18437     if (is_non_err != .none) {
  18438         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
  18439         if (is_non_err_val.toBool()) {
  18440             return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
  18441         }
  18442         // We can analyze the body directly in the parent block because we know there are
  18443         // no breaks from the body possible, and that the body is noreturn.
  18444         return sema.resolveBody(parent_block, body, inst);
  18445     }
  18446 
  18447     var sub_block = parent_block.makeSubBlock();
  18448     defer sub_block.instructions.deinit(sema.gpa);
  18449 
  18450     // This body is guaranteed to end with noreturn and has no breaks.
  18451     _ = try sema.analyzeBodyInner(&sub_block, body);
  18452 
  18453     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len +
  18454         sub_block.instructions.items.len);
  18455     const try_inst = try parent_block.addInst(.{
  18456         .tag = .@"try",
  18457         .data = .{ .pl_op = .{
  18458             .operand = err_union,
  18459             .payload = sema.addExtraAssumeCapacity(Air.Try{
  18460                 .body_len = @intCast(sub_block.instructions.items.len),
  18461             }),
  18462         } },
  18463     });
  18464     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
  18465     return try_inst;
  18466 }
  18467 
  18468 fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18469     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  18470     const src = inst_data.src();
  18471     const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  18472     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
  18473     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  18474     const operand = try sema.resolveInst(extra.data.operand);
  18475     const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src);
  18476     const err_union_ty = sema.typeOf(err_union);
  18477     const mod = sema.mod;
  18478     if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
  18479         return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
  18480             err_union_ty.fmt(mod),
  18481         });
  18482     }
  18483     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
  18484     if (is_non_err != .none) {
  18485         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
  18486         if (is_non_err_val.toBool()) {
  18487             return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
  18488         }
  18489         // We can analyze the body directly in the parent block because we know there are
  18490         // no breaks from the body possible, and that the body is noreturn.
  18491         return sema.resolveBody(parent_block, body, inst);
  18492     }
  18493 
  18494     var sub_block = parent_block.makeSubBlock();
  18495     defer sub_block.instructions.deinit(sema.gpa);
  18496 
  18497     // This body is guaranteed to end with noreturn and has no breaks.
  18498     _ = try sema.analyzeBodyInner(&sub_block, body);
  18499 
  18500     const operand_ty = sema.typeOf(operand);
  18501     const ptr_info = operand_ty.ptrInfo(mod);
  18502     const res_ty = try sema.ptrType(.{
  18503         .child = err_union_ty.errorUnionPayload(mod).toIntern(),
  18504         .flags = .{
  18505             .is_const = ptr_info.flags.is_const,
  18506             .is_volatile = ptr_info.flags.is_volatile,
  18507             .is_allowzero = ptr_info.flags.is_allowzero,
  18508             .address_space = ptr_info.flags.address_space,
  18509         },
  18510     });
  18511     const res_ty_ref = Air.internedToRef(res_ty.toIntern());
  18512     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len +
  18513         sub_block.instructions.items.len);
  18514     const try_inst = try parent_block.addInst(.{
  18515         .tag = .try_ptr,
  18516         .data = .{ .ty_pl = .{
  18517             .ty = res_ty_ref,
  18518             .payload = sema.addExtraAssumeCapacity(Air.TryPtr{
  18519                 .ptr = operand,
  18520                 .body_len = @intCast(sub_block.instructions.items.len),
  18521             }),
  18522         } },
  18523     });
  18524     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
  18525     return try_inst;
  18526 }
  18527 
  18528 // A `break` statement is inside a runtime condition, but trying to
  18529 // break from an inline loop. In such case we must convert it to
  18530 // a runtime break.
  18531 fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !void {
  18532     const gop = sema.inst_map.getOrPutAssumeCapacity(break_data.block_inst);
  18533     const labeled_block = if (!gop.found_existing) blk: {
  18534         try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1);
  18535 
  18536         const new_block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  18537         gop.value_ptr.* = new_block_inst.toRef();
  18538         try sema.air_instructions.append(sema.gpa, .{
  18539             .tag = .block,
  18540             .data = undefined,
  18541         });
  18542         const labeled_block = try sema.gpa.create(LabeledBlock);
  18543         labeled_block.* = .{
  18544             .label = .{
  18545                 .zir_block = break_data.block_inst,
  18546                 .merges = .{
  18547                     .src_locs = .{},
  18548                     .results = .{},
  18549                     .br_list = .{},
  18550                     .block_inst = new_block_inst,
  18551                 },
  18552             },
  18553             .block = .{
  18554                 .parent = child_block,
  18555                 .sema = sema,
  18556                 .src_decl = child_block.src_decl,
  18557                 .namespace = child_block.namespace,
  18558                 .wip_capture_scope = child_block.wip_capture_scope,
  18559                 .instructions = .{},
  18560                 .label = &labeled_block.label,
  18561                 .inlining = child_block.inlining,
  18562                 .is_comptime = child_block.is_comptime,
  18563             },
  18564         };
  18565         sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block);
  18566         break :blk labeled_block;
  18567     } else blk: {
  18568         const new_block_inst = gop.value_ptr.*.toIndex().?;
  18569         const labeled_block = sema.post_hoc_blocks.get(new_block_inst).?;
  18570         break :blk labeled_block;
  18571     };
  18572 
  18573     const operand = try sema.resolveInst(break_data.operand);
  18574     const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand);
  18575     try labeled_block.label.merges.results.append(sema.gpa, operand);
  18576     try labeled_block.label.merges.br_list.append(sema.gpa, br_ref.toIndex().?);
  18577     labeled_block.block.runtime_index.increment();
  18578     if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) {
  18579         labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop;
  18580         labeled_block.block.runtime_loop = child_block.runtime_loop;
  18581     }
  18582 }
  18583 
  18584 fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
  18585     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable";
  18586     const src = inst_data.src();
  18587 
  18588     if (block.is_comptime) {
  18589         return sema.fail(block, src, "reached unreachable code", .{});
  18590     }
  18591     // TODO Add compile error for @optimizeFor occurring too late in a scope.
  18592     block.addUnreachable(src, true) catch |err| switch (err) {
  18593         error.AnalysisFail => {
  18594             const msg = sema.err orelse return err;
  18595             if (!mem.eql(u8, msg.msg, "runtime safety check not allowed in naked function")) return err;
  18596             try sema.errNote(block, src, msg, "the end of a naked function is implicitly unreachable", .{});
  18597             return err;
  18598         },
  18599         else => |e| return e,
  18600     };
  18601     return always_noreturn;
  18602 }
  18603 
  18604 fn zirRetErrValue(
  18605     sema: *Sema,
  18606     block: *Block,
  18607     inst: Zir.Inst.Index,
  18608 ) CompileError!Zir.Inst.Index {
  18609     const mod = sema.mod;
  18610     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
  18611     const err_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
  18612     _ = try mod.getErrorValue(err_name);
  18613     const src = inst_data.src();
  18614     // Return the error code from the function.
  18615     const error_set_type = try mod.singleErrorSetType(err_name);
  18616     const result_inst = Air.internedToRef((try mod.intern(.{ .err = .{
  18617         .ty = error_set_type.toIntern(),
  18618         .name = err_name,
  18619     } })));
  18620     return sema.analyzeRet(block, result_inst, src);
  18621 }
  18622 
  18623 fn zirRetImplicit(
  18624     sema: *Sema,
  18625     block: *Block,
  18626     inst: Zir.Inst.Index,
  18627 ) CompileError!Zir.Inst.Index {
  18628     const tracy = trace(@src());
  18629     defer tracy.end();
  18630 
  18631     const mod = sema.mod;
  18632     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
  18633     const r_brace_src = inst_data.src();
  18634     if (block.inlining == null and sema.func_is_naked) {
  18635         assert(!block.is_comptime);
  18636         if (block.wantSafety()) {
  18637             // Calling a safety function from a naked function would not be legal.
  18638             _ = try block.addNoOp(.trap);
  18639         } else {
  18640             try block.addUnreachable(r_brace_src, false);
  18641         }
  18642         return always_noreturn;
  18643     }
  18644 
  18645     const operand = try sema.resolveInst(inst_data.operand);
  18646     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
  18647     const base_tag = sema.fn_ret_ty.baseZigTypeTag(mod);
  18648     if (base_tag == .NoReturn) {
  18649         const msg = msg: {
  18650             const msg = try sema.errMsg(block, ret_ty_src, "function declared '{}' implicitly returns", .{
  18651                 sema.fn_ret_ty.fmt(mod),
  18652             });
  18653             errdefer msg.destroy(sema.gpa);
  18654             try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{});
  18655             break :msg msg;
  18656         };
  18657         return sema.failWithOwnedErrorMsg(block, msg);
  18658     } else if (base_tag != .Void) {
  18659         const msg = msg: {
  18660             const msg = try sema.errMsg(block, ret_ty_src, "function with non-void return type '{}' implicitly returns", .{
  18661                 sema.fn_ret_ty.fmt(mod),
  18662             });
  18663             errdefer msg.destroy(sema.gpa);
  18664             try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{});
  18665             break :msg msg;
  18666         };
  18667         return sema.failWithOwnedErrorMsg(block, msg);
  18668     }
  18669 
  18670     return sema.analyzeRet(block, operand, r_brace_src);
  18671 }
  18672 
  18673 fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
  18674     const tracy = trace(@src());
  18675     defer tracy.end();
  18676 
  18677     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18678     const operand = try sema.resolveInst(inst_data.operand);
  18679     const src = inst_data.src();
  18680 
  18681     return sema.analyzeRet(block, operand, src);
  18682 }
  18683 
  18684 fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
  18685     const tracy = trace(@src());
  18686     defer tracy.end();
  18687 
  18688     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18689     const src = inst_data.src();
  18690     const ret_ptr = try sema.resolveInst(inst_data.operand);
  18691 
  18692     if (block.is_comptime or block.inlining != null or sema.func_is_naked) {
  18693         const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
  18694         return sema.analyzeRet(block, operand, src);
  18695     }
  18696 
  18697     if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
  18698         const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr);
  18699         return sema.retWithErrTracing(block, src, is_non_err, .ret_load, ret_ptr);
  18700     }
  18701 
  18702     _ = try block.addUnOp(.ret_load, ret_ptr);
  18703     return always_noreturn;
  18704 }
  18705 
  18706 fn retWithErrTracing(
  18707     sema: *Sema,
  18708     block: *Block,
  18709     src: LazySrcLoc,
  18710     is_non_err: Air.Inst.Ref,
  18711     ret_tag: Air.Inst.Tag,
  18712     operand: Air.Inst.Ref,
  18713 ) CompileError!Zir.Inst.Index {
  18714     const mod = sema.mod;
  18715     const need_check = switch (is_non_err) {
  18716         .bool_true => {
  18717             _ = try block.addUnOp(ret_tag, operand);
  18718             return always_noreturn;
  18719         },
  18720         .bool_false => false,
  18721         else => true,
  18722     };
  18723     const gpa = sema.gpa;
  18724     const stack_trace_ty = try sema.getBuiltinType("StackTrace");
  18725     try sema.resolveTypeFields(stack_trace_ty);
  18726     const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty);
  18727     const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
  18728     const return_err_fn = try sema.getBuiltin("returnError");
  18729     const args: [1]Air.Inst.Ref = .{err_return_trace};
  18730 
  18731     if (!need_check) {
  18732         try sema.callBuiltin(block, src, return_err_fn, .never_inline, &args, .@"error return");
  18733         _ = try block.addUnOp(ret_tag, operand);
  18734         return always_noreturn;
  18735     }
  18736 
  18737     var then_block = block.makeSubBlock();
  18738     defer then_block.instructions.deinit(gpa);
  18739     _ = try then_block.addUnOp(ret_tag, operand);
  18740 
  18741     var else_block = block.makeSubBlock();
  18742     defer else_block.instructions.deinit(gpa);
  18743     try sema.callBuiltin(&else_block, src, return_err_fn, .never_inline, &args, .@"error return");
  18744     _ = try else_block.addUnOp(ret_tag, operand);
  18745 
  18746     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
  18747         then_block.instructions.items.len + else_block.instructions.items.len +
  18748         @typeInfo(Air.Block).Struct.fields.len + 1);
  18749 
  18750     const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18751         .then_body_len = @intCast(then_block.instructions.items.len),
  18752         .else_body_len = @intCast(else_block.instructions.items.len),
  18753     });
  18754     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
  18755     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
  18756 
  18757     _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
  18758         .operand = is_non_err,
  18759         .payload = cond_br_payload,
  18760     } } });
  18761 
  18762     return always_noreturn;
  18763 }
  18764 
  18765 fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool {
  18766     const mod = sema.mod;
  18767     return fn_ret_ty.isError(mod) and mod.comp.config.any_error_tracing;
  18768 }
  18769 
  18770 fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18771     const mod = sema.mod;
  18772     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].save_err_ret_index;
  18773 
  18774     if (!block.ownerModule().error_tracing) return;
  18775 
  18776     // This is only relevant at runtime.
  18777     if (block.is_comptime or block.is_typeof) return;
  18778 
  18779     const save_index = inst_data.operand == .none or b: {
  18780         const operand = try sema.resolveInst(inst_data.operand);
  18781         const operand_ty = sema.typeOf(operand);
  18782         break :b operand_ty.isError(mod);
  18783     };
  18784 
  18785     if (save_index)
  18786         block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block);
  18787 }
  18788 
  18789 fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18790     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].restore_err_ret_index;
  18791     const src = sema.src; // TODO
  18792 
  18793     // This is only relevant at runtime.
  18794     if (start_block.is_comptime or start_block.is_typeof) return;
  18795 
  18796     const mod = sema.mod;
  18797     const ip = &mod.intern_pool;
  18798 
  18799     if (!ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn) return;
  18800     if (!start_block.ownerModule().error_tracing) return;
  18801 
  18802     const tracy = trace(@src());
  18803     defer tracy.end();
  18804 
  18805     const saved_index = if (inst_data.block.toIndexAllowNone()) |zir_block| b: {
  18806         var block = start_block;
  18807         while (true) {
  18808             if (block.label) |label| {
  18809                 if (label.zir_block == zir_block) {
  18810                     const target_trace_index = if (block.parent) |parent_block| tgt: {
  18811                         break :tgt parent_block.error_return_trace_index;
  18812                     } else sema.error_return_trace_index_on_fn_entry;
  18813 
  18814                     if (start_block.error_return_trace_index != target_trace_index)
  18815                         break :b target_trace_index;
  18816 
  18817                     return; // No need to restore
  18818                 }
  18819             }
  18820             block = block.parent.?;
  18821         }
  18822     } else b: {
  18823         if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry)
  18824             break :b sema.error_return_trace_index_on_fn_entry;
  18825 
  18826         return; // No need to restore
  18827     };
  18828 
  18829     assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere
  18830 
  18831     const operand = try sema.resolveInstAllowNone(inst_data.operand);
  18832     return sema.popErrorReturnTrace(start_block, src, operand, saved_index);
  18833 }
  18834 
  18835 fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
  18836     const mod = sema.mod;
  18837     const ip = &mod.intern_pool;
  18838     assert(sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion);
  18839     const err_set_ty = sema.fn_ret_ty.errorUnionSet(mod).toIntern();
  18840     switch (err_set_ty) {
  18841         .adhoc_inferred_error_set_type => {
  18842             const ies = sema.fn_ret_ty_ies.?;
  18843             assert(ies.func == .none);
  18844             try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand));
  18845         },
  18846         else => if (ip.isInferredErrorSetType(err_set_ty)) {
  18847             const ies = sema.fn_ret_ty_ies.?;
  18848             assert(ies.func == sema.func_index);
  18849             try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand));
  18850         },
  18851     }
  18852 }
  18853 
  18854 fn addToInferredErrorSetPtr(sema: *Sema, ies: *InferredErrorSet, op_ty: Type) !void {
  18855     const arena = sema.arena;
  18856     const mod = sema.mod;
  18857     const ip = &mod.intern_pool;
  18858     switch (op_ty.zigTypeTag(mod)) {
  18859         .ErrorSet => try ies.addErrorSet(op_ty, ip, arena),
  18860         .ErrorUnion => try ies.addErrorSet(op_ty.errorUnionSet(mod), ip, arena),
  18861         else => {},
  18862     }
  18863 }
  18864 
  18865 fn analyzeRet(
  18866     sema: *Sema,
  18867     block: *Block,
  18868     uncasted_operand: Air.Inst.Ref,
  18869     src: LazySrcLoc,
  18870 ) CompileError!Zir.Inst.Index {
  18871     // Special case for returning an error to an inferred error set; we need to
  18872     // add the error tag to the inferred error set of the in-scope function, so
  18873     // that the coercion below works correctly.
  18874     const mod = sema.mod;
  18875     if (sema.fn_ret_ty_ies != null and sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion) {
  18876         try sema.addToInferredErrorSet(uncasted_operand);
  18877     }
  18878     const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, .{ .is_ret = true }) catch |err| switch (err) {
  18879         error.NotCoercible => unreachable,
  18880         else => |e| return e,
  18881     };
  18882 
  18883     if (block.inlining) |inlining| {
  18884         if (block.is_comptime) {
  18885             _ = try sema.resolveConstValue(block, src, operand, .{
  18886                 .needed_comptime_reason = "value being returned at comptime must be comptime-known",
  18887             });
  18888             inlining.comptime_result = operand;
  18889             return error.ComptimeReturn;
  18890         }
  18891         // We are inlining a function call; rewrite the `ret` as a `break`.
  18892         try inlining.merges.results.append(sema.gpa, operand);
  18893         _ = try block.addBr(inlining.merges.block_inst, operand);
  18894         return always_noreturn;
  18895     } else if (block.is_comptime) {
  18896         return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{});
  18897     } else if (sema.func_is_naked) {
  18898         const msg = msg: {
  18899             const msg = try sema.errMsg(block, src, "cannot return from naked function", .{});
  18900             errdefer msg.destroy(sema.gpa);
  18901 
  18902             try sema.errNote(block, src, msg, "can only return using assembly", .{});
  18903             break :msg msg;
  18904         };
  18905         return sema.failWithOwnedErrorMsg(block, msg);
  18906     }
  18907 
  18908     try sema.resolveTypeLayout(sema.fn_ret_ty);
  18909 
  18910     if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
  18911         // Avoid adding a frame to the error return trace in case the value is comptime-known
  18912         // to be not an error.
  18913         const is_non_err = try sema.analyzeIsNonErr(block, src, operand);
  18914         return sema.retWithErrTracing(block, src, is_non_err, .ret, operand);
  18915     }
  18916 
  18917     _ = try block.addUnOp(.ret, operand);
  18918 
  18919     return always_noreturn;
  18920 }
  18921 
  18922 fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
  18923     // extend this swich as additional operators are implemented
  18924     return switch (tag) {
  18925         .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true,
  18926         else => false,
  18927     };
  18928 }
  18929 
  18930 fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18931     const tracy = trace(@src());
  18932     defer tracy.end();
  18933 
  18934     const mod = sema.mod;
  18935     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].ptr_type;
  18936     const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index);
  18937     const elem_ty_src: LazySrcLoc = .{ .node_offset_ptr_elem = extra.data.src_node };
  18938     const sentinel_src: LazySrcLoc = .{ .node_offset_ptr_sentinel = extra.data.src_node };
  18939     const align_src: LazySrcLoc = .{ .node_offset_ptr_align = extra.data.src_node };
  18940     const addrspace_src: LazySrcLoc = .{ .node_offset_ptr_addrspace = extra.data.src_node };
  18941     const bitoffset_src: LazySrcLoc = .{ .node_offset_ptr_bitoffset = extra.data.src_node };
  18942     const hostsize_src: LazySrcLoc = .{ .node_offset_ptr_hostsize = extra.data.src_node };
  18943 
  18944     const elem_ty = blk: {
  18945         const air_inst = try sema.resolveInst(extra.data.elem_type);
  18946         const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| {
  18947             if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(mod)) {
  18948                 try sema.errNote(block, elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{});
  18949             }
  18950             return err;
  18951         };
  18952         if (ty.isGenericPoison()) return error.GenericPoison;
  18953         break :blk ty;
  18954     };
  18955 
  18956     if (elem_ty.zigTypeTag(mod) == .NoReturn)
  18957         return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{});
  18958 
  18959     const target = mod.getTarget();
  18960 
  18961     var extra_i = extra.end;
  18962 
  18963     const sentinel = if (inst_data.flags.has_sentinel) blk: {
  18964         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  18965         extra_i += 1;
  18966         const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src);
  18967         const val = try sema.resolveConstDefinedValue(block, sentinel_src, coerced, .{
  18968             .needed_comptime_reason = "pointer sentinel value must be comptime-known",
  18969         });
  18970         break :blk val.toIntern();
  18971     } else .none;
  18972 
  18973     const abi_align: Alignment = if (inst_data.flags.has_align) blk: {
  18974         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  18975         extra_i += 1;
  18976         const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), align_src);
  18977         const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{
  18978             .needed_comptime_reason = "pointer alignment must be comptime-known",
  18979         });
  18980         // Check if this happens to be the lazy alignment of our element type, in
  18981         // which case we can make this 0 without resolving it.
  18982         switch (mod.intern_pool.indexToKey(val.toIntern())) {
  18983             .int => |int| switch (int.storage) {
  18984                 .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none,
  18985                 else => {},
  18986             },
  18987             else => {},
  18988         }
  18989         const align_bytes = (try val.getUnsignedIntAdvanced(mod, sema)).?;
  18990         break :blk try sema.validateAlignAllowZero(block, align_src, align_bytes);
  18991     } else .none;
  18992 
  18993     const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: {
  18994         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  18995         extra_i += 1;
  18996         break :blk try sema.analyzeAddressSpace(block, addrspace_src, ref, .pointer);
  18997     } else if (elem_ty.zigTypeTag(mod) == .Fn and target.cpu.arch == .avr) .flash else .generic;
  18998 
  18999     const bit_offset: u16 = if (inst_data.flags.has_bit_range) blk: {
  19000         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  19001         extra_i += 1;
  19002         const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, Type.u16, .{
  19003             .needed_comptime_reason = "pointer bit-offset must be comptime-known",
  19004         });
  19005         break :blk @intCast(bit_offset);
  19006     } else 0;
  19007 
  19008     const host_size: u16 = if (inst_data.flags.has_bit_range) blk: {
  19009         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  19010         extra_i += 1;
  19011         const host_size = try sema.resolveInt(block, hostsize_src, ref, Type.u16, .{
  19012             .needed_comptime_reason = "pointer host size must be comptime-known",
  19013         });
  19014         break :blk @intCast(host_size);
  19015     } else 0;
  19016 
  19017     if (host_size != 0 and bit_offset >= host_size * 8) {
  19018         return sema.fail(block, bitoffset_src, "bit offset starts after end of host integer", .{});
  19019     }
  19020 
  19021     if (elem_ty.zigTypeTag(mod) == .Fn) {
  19022         if (inst_data.size != .One) {
  19023             return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{});
  19024         }
  19025         const fn_align = mod.typeToFunc(elem_ty).?.alignment;
  19026         if (inst_data.flags.has_align and abi_align != .none and fn_align != .none and
  19027             abi_align != fn_align)
  19028         {
  19029             return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{});
  19030         }
  19031     } else if (inst_data.size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) {
  19032         return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
  19033     } else if (inst_data.size == .C) {
  19034         if (!try sema.validateExternType(elem_ty, .other)) {
  19035             const msg = msg: {
  19036                 const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)});
  19037                 errdefer msg.destroy(sema.gpa);
  19038 
  19039                 const src_decl = mod.declPtr(block.src_decl);
  19040                 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl, mod), elem_ty, .other);
  19041 
  19042                 try sema.addDeclaredHereNote(msg, elem_ty);
  19043                 break :msg msg;
  19044             };
  19045             return sema.failWithOwnedErrorMsg(block, msg);
  19046         }
  19047         if (elem_ty.zigTypeTag(mod) == .Opaque) {
  19048             return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{});
  19049         }
  19050     }
  19051 
  19052     const ty = try sema.ptrType(.{
  19053         .child = elem_ty.toIntern(),
  19054         .sentinel = sentinel,
  19055         .flags = .{
  19056             .alignment = abi_align,
  19057             .address_space = address_space,
  19058             .is_const = !inst_data.flags.is_mutable,
  19059             .is_allowzero = inst_data.flags.is_allowzero,
  19060             .is_volatile = inst_data.flags.is_volatile,
  19061             .size = inst_data.size,
  19062         },
  19063         .packed_offset = .{
  19064             .bit_offset = bit_offset,
  19065             .host_size = host_size,
  19066         },
  19067     });
  19068     return Air.internedToRef(ty.toIntern());
  19069 }
  19070 
  19071 fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19072     const tracy = trace(@src());
  19073     defer tracy.end();
  19074 
  19075     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  19076     const src = inst_data.src();
  19077     const obj_ty = try sema.resolveType(block, src, inst_data.operand);
  19078     const mod = sema.mod;
  19079 
  19080     switch (obj_ty.zigTypeTag(mod)) {
  19081         .Struct => return sema.structInitEmpty(block, obj_ty, src, src),
  19082         .Array, .Vector => return sema.arrayInitEmpty(block, src, obj_ty),
  19083         .Void => return Air.internedToRef(Value.void.toIntern()),
  19084         .Union => return sema.fail(block, src, "union initializer must initialize one field", .{}),
  19085         else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
  19086     }
  19087 }
  19088 
  19089 fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_byref: bool) CompileError!Air.Inst.Ref {
  19090     const tracy = trace(@src());
  19091     defer tracy.end();
  19092 
  19093     const mod = sema.mod;
  19094     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  19095     const src = inst_data.src();
  19096     const ty_operand = sema.resolveType(block, src, inst_data.operand) catch |err| switch (err) {
  19097         // Generic poison means this is an untyped anonymous empty struct init
  19098         error.GenericPoison => return .empty_struct,
  19099         else => |e| return e,
  19100     };
  19101     const init_ty = if (is_byref) ty: {
  19102         const ptr_ty = ty_operand.optEuBaseType(mod);
  19103         assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction
  19104         if (!ptr_ty.isSlice(mod)) {
  19105             break :ty ptr_ty.childType(mod);
  19106         }
  19107         // To make `&.{}` a `[:s]T`, the init should be a `[0:s]T`.
  19108         break :ty try mod.arrayType(.{
  19109             .len = 0,
  19110             .sentinel = if (ptr_ty.sentinel(mod)) |s| s.toIntern() else .none,
  19111             .child = ptr_ty.childType(mod).toIntern(),
  19112         });
  19113     } else ty_operand;
  19114     const obj_ty = init_ty.optEuBaseType(mod);
  19115 
  19116     const empty_ref = switch (obj_ty.zigTypeTag(mod)) {
  19117         .Struct => try sema.structInitEmpty(block, obj_ty, src, src),
  19118         .Array, .Vector => try sema.arrayInitEmpty(block, src, obj_ty),
  19119         .Union => return sema.fail(block, src, "union initializer must initialize one field", .{}),
  19120         else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
  19121     };
  19122     const init_ref = try sema.coerce(block, init_ty, empty_ref, src);
  19123 
  19124     if (is_byref) {
  19125         const init_val = (try sema.resolveValue(init_ref)).?;
  19126         return anonDeclRef(sema, init_val.toIntern());
  19127     } else {
  19128         return init_ref;
  19129     }
  19130 }
  19131 
  19132 fn structInitEmpty(
  19133     sema: *Sema,
  19134     block: *Block,
  19135     struct_ty: Type,
  19136     dest_src: LazySrcLoc,
  19137     init_src: LazySrcLoc,
  19138 ) CompileError!Air.Inst.Ref {
  19139     const mod = sema.mod;
  19140     const gpa = sema.gpa;
  19141     // This logic must be synchronized with that in `zirStructInit`.
  19142     try sema.resolveTypeFields(struct_ty);
  19143 
  19144     // The init values to use for the struct instance.
  19145     const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(mod));
  19146     defer gpa.free(field_inits);
  19147     @memset(field_inits, .none);
  19148 
  19149     return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, struct_ty, false);
  19150 }
  19151 
  19152 fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref {
  19153     const mod = sema.mod;
  19154     const arr_len = obj_ty.arrayLen(mod);
  19155     if (arr_len != 0) {
  19156         if (obj_ty.zigTypeTag(mod) == .Array) {
  19157             return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len});
  19158         } else {
  19159             return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len});
  19160         }
  19161     }
  19162     return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  19163         .ty = obj_ty.toIntern(),
  19164         .storage = .{ .elems = &.{} },
  19165     } })));
  19166 }
  19167 
  19168 fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19169     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19170     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  19171     const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  19172     const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  19173     const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
  19174     const union_ty = try sema.resolveType(block, ty_src, extra.union_type);
  19175     if (union_ty.zigTypeTag(sema.mod) != .Union) {
  19176         const msg = msg: {
  19177             const msg = try sema.errMsg(block, ty_src, "expected union type, found '{}'", .{union_ty.fmt(sema.mod)});
  19178             errdefer msg.destroy(sema.gpa);
  19179             try sema.addDeclaredHereNote(msg, union_ty);
  19180             break :msg msg;
  19181         };
  19182         return sema.failWithOwnedErrorMsg(block, msg);
  19183     }
  19184     const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{
  19185         .needed_comptime_reason = "name of field being initialized must be comptime-known",
  19186     });
  19187     const init = try sema.resolveInst(extra.init);
  19188     return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src);
  19189 }
  19190 
  19191 fn unionInit(
  19192     sema: *Sema,
  19193     block: *Block,
  19194     uncasted_init: Air.Inst.Ref,
  19195     init_src: LazySrcLoc,
  19196     union_ty: Type,
  19197     union_ty_src: LazySrcLoc,
  19198     field_name: InternPool.NullTerminatedString,
  19199     field_src: LazySrcLoc,
  19200 ) CompileError!Air.Inst.Ref {
  19201     const mod = sema.mod;
  19202     const ip = &mod.intern_pool;
  19203     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
  19204     const field_ty = Type.fromInterned(mod.typeToUnion(union_ty).?.field_types.get(ip)[field_index]);
  19205     const init = try sema.coerce(block, field_ty, uncasted_init, init_src);
  19206 
  19207     if (try sema.resolveValue(init)) |init_val| {
  19208         const tag_ty = union_ty.unionTagTypeHypothetical(mod);
  19209         const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index);
  19210         return Air.internedToRef((try mod.intern(.{ .un = .{
  19211             .ty = union_ty.toIntern(),
  19212             .tag = try tag_val.intern(tag_ty, mod),
  19213             .val = try init_val.intern(field_ty, mod),
  19214         } })));
  19215     }
  19216 
  19217     try sema.requireRuntimeBlock(block, init_src, null);
  19218     _ = union_ty_src;
  19219     try sema.queueFullTypeResolution(union_ty);
  19220     return block.addUnionInit(union_ty, field_index, init);
  19221 }
  19222 
  19223 fn zirStructInit(
  19224     sema: *Sema,
  19225     block: *Block,
  19226     inst: Zir.Inst.Index,
  19227     is_ref: bool,
  19228 ) CompileError!Air.Inst.Ref {
  19229     const gpa = sema.gpa;
  19230     const zir_datas = sema.code.instructions.items(.data);
  19231     const inst_data = zir_datas[@intFromEnum(inst)].pl_node;
  19232     const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index);
  19233     const src = inst_data.src();
  19234 
  19235     const mod = sema.mod;
  19236     const ip = &mod.intern_pool;
  19237     const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;
  19238     const first_field_type_data = zir_datas[@intFromEnum(first_item.field_type)].pl_node;
  19239     const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;
  19240     const result_ty = sema.resolveType(block, src, first_field_type_extra.container_type) catch |err| switch (err) {
  19241         error.GenericPoison => {
  19242             // The type wasn't actually known, so treat this as an anon struct init.
  19243             return sema.structInitAnon(block, src, .typed_init, extra.data, extra.end, is_ref);
  19244         },
  19245         else => |e| return e,
  19246     };
  19247     const resolved_ty = result_ty.optEuBaseType(mod);
  19248     try sema.resolveTypeLayout(resolved_ty);
  19249 
  19250     if (resolved_ty.zigTypeTag(mod) == .Struct) {
  19251         // This logic must be synchronized with that in `zirStructInitEmpty`.
  19252 
  19253         // Maps field index to field_type index of where it was already initialized.
  19254         // For making sure all fields are accounted for and no fields are duplicated.
  19255         const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(mod));
  19256         defer gpa.free(found_fields);
  19257 
  19258         // The init values to use for the struct instance.
  19259         const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(mod));
  19260         defer gpa.free(field_inits);
  19261         @memset(field_inits, .none);
  19262 
  19263         var field_i: u32 = 0;
  19264         var extra_index = extra.end;
  19265 
  19266         const is_packed = resolved_ty.containerLayout(mod) == .Packed;
  19267         while (field_i < extra.data.fields_len) : (field_i += 1) {
  19268             const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index);
  19269             extra_index = item.end;
  19270 
  19271             const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
  19272             const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node };
  19273             const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
  19274             const field_name = try ip.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start));
  19275             const field_index = if (resolved_ty.isTuple(mod))
  19276                 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)
  19277             else
  19278                 try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
  19279             assert(field_inits[field_index] == .none);
  19280             found_fields[field_index] = item.data.field_type;
  19281             const uncoerced_init = try sema.resolveInst(item.data.init);
  19282             const field_ty = resolved_ty.structFieldType(field_index, mod);
  19283             field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src);
  19284             if (!is_packed) {
  19285                 try sema.resolveStructFieldInits(resolved_ty);
  19286                 if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| {
  19287                     const init_val = (try sema.resolveValue(field_inits[field_index])) orelse {
  19288                         return sema.failWithNeededComptime(block, field_src, .{
  19289                             .needed_comptime_reason = "value stored in comptime field must be comptime-known",
  19290                         });
  19291                     };
  19292 
  19293                     if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) {
  19294                         return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
  19295                     }
  19296                 }
  19297             }
  19298         }
  19299 
  19300         return sema.finishStructInit(block, src, src, field_inits, resolved_ty, result_ty, is_ref);
  19301     } else if (resolved_ty.zigTypeTag(mod) == .Union) {
  19302         if (extra.data.fields_len != 1) {
  19303             return sema.fail(block, src, "union initialization expects exactly one field", .{});
  19304         }
  19305 
  19306         const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end);
  19307 
  19308         const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
  19309         const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node };
  19310         const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
  19311         const field_name = try ip.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start));
  19312         const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src);
  19313         const tag_ty = resolved_ty.unionTagTypeHypothetical(mod);
  19314         const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index);
  19315         const field_ty = Type.fromInterned(mod.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index]);
  19316 
  19317         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  19318             return sema.failWithOwnedErrorMsg(block, msg: {
  19319                 const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{});
  19320                 errdefer msg.destroy(sema.gpa);
  19321 
  19322                 try sema.addFieldErrNote(resolved_ty, field_index, msg, "field '{}' declared here", .{
  19323                     field_name.fmt(ip),
  19324                 });
  19325                 try sema.addDeclaredHereNote(msg, resolved_ty);
  19326                 break :msg msg;
  19327             });
  19328         }
  19329 
  19330         const uncoerced_init_inst = try sema.resolveInst(item.data.init);
  19331         const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src);
  19332 
  19333         if (try sema.resolveValue(init_inst)) |val| {
  19334             const struct_val = Value.fromInterned((try mod.intern(.{ .un = .{
  19335                 .ty = resolved_ty.toIntern(),
  19336                 .tag = try tag_val.intern(tag_ty, mod),
  19337                 .val = try val.intern(field_ty, mod),
  19338             } })));
  19339             const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src);
  19340             const final_val = (try sema.resolveValue(final_val_inst)).?;
  19341             return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
  19342         }
  19343 
  19344         if (try sema.typeRequiresComptime(resolved_ty)) {
  19345             return sema.failWithNeededComptime(block, field_src, .{
  19346                 .needed_comptime_reason = "initializer of comptime only union must be comptime-known",
  19347             });
  19348         }
  19349 
  19350         if (is_ref) {
  19351             const target = mod.getTarget();
  19352             const alloc_ty = try sema.ptrType(.{
  19353                 .child = result_ty.toIntern(),
  19354                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19355             });
  19356             const alloc = try block.addTy(.alloc, alloc_ty);
  19357             const base_ptr = try sema.optEuBasePtrInit(block, alloc, src);
  19358             const field_ptr = try sema.unionFieldPtr(block, field_src, base_ptr, field_name, field_src, resolved_ty, true);
  19359             try sema.storePtr(block, src, field_ptr, init_inst);
  19360             const new_tag = Air.internedToRef(tag_val.toIntern());
  19361             _ = try block.addBinOp(.set_union_tag, base_ptr, new_tag);
  19362             return sema.makePtrConst(block, alloc);
  19363         }
  19364 
  19365         try sema.requireRuntimeBlock(block, src, null);
  19366         try sema.queueFullTypeResolution(resolved_ty);
  19367         const union_val = try block.addUnionInit(resolved_ty, field_index, init_inst);
  19368         return sema.coerce(block, result_ty, union_val, src);
  19369     }
  19370     unreachable;
  19371 }
  19372 
  19373 fn finishStructInit(
  19374     sema: *Sema,
  19375     block: *Block,
  19376     init_src: LazySrcLoc,
  19377     dest_src: LazySrcLoc,
  19378     field_inits: []Air.Inst.Ref,
  19379     struct_ty: Type,
  19380     result_ty: Type,
  19381     is_ref: bool,
  19382 ) CompileError!Air.Inst.Ref {
  19383     const mod = sema.mod;
  19384     const ip = &mod.intern_pool;
  19385 
  19386     var root_msg: ?*Module.ErrorMsg = null;
  19387     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  19388 
  19389     switch (ip.indexToKey(struct_ty.toIntern())) {
  19390         .anon_struct_type => |anon_struct| {
  19391             // We can't get the slices, as the coercion may invalidate them.
  19392             for (0..anon_struct.types.len) |i| {
  19393                 if (field_inits[i] != .none) {
  19394                     // Coerce the init value to the field type.
  19395                     const field_ty = Type.fromInterned(anon_struct.types.get(ip)[i]);
  19396                     field_inits[i] = sema.coerce(block, field_ty, field_inits[i], .unneeded) catch |err| switch (err) {
  19397                         error.NeededSourceLocation => {
  19398                             const decl = mod.declPtr(block.src_decl);
  19399                             const field_src = mod.initSrc(init_src.node_offset.x, decl, i);
  19400                             _ = try sema.coerce(block, field_ty, field_inits[i], field_src);
  19401                             unreachable;
  19402                         },
  19403                         else => |e| return e,
  19404                     };
  19405                     continue;
  19406                 }
  19407 
  19408                 const default_val = anon_struct.values.get(ip)[i];
  19409 
  19410                 if (default_val == .none) {
  19411                     if (anon_struct.names.len == 0) {
  19412                         const template = "missing tuple field with index {d}";
  19413                         if (root_msg) |msg| {
  19414                             try sema.errNote(block, init_src, msg, template, .{i});
  19415                         } else {
  19416                             root_msg = try sema.errMsg(block, init_src, template, .{i});
  19417                         }
  19418                     } else {
  19419                         const field_name = anon_struct.names.get(ip)[i];
  19420                         const template = "missing struct field: {}";
  19421                         const args = .{field_name.fmt(ip)};
  19422                         if (root_msg) |msg| {
  19423                             try sema.errNote(block, init_src, msg, template, args);
  19424                         } else {
  19425                             root_msg = try sema.errMsg(block, init_src, template, args);
  19426                         }
  19427                     }
  19428                 } else {
  19429                     field_inits[i] = Air.internedToRef(default_val);
  19430                 }
  19431             }
  19432         },
  19433         .struct_type => |struct_type| {
  19434             for (0..struct_type.field_types.len) |i| {
  19435                 if (field_inits[i] != .none) {
  19436                     // Coerce the init value to the field type.
  19437                     const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  19438                     field_inits[i] = sema.coerce(block, field_ty, field_inits[i], init_src) catch |err| switch (err) {
  19439                         error.NeededSourceLocation => {
  19440                             const decl = mod.declPtr(block.src_decl);
  19441                             const field_src = mod.initSrc(init_src.node_offset.x, decl, i);
  19442                             _ = try sema.coerce(block, field_ty, field_inits[i], field_src);
  19443                             unreachable;
  19444                         },
  19445                         else => |e| return e,
  19446                     };
  19447                     continue;
  19448                 }
  19449 
  19450                 try sema.resolveStructFieldInits(struct_ty);
  19451 
  19452                 const field_init = struct_type.fieldInit(ip, i);
  19453                 if (field_init == .none) {
  19454                     const field_name = struct_type.field_names.get(ip)[i];
  19455                     const template = "missing struct field: {}";
  19456                     const args = .{field_name.fmt(ip)};
  19457                     if (root_msg) |msg| {
  19458                         try sema.errNote(block, init_src, msg, template, args);
  19459                     } else {
  19460                         root_msg = try sema.errMsg(block, init_src, template, args);
  19461                     }
  19462                 } else {
  19463                     field_inits[i] = Air.internedToRef(field_init);
  19464                 }
  19465             }
  19466         },
  19467         else => unreachable,
  19468     }
  19469 
  19470     if (root_msg) |msg| {
  19471         if (mod.typeToStruct(struct_ty)) |struct_type| {
  19472             const decl = mod.declPtr(struct_type.decl.unwrap().?);
  19473             const fqn = try decl.getFullyQualifiedName(mod);
  19474             try mod.errNoteNonLazy(
  19475                 decl.srcLoc(mod),
  19476                 msg,
  19477                 "struct '{}' declared here",
  19478                 .{fqn.fmt(ip)},
  19479             );
  19480         }
  19481         root_msg = null;
  19482         return sema.failWithOwnedErrorMsg(block, msg);
  19483     }
  19484 
  19485     // Find which field forces the expression to be runtime, if any.
  19486     const opt_runtime_index = for (field_inits, 0..) |field_init, i| {
  19487         if (!(try sema.isComptimeKnown(field_init))) {
  19488             break i;
  19489         }
  19490     } else null;
  19491 
  19492     const runtime_index = opt_runtime_index orelse {
  19493         const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
  19494         for (elems, field_inits) |*elem, field_init| {
  19495             elem.* = (sema.resolveValue(field_init) catch unreachable).?.toIntern();
  19496         }
  19497         const struct_val = try mod.intern(.{ .aggregate = .{
  19498             .ty = struct_ty.toIntern(),
  19499             .storage = .{ .elems = elems },
  19500         } });
  19501         const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val), init_src);
  19502         const final_val = (try sema.resolveValue(final_val_inst)).?;
  19503         return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
  19504     };
  19505 
  19506     if (try sema.typeRequiresComptime(struct_ty)) {
  19507         const decl = mod.declPtr(block.src_decl);
  19508         const field_src = mod.initSrc(init_src.node_offset.x, decl, runtime_index);
  19509         return sema.failWithNeededComptime(block, field_src, .{
  19510             .needed_comptime_reason = "initializer of comptime only struct must be comptime-known",
  19511         });
  19512     }
  19513 
  19514     if (is_ref) {
  19515         try sema.resolveStructLayout(struct_ty);
  19516         const target = sema.mod.getTarget();
  19517         const alloc_ty = try sema.ptrType(.{
  19518             .child = result_ty.toIntern(),
  19519             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19520         });
  19521         const alloc = try block.addTy(.alloc, alloc_ty);
  19522         const base_ptr = try sema.optEuBasePtrInit(block, alloc, init_src);
  19523         for (field_inits, 0..) |field_init, i_usize| {
  19524             const i: u32 = @intCast(i_usize);
  19525             const field_src = dest_src;
  19526             const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, base_ptr, i, field_src, struct_ty, true);
  19527             try sema.storePtr(block, dest_src, field_ptr, field_init);
  19528         }
  19529 
  19530         return sema.makePtrConst(block, alloc);
  19531     }
  19532 
  19533     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
  19534         error.NeededSourceLocation => {
  19535             const decl = mod.declPtr(block.src_decl);
  19536             const field_src = mod.initSrc(dest_src.node_offset.x, decl, runtime_index);
  19537             try sema.requireRuntimeBlock(block, dest_src, field_src);
  19538             unreachable;
  19539         },
  19540         else => |e| return e,
  19541     };
  19542     try sema.queueFullTypeResolution(struct_ty);
  19543     const struct_val = try block.addAggregateInit(struct_ty, field_inits);
  19544     return sema.coerce(block, result_ty, struct_val, init_src);
  19545 }
  19546 
  19547 fn zirStructInitAnon(
  19548     sema: *Sema,
  19549     block: *Block,
  19550     inst: Zir.Inst.Index,
  19551 ) CompileError!Air.Inst.Ref {
  19552     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19553     const src = inst_data.src();
  19554     const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
  19555     return sema.structInitAnon(block, src, .anon_init, extra.data, extra.end, false);
  19556 }
  19557 
  19558 fn structInitAnon(
  19559     sema: *Sema,
  19560     block: *Block,
  19561     src: LazySrcLoc,
  19562     /// It is possible for a typed struct_init to be downgraded to an anonymous init due to a
  19563     /// generic poison type. In this case, we need to know to interpret the extra data differently.
  19564     comptime kind: enum { anon_init, typed_init },
  19565     extra_data: switch (kind) {
  19566         .anon_init => Zir.Inst.StructInitAnon,
  19567         .typed_init => Zir.Inst.StructInit,
  19568     },
  19569     extra_end: usize,
  19570     is_ref: bool,
  19571 ) CompileError!Air.Inst.Ref {
  19572     const mod = sema.mod;
  19573     const gpa = sema.gpa;
  19574     const ip = &mod.intern_pool;
  19575     const zir_datas = sema.code.instructions.items(.data);
  19576 
  19577     const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);
  19578     const values = try sema.arena.alloc(InternPool.Index, types.len);
  19579     const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len);
  19580 
  19581     // Find which field forces the expression to be runtime, if any.
  19582     const opt_runtime_index = rs: {
  19583         var runtime_index: ?usize = null;
  19584         var extra_index = extra_end;
  19585         for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| {
  19586             const item = switch (kind) {
  19587                 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
  19588                 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
  19589             };
  19590             extra_index = item.end;
  19591 
  19592             const name = switch (kind) {
  19593                 .anon_init => sema.code.nullTerminatedString(item.data.field_name),
  19594                 .typed_init => name: {
  19595                     // `item.data.field_type` references a `field_type` instruction
  19596                     const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
  19597                     const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index);
  19598                     break :name sema.code.nullTerminatedString(field_type_extra.data.name_start);
  19599                 },
  19600             };
  19601 
  19602             const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
  19603             field_name.* = name_ip;
  19604 
  19605             const init = try sema.resolveInst(item.data.init);
  19606             field_ty.* = sema.typeOf(init).toIntern();
  19607             if (Type.fromInterned(field_ty.*).zigTypeTag(mod) == .Opaque) {
  19608                 const msg = msg: {
  19609                     const decl = mod.declPtr(block.src_decl);
  19610                     const field_src = mod.initSrc(src.node_offset.x, decl, @intCast(i_usize));
  19611                     const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  19612                     errdefer msg.destroy(sema.gpa);
  19613 
  19614                     try sema.addDeclaredHereNote(msg, Type.fromInterned(field_ty.*));
  19615                     break :msg msg;
  19616                 };
  19617                 return sema.failWithOwnedErrorMsg(block, msg);
  19618             }
  19619             if (try sema.resolveValue(init)) |init_val| {
  19620                 field_val.* = try init_val.intern(Type.fromInterned(field_ty.*), mod);
  19621             } else {
  19622                 field_val.* = .none;
  19623                 runtime_index = @intCast(i_usize);
  19624             }
  19625         }
  19626         break :rs runtime_index;
  19627     };
  19628 
  19629     const tuple_ty = try ip.getAnonStructType(gpa, .{
  19630         .names = names,
  19631         .types = types,
  19632         .values = values,
  19633     });
  19634 
  19635     const runtime_index = opt_runtime_index orelse {
  19636         const tuple_val = try mod.intern(.{ .aggregate = .{
  19637             .ty = tuple_ty,
  19638             .storage = .{ .elems = values },
  19639         } });
  19640         return sema.addConstantMaybeRef(tuple_val, is_ref);
  19641     };
  19642 
  19643     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
  19644         error.NeededSourceLocation => {
  19645             const decl = mod.declPtr(block.src_decl);
  19646             const field_src = mod.initSrc(src.node_offset.x, decl, runtime_index);
  19647             try sema.requireRuntimeBlock(block, src, field_src);
  19648             unreachable;
  19649         },
  19650         else => |e| return e,
  19651     };
  19652 
  19653     if (is_ref) {
  19654         const target = mod.getTarget();
  19655         const alloc_ty = try sema.ptrType(.{
  19656             .child = tuple_ty,
  19657             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19658         });
  19659         const alloc = try block.addTy(.alloc, alloc_ty);
  19660         var extra_index = extra_end;
  19661         for (types, 0..) |field_ty, i_usize| {
  19662             const i: u32 = @intCast(i_usize);
  19663             const item = switch (kind) {
  19664                 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
  19665                 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
  19666             };
  19667             extra_index = item.end;
  19668 
  19669             const field_ptr_ty = try sema.ptrType(.{
  19670                 .child = field_ty,
  19671                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19672             });
  19673             if (values[i] == .none) {
  19674                 const init = try sema.resolveInst(item.data.init);
  19675                 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
  19676                 _ = try block.addBinOp(.store, field_ptr, init);
  19677             }
  19678         }
  19679 
  19680         return sema.makePtrConst(block, alloc);
  19681     }
  19682 
  19683     const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len);
  19684     var extra_index = extra_end;
  19685     for (types, 0..) |_, i| {
  19686         const item = switch (kind) {
  19687             .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
  19688             .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
  19689         };
  19690         extra_index = item.end;
  19691         element_refs[i] = try sema.resolveInst(item.data.init);
  19692     }
  19693 
  19694     return block.addAggregateInit(Type.fromInterned(tuple_ty), element_refs);
  19695 }
  19696 
  19697 fn zirArrayInit(
  19698     sema: *Sema,
  19699     block: *Block,
  19700     inst: Zir.Inst.Index,
  19701     is_ref: bool,
  19702 ) CompileError!Air.Inst.Ref {
  19703     const mod = sema.mod;
  19704     const gpa = sema.gpa;
  19705     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19706     const src = inst_data.src();
  19707 
  19708     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
  19709     const args = sema.code.refSlice(extra.end, extra.data.operands_len);
  19710     assert(args.len >= 2); // array_ty + at least one element
  19711 
  19712     const result_ty = sema.resolveType(block, src, args[0]) catch |err| switch (err) {
  19713         error.GenericPoison => {
  19714             // The type wasn't actually known, so treat this as an anon array init.
  19715             return sema.arrayInitAnon(block, src, args[1..], is_ref);
  19716         },
  19717         else => |e| return e,
  19718     };
  19719     const array_ty = result_ty.optEuBaseType(mod);
  19720     const is_tuple = array_ty.zigTypeTag(mod) == .Struct;
  19721     const sentinel_val = array_ty.sentinel(mod);
  19722 
  19723     var root_msg: ?*Module.ErrorMsg = null;
  19724     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  19725 
  19726     const final_len = try sema.usizeCast(block, src, array_ty.arrayLenIncludingSentinel(mod));
  19727     const resolved_args = try gpa.alloc(Air.Inst.Ref, final_len);
  19728     defer gpa.free(resolved_args);
  19729     for (resolved_args, 0..) |*dest, i| {
  19730         // Less inits than needed.
  19731         if (i + 2 > args.len) if (is_tuple) {
  19732             const default_val = array_ty.structFieldDefaultValue(i, mod).toIntern();
  19733             if (default_val == .unreachable_value) {
  19734                 const template = "missing tuple field with index {d}";
  19735                 if (root_msg) |msg| {
  19736                     try sema.errNote(block, src, msg, template, .{i});
  19737                 } else {
  19738                     root_msg = try sema.errMsg(block, src, template, .{i});
  19739                 }
  19740             } else {
  19741                 dest.* = Air.internedToRef(default_val);
  19742             }
  19743             continue;
  19744         } else {
  19745             dest.* = Air.internedToRef(sentinel_val.?.toIntern());
  19746             break;
  19747         };
  19748 
  19749         const arg = args[i + 1];
  19750         const resolved_arg = try sema.resolveInst(arg);
  19751         const elem_ty = if (is_tuple)
  19752             array_ty.structFieldType(i, mod)
  19753         else
  19754             array_ty.elemType2(mod);
  19755         dest.* = sema.coerce(block, elem_ty, resolved_arg, .unneeded) catch |err| switch (err) {
  19756             error.NeededSourceLocation => {
  19757                 const decl = mod.declPtr(block.src_decl);
  19758                 const elem_src = mod.initSrc(src.node_offset.x, decl, i);
  19759                 _ = try sema.coerce(block, elem_ty, resolved_arg, elem_src);
  19760                 unreachable;
  19761             },
  19762             else => return err,
  19763         };
  19764         if (is_tuple) {
  19765             if (array_ty.structFieldIsComptime(i, mod))
  19766                 try sema.resolveStructFieldInits(array_ty);
  19767             if (try array_ty.structFieldValueComptime(mod, i)) |field_val| {
  19768                 const init_val = try sema.resolveValue(dest.*) orelse {
  19769                     const decl = mod.declPtr(block.src_decl);
  19770                     const elem_src = mod.initSrc(src.node_offset.x, decl, i);
  19771                     return sema.failWithNeededComptime(block, elem_src, .{
  19772                         .needed_comptime_reason = "value stored in comptime field must be comptime-known",
  19773                     });
  19774                 };
  19775                 if (!field_val.eql(init_val, elem_ty, mod)) {
  19776                     const decl = mod.declPtr(block.src_decl);
  19777                     const elem_src = mod.initSrc(src.node_offset.x, decl, i);
  19778                     return sema.failWithInvalidComptimeFieldStore(block, elem_src, array_ty, i);
  19779                 }
  19780             }
  19781         }
  19782     }
  19783 
  19784     if (root_msg) |msg| {
  19785         try sema.addDeclaredHereNote(msg, array_ty);
  19786         root_msg = null;
  19787         return sema.failWithOwnedErrorMsg(block, msg);
  19788     }
  19789 
  19790     const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| {
  19791         const comptime_known = try sema.isComptimeKnown(arg);
  19792         if (!comptime_known) break @intCast(i);
  19793     } else null;
  19794 
  19795     const runtime_index = opt_runtime_index orelse {
  19796         const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len);
  19797         for (elem_vals, resolved_args, 0..) |*val, arg, i| {
  19798             const elem_ty = if (is_tuple)
  19799                 array_ty.structFieldType(i, mod)
  19800             else
  19801                 array_ty.elemType2(mod);
  19802             // We checked that all args are comptime above.
  19803             val.* = try ((sema.resolveValue(arg) catch unreachable).?).intern(elem_ty, mod);
  19804         }
  19805         const arr_val = try mod.intern(.{ .aggregate = .{
  19806             .ty = array_ty.toIntern(),
  19807             .storage = .{ .elems = elem_vals },
  19808         } });
  19809         const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val), src);
  19810         const result_val = (try sema.resolveValue(result_ref)).?;
  19811         return sema.addConstantMaybeRef(result_val.toIntern(), is_ref);
  19812     };
  19813 
  19814     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
  19815         error.NeededSourceLocation => {
  19816             const decl = mod.declPtr(block.src_decl);
  19817             const elem_src = mod.initSrc(src.node_offset.x, decl, runtime_index);
  19818             try sema.requireRuntimeBlock(block, src, elem_src);
  19819             unreachable;
  19820         },
  19821         else => return err,
  19822     };
  19823     try sema.queueFullTypeResolution(array_ty);
  19824 
  19825     if (is_ref) {
  19826         const target = mod.getTarget();
  19827         const alloc_ty = try sema.ptrType(.{
  19828             .child = result_ty.toIntern(),
  19829             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19830         });
  19831         const alloc = try block.addTy(.alloc, alloc_ty);
  19832         const base_ptr = try sema.optEuBasePtrInit(block, alloc, src);
  19833 
  19834         if (is_tuple) {
  19835             for (resolved_args, 0..) |arg, i| {
  19836                 const elem_ptr_ty = try sema.ptrType(.{
  19837                     .child = array_ty.structFieldType(i, mod).toIntern(),
  19838                     .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19839                 });
  19840                 const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern());
  19841 
  19842                 const index = try mod.intRef(Type.usize, i);
  19843                 const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref);
  19844                 _ = try block.addBinOp(.store, elem_ptr, arg);
  19845             }
  19846             return sema.makePtrConst(block, alloc);
  19847         }
  19848 
  19849         const elem_ptr_ty = try sema.ptrType(.{
  19850             .child = array_ty.elemType2(mod).toIntern(),
  19851             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19852         });
  19853         const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern());
  19854 
  19855         for (resolved_args, 0..) |arg, i| {
  19856             const index = try mod.intRef(Type.usize, i);
  19857             const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref);
  19858             _ = try block.addBinOp(.store, elem_ptr, arg);
  19859         }
  19860         return sema.makePtrConst(block, alloc);
  19861     }
  19862 
  19863     const arr_ref = try block.addAggregateInit(array_ty, resolved_args);
  19864     return sema.coerce(block, result_ty, arr_ref, src);
  19865 }
  19866 
  19867 fn zirArrayInitAnon(
  19868     sema: *Sema,
  19869     block: *Block,
  19870     inst: Zir.Inst.Index,
  19871 ) CompileError!Air.Inst.Ref {
  19872     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19873     const src = inst_data.src();
  19874     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
  19875     const operands = sema.code.refSlice(extra.end, extra.data.operands_len);
  19876     return sema.arrayInitAnon(block, src, operands, false);
  19877 }
  19878 
  19879 fn arrayInitAnon(
  19880     sema: *Sema,
  19881     block: *Block,
  19882     src: LazySrcLoc,
  19883     operands: []const Zir.Inst.Ref,
  19884     is_ref: bool,
  19885 ) CompileError!Air.Inst.Ref {
  19886     const mod = sema.mod;
  19887     const gpa = sema.gpa;
  19888     const ip = &mod.intern_pool;
  19889 
  19890     const types = try sema.arena.alloc(InternPool.Index, operands.len);
  19891     const values = try sema.arena.alloc(InternPool.Index, operands.len);
  19892 
  19893     const opt_runtime_src = rs: {
  19894         var runtime_src: ?LazySrcLoc = null;
  19895         for (operands, 0..) |operand, i| {
  19896             const operand_src = src; // TODO better source location
  19897             const elem = try sema.resolveInst(operand);
  19898             types[i] = sema.typeOf(elem).toIntern();
  19899             if (Type.fromInterned(types[i]).zigTypeTag(mod) == .Opaque) {
  19900                 const msg = msg: {
  19901                     const msg = try sema.errMsg(block, operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  19902                     errdefer msg.destroy(gpa);
  19903 
  19904                     try sema.addDeclaredHereNote(msg, Type.fromInterned(types[i]));
  19905                     break :msg msg;
  19906                 };
  19907                 return sema.failWithOwnedErrorMsg(block, msg);
  19908             }
  19909             if (try sema.resolveValue(elem)) |val| {
  19910                 values[i] = val.toIntern();
  19911             } else {
  19912                 values[i] = .none;
  19913                 runtime_src = operand_src;
  19914             }
  19915         }
  19916         break :rs runtime_src;
  19917     };
  19918 
  19919     const tuple_ty = try ip.getAnonStructType(gpa, .{
  19920         .types = types,
  19921         .values = values,
  19922         .names = &.{},
  19923     });
  19924 
  19925     const runtime_src = opt_runtime_src orelse {
  19926         const tuple_val = try mod.intern(.{ .aggregate = .{
  19927             .ty = tuple_ty,
  19928             .storage = .{ .elems = values },
  19929         } });
  19930         return sema.addConstantMaybeRef(tuple_val, is_ref);
  19931     };
  19932 
  19933     try sema.requireRuntimeBlock(block, src, runtime_src);
  19934 
  19935     if (is_ref) {
  19936         const target = sema.mod.getTarget();
  19937         const alloc_ty = try sema.ptrType(.{
  19938             .child = tuple_ty,
  19939             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19940         });
  19941         const alloc = try block.addTy(.alloc, alloc_ty);
  19942         for (operands, 0..) |operand, i_usize| {
  19943             const i: u32 = @intCast(i_usize);
  19944             const field_ptr_ty = try sema.ptrType(.{
  19945                 .child = types[i],
  19946                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19947             });
  19948             if (values[i] == .none) {
  19949                 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
  19950                 _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand));
  19951             }
  19952         }
  19953 
  19954         return sema.makePtrConst(block, alloc);
  19955     }
  19956 
  19957     const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
  19958     for (operands, 0..) |operand, i| {
  19959         element_refs[i] = try sema.resolveInst(operand);
  19960     }
  19961 
  19962     return block.addAggregateInit(Type.fromInterned(tuple_ty), element_refs);
  19963 }
  19964 
  19965 fn addConstantMaybeRef(sema: *Sema, val: InternPool.Index, is_ref: bool) !Air.Inst.Ref {
  19966     return if (is_ref) anonDeclRef(sema, val) else Air.internedToRef(val);
  19967 }
  19968 
  19969 fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19970     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19971     const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data;
  19972     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  19973     const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  19974     const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
  19975     const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{
  19976         .needed_comptime_reason = "field name must be comptime-known",
  19977     });
  19978     return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
  19979 }
  19980 
  19981 fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19982     const mod = sema.mod;
  19983     const ip = &mod.intern_pool;
  19984     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19985     const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
  19986     const ty_src = inst_data.src();
  19987     const field_name_src: LazySrcLoc = .{ .node_offset_field_name_init = inst_data.src_node };
  19988     const wrapped_aggregate_ty = sema.resolveType(block, ty_src, extra.container_type) catch |err| switch (err) {
  19989         // Since this is a ZIR instruction that returns a type, encountering
  19990         // generic poison should not result in a failed compilation, but the
  19991         // generic poison type. This prevents unnecessary failures when
  19992         // constructing types at compile-time.
  19993         error.GenericPoison => return .generic_poison_type,
  19994         else => |e| return e,
  19995     };
  19996     const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(mod);
  19997     const zir_field_name = sema.code.nullTerminatedString(extra.name_start);
  19998     const field_name = try ip.getOrPutString(sema.gpa, zir_field_name);
  19999     return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src);
  20000 }
  20001 
  20002 fn fieldType(
  20003     sema: *Sema,
  20004     block: *Block,
  20005     aggregate_ty: Type,
  20006     field_name: InternPool.NullTerminatedString,
  20007     field_src: LazySrcLoc,
  20008     ty_src: LazySrcLoc,
  20009 ) CompileError!Air.Inst.Ref {
  20010     const mod = sema.mod;
  20011     const ip = &mod.intern_pool;
  20012     var cur_ty = aggregate_ty;
  20013     while (true) {
  20014         try sema.resolveTypeFields(cur_ty);
  20015         switch (cur_ty.zigTypeTag(mod)) {
  20016             .Struct => switch (ip.indexToKey(cur_ty.toIntern())) {
  20017                 .anon_struct_type => |anon_struct| {
  20018                     const field_index = if (anon_struct.names.len == 0)
  20019                         try sema.tupleFieldIndex(block, cur_ty, field_name, field_src)
  20020                     else
  20021                         try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src);
  20022                     return Air.internedToRef(anon_struct.types.get(ip)[field_index]);
  20023                 },
  20024                 .struct_type => |struct_type| {
  20025                     const field_index = struct_type.nameIndex(ip, field_name) orelse
  20026                         return sema.failWithBadStructFieldAccess(block, struct_type, field_src, field_name);
  20027                     const field_ty = struct_type.field_types.get(ip)[field_index];
  20028                     return Air.internedToRef(field_ty);
  20029                 },
  20030                 else => unreachable,
  20031             },
  20032             .Union => {
  20033                 const union_obj = mod.typeToUnion(cur_ty).?;
  20034                 const field_index = union_obj.nameIndex(ip, field_name) orelse
  20035                     return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
  20036                 const field_ty = union_obj.field_types.get(ip)[field_index];
  20037                 return Air.internedToRef(field_ty);
  20038             },
  20039             .Optional => {
  20040                 // Struct/array init through optional requires the child type to not be a pointer.
  20041                 // If the child of .optional is a pointer it'll error on the next loop.
  20042                 cur_ty = Type.fromInterned(ip.indexToKey(cur_ty.toIntern()).opt_type);
  20043                 continue;
  20044             },
  20045             .ErrorUnion => {
  20046                 cur_ty = cur_ty.errorUnionPayload(mod);
  20047                 continue;
  20048             },
  20049             else => {},
  20050         }
  20051         return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{
  20052             cur_ty.fmt(sema.mod),
  20053         });
  20054     }
  20055 }
  20056 
  20057 fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
  20058     return sema.getErrorReturnTrace(block);
  20059 }
  20060 
  20061 fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
  20062     const mod = sema.mod;
  20063     const ip = &mod.intern_pool;
  20064     const stack_trace_ty = try sema.getBuiltinType("StackTrace");
  20065     try sema.resolveTypeFields(stack_trace_ty);
  20066     const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty);
  20067     const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern());
  20068 
  20069     if (sema.owner_func_index != .none and
  20070         ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn and
  20071         block.ownerModule().error_tracing)
  20072     {
  20073         return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
  20074     }
  20075     return Air.internedToRef((try mod.intern(.{ .opt = .{
  20076         .ty = opt_ptr_stack_trace_ty.toIntern(),
  20077         .val = .none,
  20078     } })));
  20079 }
  20080 
  20081 fn zirFrame(
  20082     sema: *Sema,
  20083     block: *Block,
  20084     extended: Zir.Inst.Extended.InstData,
  20085 ) CompileError!Air.Inst.Ref {
  20086     const src = LazySrcLoc.nodeOffset(@bitCast(extended.operand));
  20087     return sema.failWithUseOfAsync(block, src);
  20088 }
  20089 
  20090 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20091     const mod = sema.mod;
  20092     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20093     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20094     const ty = try sema.resolveType(block, operand_src, inst_data.operand);
  20095     if (ty.isNoReturn(mod)) {
  20096         return sema.fail(block, operand_src, "no align available for type '{}'", .{ty.fmt(sema.mod)});
  20097     }
  20098     const val = try ty.lazyAbiAlignment(mod);
  20099     if (val.isLazyAlign(mod)) {
  20100         try sema.queueFullTypeResolution(ty);
  20101     }
  20102     return Air.internedToRef(val.toIntern());
  20103 }
  20104 
  20105 fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20106     const mod = sema.mod;
  20107     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20108     const src = inst_data.src();
  20109     const operand = try sema.resolveInst(inst_data.operand);
  20110     const operand_ty = sema.typeOf(operand);
  20111     const is_vector = operand_ty.zigTypeTag(mod) == .Vector;
  20112     const operand_scalar_ty = operand_ty.scalarType(mod);
  20113     if (operand_scalar_ty.toIntern() != .bool_type) {
  20114         return sema.fail(block, src, "expected 'bool', found '{}'", .{operand_scalar_ty.zigTypeTag(mod)});
  20115     }
  20116     if (try sema.resolveValue(operand)) |val| {
  20117         if (!is_vector) {
  20118             if (val.isUndef(mod)) return mod.undefRef(Type.u1);
  20119             if (val.toBool()) return Air.internedToRef((try mod.intValue(Type.u1, 1)).toIntern());
  20120             return Air.internedToRef((try mod.intValue(Type.u1, 0)).toIntern());
  20121         }
  20122         const len = operand_ty.vectorLen(mod);
  20123         const dest_ty = try mod.vectorType(.{ .child = .u1_type, .len = len });
  20124         if (val.isUndef(mod)) return mod.undefRef(dest_ty);
  20125         const new_elems = try sema.arena.alloc(InternPool.Index, len);
  20126         for (new_elems, 0..) |*new_elem, i| {
  20127             const old_elem = try val.elemValue(mod, i);
  20128             const new_val = if (old_elem.isUndef(mod))
  20129                 try mod.undefValue(Type.u1)
  20130             else if (old_elem.toBool())
  20131                 try mod.intValue(Type.u1, 1)
  20132             else
  20133                 try mod.intValue(Type.u1, 0);
  20134             new_elem.* = new_val.toIntern();
  20135         }
  20136         return Air.internedToRef(try mod.intern(.{ .aggregate = .{
  20137             .ty = dest_ty.toIntern(),
  20138             .storage = .{ .elems = new_elems },
  20139         } }));
  20140     }
  20141     if (!is_vector) {
  20142         return block.addUnOp(.int_from_bool, operand);
  20143     }
  20144     const len = operand_ty.vectorLen(mod);
  20145     const dest_ty = try mod.vectorType(.{ .child = .u1_type, .len = len });
  20146     const new_elems = try sema.arena.alloc(Air.Inst.Ref, len);
  20147     for (new_elems, 0..) |*new_elem, i| {
  20148         const idx_ref = try mod.intRef(Type.usize, i);
  20149         const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref);
  20150         new_elem.* = try block.addUnOp(.int_from_bool, old_elem);
  20151     }
  20152     return block.addAggregateInit(dest_ty, new_elems);
  20153 }
  20154 
  20155 fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20156     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20157     const operand = try sema.resolveInst(inst_data.operand);
  20158     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20159 
  20160     if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
  20161         const err_name = sema.mod.intern_pool.indexToKey(val.toIntern()).err.name;
  20162         return sema.addStrLit(sema.mod.intern_pool.stringToSlice(err_name));
  20163     }
  20164 
  20165     // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass
  20166     // might be able to resolve the result at compile time.
  20167     return block.addUnOp(.error_name, operand);
  20168 }
  20169 
  20170 fn zirAbs(
  20171     sema: *Sema,
  20172     block: *Block,
  20173     inst: Zir.Inst.Index,
  20174 ) CompileError!Air.Inst.Ref {
  20175     const mod = sema.mod;
  20176     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20177     const operand = try sema.resolveInst(inst_data.operand);
  20178     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20179     const operand_ty = sema.typeOf(operand);
  20180     const scalar_ty = operand_ty.scalarType(mod);
  20181 
  20182     const result_ty = switch (scalar_ty.zigTypeTag(mod)) {
  20183         .ComptimeFloat, .Float, .ComptimeInt => operand_ty,
  20184         .Int => if (scalar_ty.isSignedInt(mod)) try operand_ty.toUnsigned(mod) else return operand,
  20185         else => return sema.fail(
  20186             block,
  20187             operand_src,
  20188             "expected integer, float, or vector of either integers or floats, found '{}'",
  20189             .{operand_ty.fmt(mod)},
  20190         ),
  20191     };
  20192 
  20193     return (try sema.maybeConstantUnaryMath(operand, result_ty, Value.abs)) orelse {
  20194         try sema.requireRuntimeBlock(block, operand_src, null);
  20195         return block.addTyOp(.abs, result_ty, operand);
  20196     };
  20197 }
  20198 
  20199 fn maybeConstantUnaryMath(
  20200     sema: *Sema,
  20201     operand: Air.Inst.Ref,
  20202     result_ty: Type,
  20203     comptime eval: fn (Value, Type, Allocator, *Module) Allocator.Error!Value,
  20204 ) CompileError!?Air.Inst.Ref {
  20205     const mod = sema.mod;
  20206     switch (result_ty.zigTypeTag(mod)) {
  20207         .Vector => if (try sema.resolveValue(operand)) |val| {
  20208             const scalar_ty = result_ty.scalarType(mod);
  20209             const vec_len = result_ty.vectorLen(mod);
  20210             if (val.isUndef(mod))
  20211                 return try mod.undefRef(result_ty);
  20212 
  20213             const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  20214             for (elems, 0..) |*elem, i| {
  20215                 const elem_val = try val.elemValue(sema.mod, i);
  20216                 elem.* = try (try eval(elem_val, scalar_ty, sema.arena, sema.mod)).intern(scalar_ty, mod);
  20217             }
  20218             return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  20219                 .ty = result_ty.toIntern(),
  20220                 .storage = .{ .elems = elems },
  20221             } })));
  20222         },
  20223         else => if (try sema.resolveValue(operand)) |operand_val| {
  20224             if (operand_val.isUndef(mod))
  20225                 return try mod.undefRef(result_ty);
  20226             const result_val = try eval(operand_val, result_ty, sema.arena, sema.mod);
  20227             return Air.internedToRef(result_val.toIntern());
  20228         },
  20229     }
  20230     return null;
  20231 }
  20232 
  20233 fn zirUnaryMath(
  20234     sema: *Sema,
  20235     block: *Block,
  20236     inst: Zir.Inst.Index,
  20237     air_tag: Air.Inst.Tag,
  20238     comptime eval: fn (Value, Type, Allocator, *Module) Allocator.Error!Value,
  20239 ) CompileError!Air.Inst.Ref {
  20240     const tracy = trace(@src());
  20241     defer tracy.end();
  20242 
  20243     const mod = sema.mod;
  20244     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20245     const operand = try sema.resolveInst(inst_data.operand);
  20246     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20247     const operand_ty = sema.typeOf(operand);
  20248     const scalar_ty = operand_ty.scalarType(mod);
  20249 
  20250     switch (scalar_ty.zigTypeTag(mod)) {
  20251         .ComptimeFloat, .Float => {},
  20252         else => return sema.fail(
  20253             block,
  20254             operand_src,
  20255             "expected vector of floats or float type, found '{}'",
  20256             .{operand_ty.fmt(sema.mod)},
  20257         ),
  20258     }
  20259 
  20260     return (try sema.maybeConstantUnaryMath(operand, operand_ty, eval)) orelse {
  20261         try sema.requireRuntimeBlock(block, operand_src, null);
  20262         return block.addUnOp(air_tag, operand);
  20263     };
  20264 }
  20265 
  20266 fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20267     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20268     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20269     const src = inst_data.src();
  20270     const operand = try sema.resolveInst(inst_data.operand);
  20271     const operand_ty = sema.typeOf(operand);
  20272     const mod = sema.mod;
  20273     const ip = &mod.intern_pool;
  20274 
  20275     try sema.resolveTypeLayout(operand_ty);
  20276     const enum_ty = switch (operand_ty.zigTypeTag(mod)) {
  20277         .EnumLiteral => {
  20278             const val = try sema.resolveConstDefinedValue(block, .unneeded, operand, undefined);
  20279             const tag_name = ip.indexToKey(val.toIntern()).enum_literal;
  20280             return sema.addStrLit(ip.stringToSlice(tag_name));
  20281         },
  20282         .Enum => operand_ty,
  20283         .Union => operand_ty.unionTagType(mod) orelse {
  20284             const msg = msg: {
  20285                 const msg = try sema.errMsg(block, src, "union '{}' is untagged", .{
  20286                     operand_ty.fmt(sema.mod),
  20287                 });
  20288                 errdefer msg.destroy(sema.gpa);
  20289                 try sema.addDeclaredHereNote(msg, operand_ty);
  20290                 break :msg msg;
  20291             };
  20292             return sema.failWithOwnedErrorMsg(block, msg);
  20293         },
  20294         else => return sema.fail(block, operand_src, "expected enum or union; found '{}'", .{
  20295             operand_ty.fmt(mod),
  20296         }),
  20297     };
  20298     if (enum_ty.enumFieldCount(mod) == 0) {
  20299         // TODO I don't think this is the correct way to handle this but
  20300         // it prevents a crash.
  20301         return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{}'", .{
  20302             enum_ty.fmt(mod),
  20303         });
  20304     }
  20305     const enum_decl_index = enum_ty.getOwnerDecl(mod);
  20306     const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);
  20307     if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {
  20308         const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse {
  20309             const enum_decl = mod.declPtr(enum_decl_index);
  20310             const msg = msg: {
  20311                 const msg = try sema.errMsg(block, src, "no field with value '{}' in enum '{}'", .{
  20312                     val.fmtValue(enum_ty, sema.mod), enum_decl.name.fmt(ip),
  20313                 });
  20314                 errdefer msg.destroy(sema.gpa);
  20315                 try mod.errNoteNonLazy(enum_decl.srcLoc(mod), msg, "declared here", .{});
  20316                 break :msg msg;
  20317             };
  20318             return sema.failWithOwnedErrorMsg(block, msg);
  20319         };
  20320         // TODO: write something like getCoercedInts to avoid needing to dupe
  20321         const field_name = enum_ty.enumFieldName(field_index, mod);
  20322         return sema.addStrLit(ip.stringToSlice(field_name));
  20323     }
  20324     try sema.requireRuntimeBlock(block, src, operand_src);
  20325     if (block.wantSafety() and sema.mod.backendSupportsFeature(.is_named_enum_value)) {
  20326         const ok = try block.addUnOp(.is_named_enum_value, casted_operand);
  20327         try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
  20328     }
  20329     // In case the value is runtime-known, we have an AIR instruction for this instead
  20330     // of trying to lower it in Sema because an optimization pass may result in the operand
  20331     // being comptime-known, which would let us elide the `tag_name` AIR instruction.
  20332     return block.addUnOp(.tag_name, casted_operand);
  20333 }
  20334 
  20335 fn zirReify(
  20336     sema: *Sema,
  20337     block: *Block,
  20338     extended: Zir.Inst.Extended.InstData,
  20339     inst: Zir.Inst.Index,
  20340 ) CompileError!Air.Inst.Ref {
  20341     const mod = sema.mod;
  20342     const gpa = sema.gpa;
  20343     const ip = &mod.intern_pool;
  20344     const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small);
  20345     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  20346     const src = LazySrcLoc.nodeOffset(extra.node);
  20347     const type_info_ty = try sema.getBuiltinType("Type");
  20348     const uncasted_operand = try sema.resolveInst(extra.operand);
  20349     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  20350     const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
  20351     const val = try sema.resolveConstDefinedValue(block, operand_src, type_info, .{
  20352         .needed_comptime_reason = "operand to @Type must be comptime-known",
  20353     });
  20354     const union_val = ip.indexToKey(val.toIntern()).un;
  20355     const target = mod.getTarget();
  20356     if (try Value.fromInterned(union_val.val).anyUndef(mod)) return sema.failWithUseOfUndef(block, src);
  20357     const tag_index = type_info_ty.unionTagFieldIndex(Value.fromInterned(union_val.tag), mod).?;
  20358     switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) {
  20359         .Type => return .type_type,
  20360         .Void => return .void_type,
  20361         .Bool => return .bool_type,
  20362         .NoReturn => return .noreturn_type,
  20363         .ComptimeFloat => return .comptime_float_type,
  20364         .ComptimeInt => return .comptime_int_type,
  20365         .Undefined => return .undefined_type,
  20366         .Null => return .null_type,
  20367         .AnyFrame => return sema.failWithUseOfAsync(block, src),
  20368         .EnumLiteral => return .enum_literal_type,
  20369         .Int => {
  20370             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  20371             const signedness_val = try Value.fromInterned(union_val.val).fieldValue(
  20372                 mod,
  20373                 struct_type.nameIndex(ip, try ip.getOrPutString(gpa, "signedness")).?,
  20374             );
  20375             const bits_val = try Value.fromInterned(union_val.val).fieldValue(
  20376                 mod,
  20377                 struct_type.nameIndex(ip, try ip.getOrPutString(gpa, "bits")).?,
  20378             );
  20379 
  20380             const signedness = mod.toEnum(std.builtin.Signedness, signedness_val);
  20381             const bits: u16 = @intCast(bits_val.toUnsignedInt(mod));
  20382             const ty = try mod.intType(signedness, bits);
  20383             return Air.internedToRef(ty.toIntern());
  20384         },
  20385         .Vector => {
  20386             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  20387             const len_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20388                 ip,
  20389                 try ip.getOrPutString(gpa, "len"),
  20390             ).?);
  20391             const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20392                 ip,
  20393                 try ip.getOrPutString(gpa, "child"),
  20394             ).?);
  20395 
  20396             const len: u32 = @intCast(len_val.toUnsignedInt(mod));
  20397             const child_ty = child_val.toType();
  20398 
  20399             try sema.checkVectorElemType(block, src, child_ty);
  20400 
  20401             const ty = try mod.vectorType(.{
  20402                 .len = len,
  20403                 .child = child_ty.toIntern(),
  20404             });
  20405             return Air.internedToRef(ty.toIntern());
  20406         },
  20407         .Float => {
  20408             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  20409             const bits_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20410                 ip,
  20411                 try ip.getOrPutString(gpa, "bits"),
  20412             ).?);
  20413 
  20414             const bits: u16 = @intCast(bits_val.toUnsignedInt(mod));
  20415             const ty = switch (bits) {
  20416                 16 => Type.f16,
  20417                 32 => Type.f32,
  20418                 64 => Type.f64,
  20419                 80 => Type.f80,
  20420                 128 => Type.f128,
  20421                 else => return sema.fail(block, src, "{}-bit float unsupported", .{bits}),
  20422             };
  20423             return Air.internedToRef(ty.toIntern());
  20424         },
  20425         .Pointer => {
  20426             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  20427             const size_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20428                 ip,
  20429                 try ip.getOrPutString(gpa, "size"),
  20430             ).?);
  20431             const is_const_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20432                 ip,
  20433                 try ip.getOrPutString(gpa, "is_const"),
  20434             ).?);
  20435             const is_volatile_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20436                 ip,
  20437                 try ip.getOrPutString(gpa, "is_volatile"),
  20438             ).?);
  20439             const alignment_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20440                 ip,
  20441                 try ip.getOrPutString(gpa, "alignment"),
  20442             ).?);
  20443             const address_space_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20444                 ip,
  20445                 try ip.getOrPutString(gpa, "address_space"),
  20446             ).?);
  20447             const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20448                 ip,
  20449                 try ip.getOrPutString(gpa, "child"),
  20450             ).?);
  20451             const is_allowzero_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20452                 ip,
  20453                 try ip.getOrPutString(gpa, "is_allowzero"),
  20454             ).?);
  20455             const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20456                 ip,
  20457                 try ip.getOrPutString(gpa, "sentinel"),
  20458             ).?);
  20459 
  20460             if (!try sema.intFitsInType(alignment_val, Type.u32, null)) {
  20461                 return sema.fail(block, src, "alignment must fit in 'u32'", .{});
  20462             }
  20463 
  20464             const alignment_val_int = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?;
  20465             if (alignment_val_int > 0 and !math.isPowerOfTwo(alignment_val_int)) {
  20466                 return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{alignment_val_int});
  20467             }
  20468             const abi_align = Alignment.fromByteUnits(alignment_val_int);
  20469 
  20470             const elem_ty = child_val.toType();
  20471             if (abi_align != .none) {
  20472                 try sema.resolveTypeLayout(elem_ty);
  20473             }
  20474 
  20475             const ptr_size = mod.toEnum(std.builtin.Type.Pointer.Size, size_val);
  20476 
  20477             const actual_sentinel: InternPool.Index = s: {
  20478                 if (!sentinel_val.isNull(mod)) {
  20479                     if (ptr_size == .One or ptr_size == .C) {
  20480                         return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{});
  20481                     }
  20482                     const sentinel_ptr_val = sentinel_val.optionalValue(mod).?;
  20483                     const ptr_ty = try mod.singleMutPtrType(elem_ty);
  20484                     const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?;
  20485                     break :s sent_val.toIntern();
  20486                 }
  20487                 break :s .none;
  20488             };
  20489 
  20490             if (elem_ty.zigTypeTag(mod) == .NoReturn) {
  20491                 return sema.fail(block, src, "pointer to noreturn not allowed", .{});
  20492             } else if (elem_ty.zigTypeTag(mod) == .Fn) {
  20493                 if (ptr_size != .One) {
  20494                     return sema.fail(block, src, "function pointers must be single pointers", .{});
  20495                 }
  20496                 const fn_align = mod.typeToFunc(elem_ty).?.alignment;
  20497                 if (abi_align != .none and fn_align != .none and
  20498                     abi_align != fn_align)
  20499                 {
  20500                     return sema.fail(block, src, "function pointer alignment disagrees with function alignment", .{});
  20501                 }
  20502             } else if (ptr_size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) {
  20503                 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});
  20504             } else if (ptr_size == .C) {
  20505                 if (!try sema.validateExternType(elem_ty, .other)) {
  20506                     const msg = msg: {
  20507                         const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)});
  20508                         errdefer msg.destroy(gpa);
  20509 
  20510                         const src_decl = mod.declPtr(block.src_decl);
  20511                         try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), elem_ty, .other);
  20512 
  20513                         try sema.addDeclaredHereNote(msg, elem_ty);
  20514                         break :msg msg;
  20515                     };
  20516                     return sema.failWithOwnedErrorMsg(block, msg);
  20517                 }
  20518                 if (elem_ty.zigTypeTag(mod) == .Opaque) {
  20519                     return sema.fail(block, src, "C pointers cannot point to opaque types", .{});
  20520                 }
  20521             }
  20522 
  20523             const ty = try sema.ptrType(.{
  20524                 .child = elem_ty.toIntern(),
  20525                 .sentinel = actual_sentinel,
  20526                 .flags = .{
  20527                     .size = ptr_size,
  20528                     .is_const = is_const_val.toBool(),
  20529                     .is_volatile = is_volatile_val.toBool(),
  20530                     .alignment = abi_align,
  20531                     .address_space = mod.toEnum(std.builtin.AddressSpace, address_space_val),
  20532                     .is_allowzero = is_allowzero_val.toBool(),
  20533                 },
  20534             });
  20535             return Air.internedToRef(ty.toIntern());
  20536         },
  20537         .Array => {
  20538             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  20539             const len_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20540                 ip,
  20541                 try ip.getOrPutString(gpa, "len"),
  20542             ).?);
  20543             const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20544                 ip,
  20545                 try ip.getOrPutString(gpa, "child"),
  20546             ).?);
  20547             const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20548                 ip,
  20549                 try ip.getOrPutString(gpa, "sentinel"),
  20550             ).?);
  20551 
  20552             const len = len_val.toUnsignedInt(mod);
  20553             const child_ty = child_val.toType();
  20554             const sentinel = if (sentinel_val.optionalValue(mod)) |p| blk: {
  20555                 const ptr_ty = try mod.singleMutPtrType(child_ty);
  20556                 break :blk (try sema.pointerDeref(block, src, p, ptr_ty)).?;
  20557             } else null;
  20558 
  20559             const ty = try mod.arrayType(.{
  20560                 .len = len,
  20561                 .sentinel = if (sentinel) |s| s.toIntern() else .none,
  20562                 .child = child_ty.toIntern(),
  20563             });
  20564             return Air.internedToRef(ty.toIntern());
  20565         },
  20566         .Optional => {
  20567             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  20568             const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20569                 ip,
  20570                 try ip.getOrPutString(gpa, "child"),
  20571             ).?);
  20572 
  20573             const child_ty = child_val.toType();
  20574 
  20575             const ty = try mod.optionalType(child_ty.toIntern());
  20576             return Air.internedToRef(ty.toIntern());
  20577         },
  20578         .ErrorUnion => {
  20579             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  20580             const error_set_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20581                 ip,
  20582                 try ip.getOrPutString(gpa, "error_set"),
  20583             ).?);
  20584             const payload_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20585                 ip,
  20586                 try ip.getOrPutString(gpa, "payload"),
  20587             ).?);
  20588 
  20589             const error_set_ty = error_set_val.toType();
  20590             const payload_ty = payload_val.toType();
  20591 
  20592             if (error_set_ty.zigTypeTag(mod) != .ErrorSet) {
  20593                 return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{});
  20594             }
  20595 
  20596             const ty = try mod.errorUnionType(error_set_ty, payload_ty);
  20597             return Air.internedToRef(ty.toIntern());
  20598         },
  20599         .ErrorSet => {
  20600             const payload_val = Value.fromInterned(union_val.val).optionalValue(mod) orelse
  20601                 return Air.internedToRef(Type.anyerror.toIntern());
  20602 
  20603             const len = try sema.usizeCast(block, src, payload_val.sliceLen(mod));
  20604             var names: InferredErrorSet.NameMap = .{};
  20605             try names.ensureUnusedCapacity(sema.arena, len);
  20606             for (0..len) |i| {
  20607                 const elem_val = try payload_val.elemValue(mod, i);
  20608                 const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type;
  20609                 const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  20610                     ip,
  20611                     try ip.getOrPutString(gpa, "name"),
  20612                 ).?);
  20613 
  20614                 const name = try name_val.toIpString(Type.slice_const_u8, mod);
  20615                 _ = try mod.getErrorValue(name);
  20616                 const gop = names.getOrPutAssumeCapacity(name);
  20617                 if (gop.found_existing) {
  20618                     return sema.fail(block, src, "duplicate error '{}'", .{
  20619                         name.fmt(ip),
  20620                     });
  20621                 }
  20622             }
  20623 
  20624             const ty = try mod.errorSetFromUnsortedNames(names.keys());
  20625             return Air.internedToRef(ty.toIntern());
  20626         },
  20627         .Struct => {
  20628             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  20629             const layout_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20630                 ip,
  20631                 try ip.getOrPutString(gpa, "layout"),
  20632             ).?);
  20633             const backing_integer_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20634                 ip,
  20635                 try ip.getOrPutString(gpa, "backing_integer"),
  20636             ).?);
  20637             const fields_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20638                 ip,
  20639                 try ip.getOrPutString(gpa, "fields"),
  20640             ).?);
  20641             const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20642                 ip,
  20643                 try ip.getOrPutString(gpa, "decls"),
  20644             ).?);
  20645             const is_tuple_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20646                 ip,
  20647                 try ip.getOrPutString(gpa, "is_tuple"),
  20648             ).?);
  20649 
  20650             const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val);
  20651 
  20652             // Decls
  20653             if (decls_val.sliceLen(mod) > 0) {
  20654                 return sema.fail(block, src, "reified structs must have no decls", .{});
  20655             }
  20656 
  20657             if (layout != .Packed and !backing_integer_val.isNull(mod)) {
  20658                 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{});
  20659             }
  20660 
  20661             return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_val, name_strategy, is_tuple_val.toBool());
  20662         },
  20663         .Enum => {
  20664             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  20665             const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20666                 ip,
  20667                 try ip.getOrPutString(gpa, "tag_type"),
  20668             ).?);
  20669             const fields_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20670                 ip,
  20671                 try ip.getOrPutString(gpa, "fields"),
  20672             ).?);
  20673             const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20674                 ip,
  20675                 try ip.getOrPutString(gpa, "decls"),
  20676             ).?);
  20677             const is_exhaustive_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20678                 ip,
  20679                 try ip.getOrPutString(gpa, "is_exhaustive"),
  20680             ).?);
  20681 
  20682             // Decls
  20683             if (decls_val.sliceLen(mod) > 0) {
  20684                 return sema.fail(block, src, "reified enums must have no decls", .{});
  20685             }
  20686 
  20687             const int_tag_ty = tag_type_val.toType();
  20688             if (int_tag_ty.zigTypeTag(mod) != .Int) {
  20689                 return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{});
  20690             }
  20691 
  20692             // Because these things each reference each other, `undefined`
  20693             // placeholders are used before being set after the enum type gains
  20694             // an InternPool index.
  20695 
  20696             const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  20697                 .ty = Type.noreturn,
  20698                 .val = Value.@"unreachable",
  20699             }, name_strategy, "enum", inst);
  20700             const new_decl = mod.declPtr(new_decl_index);
  20701             new_decl.owns_tv = true;
  20702             errdefer {
  20703                 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  20704                 mod.abortAnonDecl(new_decl_index);
  20705             }
  20706 
  20707             // Define our empty enum decl
  20708             const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod)));
  20709             const incomplete_enum = try ip.getIncompleteEnum(gpa, .{
  20710                 .decl = new_decl_index,
  20711                 .namespace = .none,
  20712                 .fields_len = fields_len,
  20713                 .has_values = true,
  20714                 .tag_mode = if (!is_exhaustive_val.toBool())
  20715                     .nonexhaustive
  20716                 else
  20717                     .explicit,
  20718                 .tag_ty = int_tag_ty.toIntern(),
  20719             });
  20720             // TODO: figure out InternPool removals for incremental compilation
  20721             //errdefer ip.remove(incomplete_enum.index);
  20722 
  20723             new_decl.ty = Type.type;
  20724             new_decl.val = Value.fromInterned(incomplete_enum.index);
  20725 
  20726             for (0..fields_len) |field_i| {
  20727                 const elem_val = try fields_val.elemValue(mod, field_i);
  20728                 const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type;
  20729                 const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  20730                     ip,
  20731                     try ip.getOrPutString(gpa, "name"),
  20732                 ).?);
  20733                 const value_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  20734                     ip,
  20735                     try ip.getOrPutString(gpa, "value"),
  20736                 ).?);
  20737 
  20738                 const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
  20739 
  20740                 if (!try sema.intFitsInType(value_val, int_tag_ty, null)) {
  20741                     // TODO: better source location
  20742                     return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{
  20743                         field_name.fmt(ip),
  20744                         value_val.fmtValue(Type.comptime_int, mod),
  20745                         int_tag_ty.fmt(mod),
  20746                     });
  20747                 }
  20748 
  20749                 if (incomplete_enum.addFieldName(ip, field_name)) |other_index| {
  20750                     const msg = msg: {
  20751                         const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{
  20752                             field_name.fmt(ip),
  20753                         });
  20754                         errdefer msg.destroy(gpa);
  20755                         _ = other_index; // TODO: this note is incorrect
  20756                         try sema.errNote(block, src, msg, "other field here", .{});
  20757                         break :msg msg;
  20758                     };
  20759                     return sema.failWithOwnedErrorMsg(block, msg);
  20760                 }
  20761 
  20762                 if (incomplete_enum.addFieldValue(ip, (try mod.getCoerced(value_val, int_tag_ty)).toIntern())) |other| {
  20763                     const msg = msg: {
  20764                         const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{value_val.fmtValue(Type.comptime_int, mod)});
  20765                         errdefer msg.destroy(gpa);
  20766                         _ = other; // TODO: this note is incorrect
  20767                         try sema.errNote(block, src, msg, "other enum tag value here", .{});
  20768                         break :msg msg;
  20769                     };
  20770                     return sema.failWithOwnedErrorMsg(block, msg);
  20771                 }
  20772             }
  20773 
  20774             const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  20775             try mod.finalizeAnonDecl(new_decl_index);
  20776             return decl_val;
  20777         },
  20778         .Opaque => {
  20779             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  20780             const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20781                 ip,
  20782                 try ip.getOrPutString(gpa, "decls"),
  20783             ).?);
  20784 
  20785             // Decls
  20786             if (decls_val.sliceLen(mod) > 0) {
  20787                 return sema.fail(block, src, "reified opaque must have no decls", .{});
  20788             }
  20789 
  20790             // Because these three things each reference each other,
  20791             // `undefined` placeholders are used in two places before being set
  20792             // after the opaque type gains an InternPool index.
  20793 
  20794             const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  20795                 .ty = Type.noreturn,
  20796                 .val = Value.@"unreachable",
  20797             }, name_strategy, "opaque", inst);
  20798             const new_decl = mod.declPtr(new_decl_index);
  20799             new_decl.owns_tv = true;
  20800             errdefer {
  20801                 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  20802                 mod.abortAnonDecl(new_decl_index);
  20803             }
  20804 
  20805             const new_namespace_index = try mod.createNamespace(.{
  20806                 .parent = block.namespace.toOptional(),
  20807                 .ty = undefined,
  20808                 .file_scope = block.getFileScope(mod),
  20809             });
  20810             const new_namespace = mod.namespacePtr(new_namespace_index);
  20811             errdefer mod.destroyNamespace(new_namespace_index);
  20812 
  20813             const opaque_ty = try mod.intern(.{ .opaque_type = .{
  20814                 .decl = new_decl_index,
  20815                 .namespace = new_namespace_index,
  20816             } });
  20817             // TODO: figure out InternPool removals for incremental compilation
  20818             //errdefer ip.remove(opaque_ty);
  20819 
  20820             new_decl.ty = Type.type;
  20821             new_decl.val = Value.fromInterned(opaque_ty);
  20822             new_namespace.ty = Type.fromInterned(opaque_ty);
  20823 
  20824             const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  20825             try mod.finalizeAnonDecl(new_decl_index);
  20826             return decl_val;
  20827         },
  20828         .Union => {
  20829             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  20830             const layout_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20831                 ip,
  20832                 try ip.getOrPutString(gpa, "layout"),
  20833             ).?);
  20834             const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20835                 ip,
  20836                 try ip.getOrPutString(gpa, "tag_type"),
  20837             ).?);
  20838             const fields_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20839                 ip,
  20840                 try ip.getOrPutString(gpa, "fields"),
  20841             ).?);
  20842             const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  20843                 ip,
  20844                 try ip.getOrPutString(gpa, "decls"),
  20845             ).?);
  20846 
  20847             // Decls
  20848             if (decls_val.sliceLen(mod) > 0) {
  20849                 return sema.fail(block, src, "reified unions must have no decls", .{});
  20850             }
  20851             const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val);
  20852             const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod)));
  20853 
  20854             // Tag type
  20855             var explicit_tags_seen: []bool = &.{};
  20856             var enum_field_names: []InternPool.NullTerminatedString = &.{};
  20857             var enum_tag_ty: InternPool.Index = .none;
  20858             if (tag_type_val.optionalValue(mod)) |payload_val| {
  20859                 enum_tag_ty = payload_val.toType().toIntern();
  20860 
  20861                 const enum_type = switch (ip.indexToKey(enum_tag_ty)) {
  20862                     .enum_type => |x| x,
  20863                     else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}),
  20864                 };
  20865 
  20866                 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
  20867                 @memset(explicit_tags_seen, false);
  20868             } else {
  20869                 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  20870             }
  20871 
  20872             // Fields
  20873             var any_aligned_fields: bool = false;
  20874             var union_fields: std.MultiArrayList(struct {
  20875                 type: InternPool.Index,
  20876                 alignment: InternPool.Alignment,
  20877             }) = .{};
  20878             var field_name_table: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
  20879             try field_name_table.ensureTotalCapacity(sema.arena, fields_len);
  20880 
  20881             for (0..fields_len) |i| {
  20882                 const elem_val = try fields_val.elemValue(mod, i);
  20883                 const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type;
  20884                 const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  20885                     ip,
  20886                     try ip.getOrPutString(gpa, "name"),
  20887                 ).?);
  20888                 const type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  20889                     ip,
  20890                     try ip.getOrPutString(gpa, "type"),
  20891                 ).?);
  20892                 const alignment_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  20893                     ip,
  20894                     try ip.getOrPutString(gpa, "alignment"),
  20895                 ).?);
  20896 
  20897                 const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
  20898 
  20899                 if (enum_field_names.len != 0) {
  20900                     enum_field_names[i] = field_name;
  20901                 }
  20902 
  20903                 if (explicit_tags_seen.len > 0) {
  20904                     const tag_info = ip.indexToKey(enum_tag_ty).enum_type;
  20905                     const enum_index = tag_info.nameIndex(ip, field_name) orelse {
  20906                         const msg = msg: {
  20907                             const msg = try sema.errMsg(block, src, "no field named '{}' in enum '{}'", .{
  20908                                 field_name.fmt(ip),
  20909                                 Type.fromInterned(enum_tag_ty).fmt(mod),
  20910                             });
  20911                             errdefer msg.destroy(gpa);
  20912                             try sema.addDeclaredHereNote(msg, Type.fromInterned(enum_tag_ty));
  20913                             break :msg msg;
  20914                         };
  20915                         return sema.failWithOwnedErrorMsg(block, msg);
  20916                     };
  20917                     // No check for duplicate because the check already happened in order
  20918                     // to create the enum type in the first place.
  20919                     assert(!explicit_tags_seen[enum_index]);
  20920                     explicit_tags_seen[enum_index] = true;
  20921                 }
  20922 
  20923                 const gop = field_name_table.getOrPutAssumeCapacity(field_name);
  20924                 if (gop.found_existing) {
  20925                     // TODO: better source location
  20926                     return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)});
  20927                 }
  20928 
  20929                 const field_ty = type_val.toType();
  20930                 const alignment_val_int = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?;
  20931                 if (alignment_val_int > 0 and !math.isPowerOfTwo(alignment_val_int)) {
  20932                     // TODO: better source location
  20933                     return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{
  20934                         alignment_val_int,
  20935                     });
  20936                 }
  20937                 const field_align = Alignment.fromByteUnits(alignment_val_int);
  20938                 any_aligned_fields = any_aligned_fields or field_align != .none;
  20939 
  20940                 try union_fields.append(sema.arena, .{
  20941                     .type = field_ty.toIntern(),
  20942                     .alignment = field_align,
  20943                 });
  20944 
  20945                 if (field_ty.zigTypeTag(mod) == .Opaque) {
  20946                     const msg = msg: {
  20947                         const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
  20948                         errdefer msg.destroy(gpa);
  20949 
  20950                         try sema.addDeclaredHereNote(msg, field_ty);
  20951                         break :msg msg;
  20952                     };
  20953                     return sema.failWithOwnedErrorMsg(block, msg);
  20954                 }
  20955                 if (layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) {
  20956                     const msg = msg: {
  20957                         const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  20958                         errdefer msg.destroy(gpa);
  20959 
  20960                         const src_decl = mod.declPtr(block.src_decl);
  20961                         try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field);
  20962 
  20963                         try sema.addDeclaredHereNote(msg, field_ty);
  20964                         break :msg msg;
  20965                     };
  20966                     return sema.failWithOwnedErrorMsg(block, msg);
  20967                 } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) {
  20968                     const msg = msg: {
  20969                         const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  20970                         errdefer msg.destroy(gpa);
  20971 
  20972                         const src_decl = mod.declPtr(block.src_decl);
  20973                         try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty);
  20974 
  20975                         try sema.addDeclaredHereNote(msg, field_ty);
  20976                         break :msg msg;
  20977                     };
  20978                     return sema.failWithOwnedErrorMsg(block, msg);
  20979                 }
  20980             }
  20981 
  20982             if (explicit_tags_seen.len > 0) {
  20983                 const tag_info = ip.indexToKey(enum_tag_ty).enum_type;
  20984                 if (tag_info.names.len > fields_len) {
  20985                     const msg = msg: {
  20986                         const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
  20987                         errdefer msg.destroy(gpa);
  20988 
  20989                         for (tag_info.names.get(ip), 0..) |field_name, field_index| {
  20990                             if (explicit_tags_seen[field_index]) continue;
  20991                             try sema.addFieldErrNote(Type.fromInterned(enum_tag_ty), field_index, msg, "field '{}' missing, declared here", .{
  20992                                 field_name.fmt(ip),
  20993                             });
  20994                         }
  20995                         try sema.addDeclaredHereNote(msg, Type.fromInterned(enum_tag_ty));
  20996                         break :msg msg;
  20997                     };
  20998                     return sema.failWithOwnedErrorMsg(block, msg);
  20999                 }
  21000             } else {
  21001                 enum_tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, .none);
  21002             }
  21003 
  21004             // Because these three things each reference each other, `undefined`
  21005             // placeholders are used before being set after the union type gains an
  21006             // InternPool index.
  21007 
  21008             const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  21009                 .ty = Type.noreturn,
  21010                 .val = Value.@"unreachable",
  21011             }, name_strategy, "union", inst);
  21012             const new_decl = mod.declPtr(new_decl_index);
  21013             new_decl.owns_tv = true;
  21014             errdefer {
  21015                 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  21016                 mod.abortAnonDecl(new_decl_index);
  21017             }
  21018 
  21019             const new_namespace_index = try mod.createNamespace(.{
  21020                 .parent = block.namespace.toOptional(),
  21021                 .ty = undefined,
  21022                 .file_scope = block.getFileScope(mod),
  21023             });
  21024             const new_namespace = mod.namespacePtr(new_namespace_index);
  21025             errdefer mod.destroyNamespace(new_namespace_index);
  21026 
  21027             const union_ty = try ip.getUnionType(gpa, .{
  21028                 .decl = new_decl_index,
  21029                 .namespace = new_namespace_index,
  21030                 .enum_tag_ty = enum_tag_ty,
  21031                 .fields_len = fields_len,
  21032                 .zir_index = inst,
  21033                 .flags = .{
  21034                     .layout = layout,
  21035                     .status = .have_field_types,
  21036                     .runtime_tag = if (!tag_type_val.isNull(mod))
  21037                         .tagged
  21038                     else if (layout != .Auto)
  21039                         .none
  21040                     else switch (block.wantSafety()) {
  21041                         true => .safety,
  21042                         false => .none,
  21043                     },
  21044                     .any_aligned_fields = any_aligned_fields,
  21045                     .requires_comptime = .unknown,
  21046                     .assumed_runtime_bits = false,
  21047                     .assumed_pointer_aligned = false,
  21048                     .alignment = .none,
  21049                 },
  21050                 .field_types = union_fields.items(.type),
  21051                 .field_aligns = if (any_aligned_fields) union_fields.items(.alignment) else &.{},
  21052             });
  21053 
  21054             new_decl.ty = Type.type;
  21055             new_decl.val = Value.fromInterned(union_ty);
  21056             new_namespace.ty = Type.fromInterned(union_ty);
  21057 
  21058             const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  21059             try mod.finalizeAnonDecl(new_decl_index);
  21060             return decl_val;
  21061         },
  21062         .Fn => {
  21063             const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type;
  21064             const calling_convention_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  21065                 ip,
  21066                 try ip.getOrPutString(gpa, "calling_convention"),
  21067             ).?);
  21068             const alignment_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  21069                 ip,
  21070                 try ip.getOrPutString(gpa, "alignment"),
  21071             ).?);
  21072             const is_generic_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  21073                 ip,
  21074                 try ip.getOrPutString(gpa, "is_generic"),
  21075             ).?);
  21076             const is_var_args_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  21077                 ip,
  21078                 try ip.getOrPutString(gpa, "is_var_args"),
  21079             ).?);
  21080             const return_type_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  21081                 ip,
  21082                 try ip.getOrPutString(gpa, "return_type"),
  21083             ).?);
  21084             const params_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex(
  21085                 ip,
  21086                 try ip.getOrPutString(gpa, "params"),
  21087             ).?);
  21088 
  21089             const is_generic = is_generic_val.toBool();
  21090             if (is_generic) {
  21091                 return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{});
  21092             }
  21093 
  21094             const is_var_args = is_var_args_val.toBool();
  21095             const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val);
  21096             if (is_var_args) {
  21097                 try sema.checkCallConvSupportsVarArgs(block, src, cc);
  21098             }
  21099 
  21100             const alignment = alignment: {
  21101                 const alignment = try sema.validateAlignAllowZero(block, src, alignment_val.toUnsignedInt(mod));
  21102                 const default = target_util.defaultFunctionAlignment(target);
  21103                 break :alignment if (alignment == default) .none else alignment;
  21104             };
  21105             const return_type = return_type_val.optionalValue(mod) orelse
  21106                 return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{});
  21107 
  21108             const args_len = try sema.usizeCast(block, src, params_val.sliceLen(mod));
  21109             const param_types = try sema.arena.alloc(InternPool.Index, args_len);
  21110 
  21111             var noalias_bits: u32 = 0;
  21112             for (param_types, 0..) |*param_type, i| {
  21113                 const elem_val = try params_val.elemValue(mod, i);
  21114                 const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type;
  21115                 const param_is_generic_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  21116                     ip,
  21117                     try ip.getOrPutString(gpa, "is_generic"),
  21118                 ).?);
  21119                 const param_is_noalias_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  21120                     ip,
  21121                     try ip.getOrPutString(gpa, "is_noalias"),
  21122                 ).?);
  21123                 const opt_param_type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  21124                     ip,
  21125                     try ip.getOrPutString(gpa, "type"),
  21126                 ).?);
  21127 
  21128                 if (param_is_generic_val.toBool()) {
  21129                     return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{});
  21130                 }
  21131 
  21132                 const param_type_val = opt_param_type_val.optionalValue(mod) orelse
  21133                     return sema.fail(block, src, "Type.Fn.Param.type must be non-null for @Type", .{});
  21134                 param_type.* = param_type_val.toIntern();
  21135 
  21136                 if (param_is_noalias_val.toBool()) {
  21137                     if (!Type.fromInterned(param_type.*).isPtrAtRuntime(mod)) {
  21138                         return sema.fail(block, src, "non-pointer parameter declared noalias", .{});
  21139                     }
  21140                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, i) orelse
  21141                         return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
  21142                 }
  21143             }
  21144 
  21145             const ty = try mod.funcType(.{
  21146                 .param_types = param_types,
  21147                 .noalias_bits = noalias_bits,
  21148                 .return_type = return_type.toIntern(),
  21149                 .alignment = alignment,
  21150                 .cc = cc,
  21151                 .is_var_args = is_var_args,
  21152             });
  21153             return Air.internedToRef(ty.toIntern());
  21154         },
  21155         .Frame => return sema.failWithUseOfAsync(block, src),
  21156     }
  21157 }
  21158 
  21159 fn reifyStruct(
  21160     sema: *Sema,
  21161     block: *Block,
  21162     inst: Zir.Inst.Index,
  21163     src: LazySrcLoc,
  21164     layout: std.builtin.Type.ContainerLayout,
  21165     backing_int_val: Value,
  21166     fields_val: Value,
  21167     name_strategy: Zir.Inst.NameStrategy,
  21168     is_tuple: bool,
  21169 ) CompileError!Air.Inst.Ref {
  21170     const mod = sema.mod;
  21171     const gpa = sema.gpa;
  21172     const ip = &mod.intern_pool;
  21173 
  21174     if (is_tuple) switch (layout) {
  21175         .Extern => return sema.fail(block, src, "extern tuples are not supported", .{}),
  21176         .Packed => return sema.fail(block, src, "packed tuples are not supported", .{}),
  21177         .Auto => {},
  21178     };
  21179 
  21180     const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod)));
  21181 
  21182     // Because these three things each reference each other, `undefined`
  21183     // placeholders are used before being set after the struct type gains an
  21184     // InternPool index.
  21185 
  21186     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  21187         .ty = Type.noreturn,
  21188         .val = Value.@"unreachable",
  21189     }, name_strategy, "struct", inst);
  21190     const new_decl = mod.declPtr(new_decl_index);
  21191     new_decl.owns_tv = true;
  21192     errdefer {
  21193         new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  21194         mod.abortAnonDecl(new_decl_index);
  21195     }
  21196 
  21197     const ty = try ip.getStructType(gpa, .{
  21198         .decl = new_decl_index,
  21199         .namespace = .none,
  21200         .zir_index = inst,
  21201         .layout = layout,
  21202         .known_non_opv = false,
  21203         .fields_len = fields_len,
  21204         .requires_comptime = .unknown,
  21205         .is_tuple = is_tuple,
  21206         // So that we don't have to scan ahead, we allocate space in the struct
  21207         // type for alignments, comptime fields, and default inits. This might
  21208         // result in wasted space, however, this is a permitted encoding of
  21209         // struct types.
  21210         .any_comptime_fields = true,
  21211         .any_default_inits = true,
  21212         .inits_resolved = true,
  21213         .any_aligned_fields = true,
  21214     });
  21215     // TODO: figure out InternPool removals for incremental compilation
  21216     //errdefer ip.remove(ty);
  21217     const struct_type = ip.indexToKey(ty).struct_type;
  21218 
  21219     new_decl.ty = Type.type;
  21220     new_decl.val = Value.fromInterned(ty);
  21221 
  21222     // Fields
  21223     for (0..fields_len) |i| {
  21224         const elem_val = try fields_val.elemValue(mod, i);
  21225         const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type;
  21226         const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  21227             ip,
  21228             try ip.getOrPutString(gpa, "name"),
  21229         ).?);
  21230         const type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  21231             ip,
  21232             try ip.getOrPutString(gpa, "type"),
  21233         ).?);
  21234         const default_value_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  21235             ip,
  21236             try ip.getOrPutString(gpa, "default_value"),
  21237         ).?);
  21238         const is_comptime_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  21239             ip,
  21240             try ip.getOrPutString(gpa, "is_comptime"),
  21241         ).?);
  21242         const alignment_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
  21243             ip,
  21244             try ip.getOrPutString(gpa, "alignment"),
  21245         ).?);
  21246 
  21247         if (!try sema.intFitsInType(alignment_val, Type.u32, null)) {
  21248             return sema.fail(block, src, "alignment must fit in 'u32'", .{});
  21249         }
  21250         const abi_align = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?;
  21251 
  21252         if (layout == .Packed) {
  21253             if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
  21254             if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{});
  21255         } else {
  21256             if (abi_align > 0 and !math.isPowerOfTwo(abi_align)) return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{abi_align});
  21257             struct_type.field_aligns.get(ip)[i] = Alignment.fromByteUnits(abi_align);
  21258         }
  21259         if (layout == .Extern and is_comptime_val.toBool()) {
  21260             return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{});
  21261         }
  21262 
  21263         const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
  21264 
  21265         if (is_tuple) {
  21266             const field_index = field_name.toUnsigned(ip) orelse return sema.fail(
  21267                 block,
  21268                 src,
  21269                 "tuple cannot have non-numeric field '{}'",
  21270                 .{field_name.fmt(ip)},
  21271             );
  21272 
  21273             if (field_index >= fields_len) {
  21274                 return sema.fail(
  21275                     block,
  21276                     src,
  21277                     "tuple field {} exceeds tuple field count",
  21278                     .{field_index},
  21279                 );
  21280             }
  21281         } else if (struct_type.addFieldName(ip, field_name)) |prev_index| {
  21282             _ = prev_index; // TODO: better source location
  21283             return sema.fail(block, src, "duplicate struct field {}", .{field_name.fmt(ip)});
  21284         }
  21285 
  21286         const field_ty = type_val.toType();
  21287         const default_val = if (default_value_val.optionalValue(mod)) |opt_val|
  21288             (try sema.pointerDeref(block, src, opt_val, try mod.singleConstPtrType(field_ty)) orelse
  21289                 return sema.failWithNeededComptime(block, src, .{
  21290                 .needed_comptime_reason = "struct field default value must be comptime-known",
  21291             })).toIntern()
  21292         else
  21293             .none;
  21294         if (is_comptime_val.toBool() and default_val == .none) {
  21295             return sema.fail(block, src, "comptime field without default initialization value", .{});
  21296         }
  21297 
  21298         struct_type.field_types.get(ip)[i] = field_ty.toIntern();
  21299         struct_type.field_inits.get(ip)[i] = default_val;
  21300         if (is_comptime_val.toBool())
  21301             struct_type.setFieldComptime(ip, i);
  21302 
  21303         if (field_ty.zigTypeTag(mod) == .Opaque) {
  21304             const msg = msg: {
  21305                 const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  21306                 errdefer msg.destroy(gpa);
  21307 
  21308                 try sema.addDeclaredHereNote(msg, field_ty);
  21309                 break :msg msg;
  21310             };
  21311             return sema.failWithOwnedErrorMsg(block, msg);
  21312         }
  21313         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  21314             const msg = msg: {
  21315                 const msg = try sema.errMsg(block, src, "struct fields cannot be 'noreturn'", .{});
  21316                 errdefer msg.destroy(gpa);
  21317 
  21318                 try sema.addDeclaredHereNote(msg, field_ty);
  21319                 break :msg msg;
  21320             };
  21321             return sema.failWithOwnedErrorMsg(block, msg);
  21322         }
  21323         if (layout == .Extern and !try sema.validateExternType(field_ty, .struct_field)) {
  21324             const msg = msg: {
  21325                 const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
  21326                 errdefer msg.destroy(gpa);
  21327 
  21328                 const src_decl = sema.mod.declPtr(block.src_decl);
  21329                 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .struct_field);
  21330 
  21331                 try sema.addDeclaredHereNote(msg, field_ty);
  21332                 break :msg msg;
  21333             };
  21334             return sema.failWithOwnedErrorMsg(block, msg);
  21335         } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) {
  21336             const msg = msg: {
  21337                 const msg = try sema.errMsg(block, src, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
  21338                 errdefer msg.destroy(gpa);
  21339 
  21340                 const src_decl = sema.mod.declPtr(block.src_decl);
  21341                 try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty);
  21342 
  21343                 try sema.addDeclaredHereNote(msg, field_ty);
  21344                 break :msg msg;
  21345             };
  21346             return sema.failWithOwnedErrorMsg(block, msg);
  21347         }
  21348     }
  21349 
  21350     if (layout == .Packed) {
  21351         for (0..struct_type.field_types.len) |index| {
  21352             const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[index]);
  21353             sema.resolveTypeLayout(field_ty) catch |err| switch (err) {
  21354                 error.AnalysisFail => {
  21355                     const msg = sema.err orelse return err;
  21356                     try sema.addFieldErrNote(Type.fromInterned(ty), index, msg, "while checking this field", .{});
  21357                     return err;
  21358                 },
  21359                 else => return err,
  21360             };
  21361         }
  21362 
  21363         var fields_bit_sum: u64 = 0;
  21364         for (0..struct_type.field_types.len) |i| {
  21365             const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  21366             fields_bit_sum += field_ty.bitSize(mod);
  21367         }
  21368 
  21369         if (backing_int_val.optionalValue(mod)) |backing_int_ty_val| {
  21370             const backing_int_ty = backing_int_ty_val.toType();
  21371             try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum);
  21372             struct_type.backingIntType(ip).* = backing_int_ty.toIntern();
  21373         } else {
  21374             const backing_int_ty = try mod.intType(.unsigned, @intCast(fields_bit_sum));
  21375             struct_type.backingIntType(ip).* = backing_int_ty.toIntern();
  21376         }
  21377     }
  21378 
  21379     const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  21380     try mod.finalizeAnonDecl(new_decl_index);
  21381     return decl_val;
  21382 }
  21383 
  21384 fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
  21385     const va_list_ty = try sema.getBuiltinType("VaList");
  21386     const va_list_ptr = try sema.mod.singleMutPtrType(va_list_ty);
  21387 
  21388     const inst = try sema.resolveInst(zir_ref);
  21389     return sema.coerce(block, va_list_ptr, inst, src);
  21390 }
  21391 
  21392 fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21393     const mod = sema.mod;
  21394     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  21395     const src = LazySrcLoc.nodeOffset(extra.node);
  21396     const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  21397     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  21398 
  21399     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs);
  21400     const arg_ty = try sema.resolveType(block, ty_src, extra.rhs);
  21401 
  21402     if (!try sema.validateExternType(arg_ty, .param_ty)) {
  21403         const msg = msg: {
  21404             const msg = try sema.errMsg(block, ty_src, "cannot get '{}' from variadic argument", .{arg_ty.fmt(sema.mod)});
  21405             errdefer msg.destroy(sema.gpa);
  21406 
  21407             const src_decl = sema.mod.declPtr(block.src_decl);
  21408             try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), arg_ty, .param_ty);
  21409 
  21410             try sema.addDeclaredHereNote(msg, arg_ty);
  21411             break :msg msg;
  21412         };
  21413         return sema.failWithOwnedErrorMsg(block, msg);
  21414     }
  21415 
  21416     try sema.requireRuntimeBlock(block, src, null);
  21417     return block.addTyOp(.c_va_arg, arg_ty, va_list_ref);
  21418 }
  21419 
  21420 fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21421     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  21422     const src = LazySrcLoc.nodeOffset(extra.node);
  21423     const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  21424 
  21425     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
  21426     const va_list_ty = try sema.getBuiltinType("VaList");
  21427 
  21428     try sema.requireRuntimeBlock(block, src, null);
  21429     return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref);
  21430 }
  21431 
  21432 fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21433     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  21434     const src = LazySrcLoc.nodeOffset(extra.node);
  21435     const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  21436 
  21437     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
  21438 
  21439     try sema.requireRuntimeBlock(block, src, null);
  21440     return block.addUnOp(.c_va_end, va_list_ref);
  21441 }
  21442 
  21443 fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21444     const src = LazySrcLoc.nodeOffset(@bitCast(extended.operand));
  21445 
  21446     const va_list_ty = try sema.getBuiltinType("VaList");
  21447     try sema.requireRuntimeBlock(block, src, null);
  21448     return block.addInst(.{
  21449         .tag = .c_va_start,
  21450         .data = .{ .ty = va_list_ty },
  21451     });
  21452 }
  21453 
  21454 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21455     const mod = sema.mod;
  21456     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  21457     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21458     const ty = try sema.resolveType(block, ty_src, inst_data.operand);
  21459 
  21460     var bytes = std.ArrayList(u8).init(sema.arena);
  21461     try ty.print(bytes.writer(), mod);
  21462     return addStrLitNoAlias(sema, bytes.items);
  21463 }
  21464 
  21465 fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21466     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  21467     const src = inst_data.src();
  21468     return sema.failWithUseOfAsync(block, src);
  21469 }
  21470 
  21471 fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21472     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  21473     const src = inst_data.src();
  21474     return sema.failWithUseOfAsync(block, src);
  21475 }
  21476 
  21477 fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21478     const mod = sema.mod;
  21479     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  21480     const src = inst_data.src();
  21481     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21482     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21483     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat");
  21484     const operand = try sema.resolveInst(extra.rhs);
  21485     const operand_ty = sema.typeOf(operand);
  21486 
  21487     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
  21488     const is_vector = dest_ty.zigTypeTag(mod) == .Vector;
  21489 
  21490     const dest_scalar_ty = dest_ty.scalarType(mod);
  21491     const operand_scalar_ty = operand_ty.scalarType(mod);
  21492 
  21493     _ = try sema.checkIntType(block, src, dest_scalar_ty);
  21494     try sema.checkFloatType(block, operand_src, operand_scalar_ty);
  21495 
  21496     if (try sema.resolveValue(operand)) |operand_val| {
  21497         const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, .truncate);
  21498         return Air.internedToRef(result_val.toIntern());
  21499     } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
  21500         return sema.failWithNeededComptime(block, operand_src, .{
  21501             .needed_comptime_reason = "value being casted to 'comptime_int' must be comptime-known",
  21502         });
  21503     }
  21504 
  21505     try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
  21506     if (dest_scalar_ty.intInfo(mod).bits == 0) {
  21507         if (!is_vector) {
  21508             if (block.wantSafety()) {
  21509                 const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, Air.internedToRef((try mod.floatValue(operand_ty, 0.0)).toIntern()));
  21510                 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);
  21511             }
  21512             return Air.internedToRef((try mod.intValue(dest_ty, 0)).toIntern());
  21513         }
  21514         if (block.wantSafety()) {
  21515             const len = dest_ty.vectorLen(mod);
  21516             for (0..len) |i| {
  21517                 const idx_ref = try mod.intRef(Type.usize, i);
  21518                 const elem_ref = try block.addBinOp(.array_elem_val, operand, idx_ref);
  21519                 const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, elem_ref, Air.internedToRef((try mod.floatValue(operand_scalar_ty, 0.0)).toIntern()));
  21520                 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);
  21521             }
  21522         }
  21523         return Air.internedToRef(try mod.intern(.{ .aggregate = .{
  21524             .ty = dest_ty.toIntern(),
  21525             .storage = .{ .repeated_elem = (try mod.intValue(dest_scalar_ty, 0)).toIntern() },
  21526         } }));
  21527     }
  21528     if (!is_vector) {
  21529         const result = try block.addTyOp(if (block.float_mode == .Optimized) .int_from_float_optimized else .int_from_float, dest_ty, operand);
  21530         if (block.wantSafety()) {
  21531             const back = try block.addTyOp(.float_from_int, operand_ty, result);
  21532             const diff = try block.addBinOp(.sub, operand, back);
  21533             const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, Air.internedToRef((try mod.floatValue(operand_ty, 1.0)).toIntern()));
  21534             const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, Air.internedToRef((try mod.floatValue(operand_ty, -1.0)).toIntern()));
  21535             const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg);
  21536             try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);
  21537         }
  21538         return result;
  21539     }
  21540     const len = dest_ty.vectorLen(mod);
  21541     const new_elems = try sema.arena.alloc(Air.Inst.Ref, len);
  21542     for (new_elems, 0..) |*new_elem, i| {
  21543         const idx_ref = try mod.intRef(Type.usize, i);
  21544         const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref);
  21545         const result = try block.addTyOp(if (block.float_mode == .Optimized) .int_from_float_optimized else .int_from_float, dest_scalar_ty, old_elem);
  21546         if (block.wantSafety()) {
  21547             const back = try block.addTyOp(.float_from_int, operand_scalar_ty, result);
  21548             const diff = try block.addBinOp(.sub, old_elem, back);
  21549             const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, Air.internedToRef((try mod.floatValue(operand_scalar_ty, 1.0)).toIntern()));
  21550             const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, Air.internedToRef((try mod.floatValue(operand_scalar_ty, -1.0)).toIntern()));
  21551             const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg);
  21552             try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);
  21553         }
  21554         new_elem.* = result;
  21555     }
  21556     return block.addAggregateInit(dest_ty, new_elems);
  21557 }
  21558 
  21559 fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21560     const mod = sema.mod;
  21561     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  21562     const src = inst_data.src();
  21563     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21564     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21565     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt");
  21566     const operand = try sema.resolveInst(extra.rhs);
  21567     const operand_ty = sema.typeOf(operand);
  21568 
  21569     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
  21570     const is_vector = dest_ty.zigTypeTag(mod) == .Vector;
  21571 
  21572     const dest_scalar_ty = dest_ty.scalarType(mod);
  21573     const operand_scalar_ty = operand_ty.scalarType(mod);
  21574 
  21575     try sema.checkFloatType(block, src, dest_scalar_ty);
  21576     _ = try sema.checkIntType(block, operand_src, operand_scalar_ty);
  21577 
  21578     if (try sema.resolveValue(operand)) |operand_val| {
  21579         const result_val = try operand_val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, mod, sema);
  21580         return Air.internedToRef(result_val.toIntern());
  21581     } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeFloat) {
  21582         return sema.failWithNeededComptime(block, operand_src, .{
  21583             .needed_comptime_reason = "value being casted to 'comptime_float' must be comptime-known",
  21584         });
  21585     }
  21586 
  21587     try sema.requireRuntimeBlock(block, src, operand_src);
  21588     if (!is_vector) {
  21589         return block.addTyOp(.float_from_int, dest_ty, operand);
  21590     }
  21591     const len = operand_ty.vectorLen(mod);
  21592     const new_elems = try sema.arena.alloc(Air.Inst.Ref, len);
  21593     for (new_elems, 0..) |*new_elem, i| {
  21594         const idx_ref = try mod.intRef(Type.usize, i);
  21595         const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref);
  21596         new_elem.* = try block.addTyOp(.float_from_int, dest_scalar_ty, old_elem);
  21597     }
  21598     return block.addAggregateInit(dest_ty, new_elems);
  21599 }
  21600 
  21601 fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21602     const mod = sema.mod;
  21603     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  21604     const src = inst_data.src();
  21605 
  21606     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21607 
  21608     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21609     const operand_res = try sema.resolveInst(extra.rhs);
  21610 
  21611     const uncoerced_operand_ty = sema.typeOf(operand_res);
  21612     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt");
  21613     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, uncoerced_operand_ty, src, operand_src);
  21614 
  21615     const is_vector = dest_ty.zigTypeTag(mod) == .Vector;
  21616     const operand_ty = if (is_vector) operand_ty: {
  21617         const len = dest_ty.vectorLen(mod);
  21618         break :operand_ty try mod.vectorType(.{ .child = .usize_type, .len = len });
  21619     } else Type.usize;
  21620 
  21621     const operand_coerced = try sema.coerce(block, operand_ty, operand_res, operand_src);
  21622 
  21623     const ptr_ty = dest_ty.scalarType(mod);
  21624     try sema.checkPtrType(block, src, ptr_ty, true);
  21625 
  21626     const elem_ty = ptr_ty.elemType2(mod);
  21627     const ptr_align = try ptr_ty.ptrAlignmentAdvanced(mod, sema);
  21628 
  21629     if (ptr_ty.isSlice(mod)) {
  21630         const msg = msg: {
  21631             const msg = try sema.errMsg(block, src, "integer cannot be converted to slice type '{}'", .{ptr_ty.fmt(sema.mod)});
  21632             errdefer msg.destroy(sema.gpa);
  21633             try sema.errNote(block, src, msg, "slice length cannot be inferred from address", .{});
  21634             break :msg msg;
  21635         };
  21636         return sema.failWithOwnedErrorMsg(block, msg);
  21637     }
  21638 
  21639     if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
  21640         if (!is_vector) {
  21641             const ptr_val = try sema.ptrFromIntVal(block, operand_src, val, ptr_ty, ptr_align);
  21642             return Air.internedToRef(ptr_val.toIntern());
  21643         }
  21644         const len = dest_ty.vectorLen(mod);
  21645         const new_elems = try sema.arena.alloc(InternPool.Index, len);
  21646         for (new_elems, 0..) |*new_elem, i| {
  21647             const elem = try val.elemValue(mod, i);
  21648             const ptr_val = try sema.ptrFromIntVal(block, operand_src, elem, ptr_ty, ptr_align);
  21649             new_elem.* = ptr_val.toIntern();
  21650         }
  21651         return Air.internedToRef(try mod.intern(.{ .aggregate = .{
  21652             .ty = dest_ty.toIntern(),
  21653             .storage = .{ .elems = new_elems },
  21654         } }));
  21655     }
  21656 
  21657     try sema.requireRuntimeBlock(block, src, operand_src);
  21658     if (!is_vector) {
  21659         if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) {
  21660             if (!ptr_ty.isAllowzeroPtr(mod)) {
  21661                 const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
  21662                 try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null);
  21663             }
  21664             if (ptr_align.compare(.gt, .@"1")) {
  21665                 const align_bytes_minus_1 = ptr_align.toByteUnitsOptional().? - 1;
  21666                 const align_minus_1 = Air.internedToRef((try mod.intValue(Type.usize, align_bytes_minus_1)).toIntern());
  21667                 const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1);
  21668                 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  21669                 try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment);
  21670             }
  21671         }
  21672         return block.addBitCast(dest_ty, operand_coerced);
  21673     }
  21674 
  21675     const len = dest_ty.vectorLen(mod);
  21676     if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) {
  21677         for (0..len) |i| {
  21678             const idx_ref = try mod.intRef(Type.usize, i);
  21679             const elem_coerced = try block.addBinOp(.array_elem_val, operand_coerced, idx_ref);
  21680             if (!ptr_ty.isAllowzeroPtr(mod)) {
  21681                 const is_non_zero = try block.addBinOp(.cmp_neq, elem_coerced, .zero_usize);
  21682                 try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null);
  21683             }
  21684             if (ptr_align.compare(.gt, .@"1")) {
  21685                 const align_bytes_minus_1 = ptr_align.toByteUnitsOptional().? - 1;
  21686                 const align_minus_1 = Air.internedToRef((try mod.intValue(Type.usize, align_bytes_minus_1)).toIntern());
  21687                 const remainder = try block.addBinOp(.bit_and, elem_coerced, align_minus_1);
  21688                 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  21689                 try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment);
  21690             }
  21691         }
  21692     }
  21693 
  21694     const new_elems = try sema.arena.alloc(Air.Inst.Ref, len);
  21695     for (new_elems, 0..) |*new_elem, i| {
  21696         const idx_ref = try mod.intRef(Type.usize, i);
  21697         const old_elem = try block.addBinOp(.array_elem_val, operand_coerced, idx_ref);
  21698         new_elem.* = try block.addBitCast(ptr_ty, old_elem);
  21699     }
  21700     return block.addAggregateInit(dest_ty, new_elems);
  21701 }
  21702 
  21703 fn ptrFromIntVal(
  21704     sema: *Sema,
  21705     block: *Block,
  21706     operand_src: LazySrcLoc,
  21707     operand_val: Value,
  21708     ptr_ty: Type,
  21709     ptr_align: Alignment,
  21710 ) !Value {
  21711     const mod = sema.mod;
  21712     const addr = operand_val.toUnsignedInt(mod);
  21713     if (!ptr_ty.isAllowzeroPtr(mod) and addr == 0)
  21714         return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{ptr_ty.fmt(sema.mod)});
  21715     if (addr != 0 and ptr_align != .none and !ptr_align.check(addr))
  21716         return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)});
  21717 
  21718     return switch (ptr_ty.zigTypeTag(mod)) {
  21719         .Optional => Value.fromInterned((try mod.intern(.{ .opt = .{
  21720             .ty = ptr_ty.toIntern(),
  21721             .val = if (addr == 0) .none else (try mod.ptrIntValue(ptr_ty.childType(mod), addr)).toIntern(),
  21722         } }))),
  21723         .Pointer => try mod.ptrIntValue(ptr_ty, addr),
  21724         else => unreachable,
  21725     };
  21726 }
  21727 
  21728 fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21729     const mod = sema.mod;
  21730     const ip = &mod.intern_pool;
  21731     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  21732     const src = LazySrcLoc.nodeOffset(extra.node);
  21733     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  21734     const base_dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast");
  21735     const operand = try sema.resolveInst(extra.rhs);
  21736     const base_operand_ty = sema.typeOf(operand);
  21737     const dest_tag = base_dest_ty.zigTypeTag(mod);
  21738     const operand_tag = base_operand_ty.zigTypeTag(mod);
  21739     if (dest_tag != operand_tag) {
  21740         return sema.fail(block, src, "expected source and destination types to match, found '{s}' and '{s}'", .{
  21741             @tagName(operand_tag), @tagName(dest_tag),
  21742         });
  21743     } else if (dest_tag != .ErrorSet and dest_tag != .ErrorUnion) {
  21744         return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)});
  21745     }
  21746     const dest_ty, const operand_ty = if (dest_tag == .ErrorUnion) .{
  21747         base_dest_ty.errorUnionSet(mod),
  21748         base_operand_ty.errorUnionSet(mod),
  21749     } else .{
  21750         base_dest_ty,
  21751         base_operand_ty,
  21752     };
  21753 
  21754     // operand must be defined since it can be an invalid error value
  21755     const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand);
  21756 
  21757     const disjoint = disjoint: {
  21758         // Try avoiding resolving inferred error sets if we can
  21759         if (!dest_ty.isAnyError(mod) and dest_ty.errorSetIsEmpty(mod)) break :disjoint true;
  21760         if (!operand_ty.isAnyError(mod) and operand_ty.errorSetIsEmpty(mod)) break :disjoint true;
  21761         if (dest_ty.isAnyError(mod)) break :disjoint false;
  21762         if (operand_ty.isAnyError(mod)) break :disjoint false;
  21763         for (dest_ty.errorSetNames(mod)) |dest_err_name| {
  21764             if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name))
  21765                 break :disjoint false;
  21766         }
  21767 
  21768         if (!ip.isInferredErrorSetType(dest_ty.toIntern()) and
  21769             !ip.isInferredErrorSetType(operand_ty.toIntern()))
  21770         {
  21771             break :disjoint true;
  21772         }
  21773 
  21774         _ = try sema.resolveInferredErrorSetTy(block, src, dest_ty.toIntern());
  21775         _ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty.toIntern());
  21776         for (dest_ty.errorSetNames(mod)) |dest_err_name| {
  21777             if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name))
  21778                 break :disjoint false;
  21779         }
  21780 
  21781         break :disjoint true;
  21782     };
  21783     if (disjoint and dest_tag != .ErrorUnion) {
  21784         const msg = msg: {
  21785             const msg = try sema.errMsg(
  21786                 block,
  21787                 src,
  21788                 "error sets '{}' and '{}' have no common errors",
  21789                 .{ operand_ty.fmt(sema.mod), dest_ty.fmt(sema.mod) },
  21790             );
  21791             errdefer msg.destroy(sema.gpa);
  21792             try sema.addDeclaredHereNote(msg, operand_ty);
  21793             try sema.addDeclaredHereNote(msg, dest_ty);
  21794             break :msg msg;
  21795         };
  21796         return sema.failWithOwnedErrorMsg(block, msg);
  21797     }
  21798 
  21799     if (maybe_operand_val) |val| {
  21800         if (!dest_ty.isAnyError(mod)) check: {
  21801             const operand_val = mod.intern_pool.indexToKey(val.toIntern());
  21802             var error_name: InternPool.NullTerminatedString = undefined;
  21803             if (dest_tag == .ErrorUnion) {
  21804                 if (operand_val.error_union.val != .err_name) break :check;
  21805                 error_name = operand_val.error_union.val.err_name;
  21806             } else {
  21807                 error_name = operand_val.err.name;
  21808             }
  21809             if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) {
  21810                 const msg = msg: {
  21811                     const msg = try sema.errMsg(
  21812                         block,
  21813                         src,
  21814                         "'error.{}' not a member of error set '{}'",
  21815                         .{ error_name.fmt(ip), dest_ty.fmt(sema.mod) },
  21816                     );
  21817                     errdefer msg.destroy(sema.gpa);
  21818                     try sema.addDeclaredHereNote(msg, dest_ty);
  21819                     break :msg msg;
  21820                 };
  21821                 return sema.failWithOwnedErrorMsg(block, msg);
  21822             }
  21823         }
  21824 
  21825         return Air.internedToRef((try mod.getCoerced(val, base_dest_ty)).toIntern());
  21826     }
  21827 
  21828     try sema.requireRuntimeBlock(block, src, operand_src);
  21829     const err_int_ty = try mod.errorIntType();
  21830     if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) {
  21831         if (dest_tag == .ErrorUnion) {
  21832             const err_code = try sema.analyzeErrUnionCode(block, operand_src, operand);
  21833             const err_int = try block.addBitCast(err_int_ty, err_code);
  21834             const zero_err = try mod.intRef(try mod.errorIntType(), 0);
  21835 
  21836             const is_zero = try block.addBinOp(.cmp_eq, err_int, zero_err);
  21837             if (disjoint) {
  21838                 // Error must be zero.
  21839                 try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code);
  21840             } else {
  21841                 // Error must be in destination set or zero.
  21842                 const has_value = try block.addTyOp(.error_set_has_value, dest_ty, err_code);
  21843                 const ok = try block.addBinOp(.bool_or, has_value, is_zero);
  21844                 try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
  21845             }
  21846         } else {
  21847             const err_int_inst = try block.addBitCast(err_int_ty, operand);
  21848             const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
  21849             try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
  21850         }
  21851     }
  21852     return block.addBitCast(base_dest_ty, operand);
  21853 }
  21854 
  21855 fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21856     const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(
  21857         @typeInfo(Zir.Inst.FullPtrCastFlags).Struct.backing_integer.?,
  21858         @truncate(extended.small),
  21859     ));
  21860     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  21861     const src = LazySrcLoc.nodeOffset(extra.node);
  21862     const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node };
  21863     const operand = try sema.resolveInst(extra.rhs);
  21864     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName());
  21865     return sema.ptrCastFull(
  21866         block,
  21867         flags,
  21868         src,
  21869         operand,
  21870         operand_src,
  21871         dest_ty,
  21872     );
  21873 }
  21874 
  21875 fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21876     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  21877     const src = inst_data.src();
  21878     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21879     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21880     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrCast");
  21881     const operand = try sema.resolveInst(extra.rhs);
  21882 
  21883     return sema.ptrCastFull(
  21884         block,
  21885         .{ .ptr_cast = true },
  21886         src,
  21887         operand,
  21888         operand_src,
  21889         dest_ty,
  21890     );
  21891 }
  21892 
  21893 fn ptrCastFull(
  21894     sema: *Sema,
  21895     block: *Block,
  21896     flags: Zir.Inst.FullPtrCastFlags,
  21897     src: LazySrcLoc,
  21898     operand: Air.Inst.Ref,
  21899     operand_src: LazySrcLoc,
  21900     dest_ty: Type,
  21901 ) CompileError!Air.Inst.Ref {
  21902     const mod = sema.mod;
  21903     const operand_ty = sema.typeOf(operand);
  21904 
  21905     try sema.checkPtrType(block, src, dest_ty, true);
  21906     try sema.checkPtrOperand(block, operand_src, operand_ty);
  21907 
  21908     const src_info = operand_ty.ptrInfo(mod);
  21909     const dest_info = dest_ty.ptrInfo(mod);
  21910 
  21911     try sema.resolveTypeLayout(Type.fromInterned(src_info.child));
  21912     try sema.resolveTypeLayout(Type.fromInterned(dest_info.child));
  21913 
  21914     const src_slice_like = src_info.flags.size == .Slice or
  21915         (src_info.flags.size == .One and Type.fromInterned(src_info.child).zigTypeTag(mod) == .Array);
  21916 
  21917     const dest_slice_like = dest_info.flags.size == .Slice or
  21918         (dest_info.flags.size == .One and Type.fromInterned(dest_info.child).zigTypeTag(mod) == .Array);
  21919 
  21920     if (dest_info.flags.size == .Slice and !src_slice_like) {
  21921         return sema.fail(block, src, "illegal pointer cast to slice", .{});
  21922     }
  21923 
  21924     if (dest_info.flags.size == .Slice) {
  21925         const src_elem_size = switch (src_info.flags.size) {
  21926             .Slice => Type.fromInterned(src_info.child).abiSize(mod),
  21927             // pointer to array
  21928             .One => Type.fromInterned(src_info.child).childType(mod).abiSize(mod),
  21929             else => unreachable,
  21930         };
  21931         const dest_elem_size = Type.fromInterned(dest_info.child).abiSize(mod);
  21932         if (src_elem_size != dest_elem_size) {
  21933             return sema.fail(block, src, "TODO: implement @ptrCast between slices changing the length", .{});
  21934         }
  21935     }
  21936 
  21937     // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs
  21938 
  21939     if (!flags.ptr_cast) {
  21940         check_size: {
  21941             if (src_info.flags.size == dest_info.flags.size) break :check_size;
  21942             if (src_slice_like and dest_slice_like) break :check_size;
  21943             if (src_info.flags.size == .C) break :check_size;
  21944             if (dest_info.flags.size == .C) break :check_size;
  21945             return sema.failWithOwnedErrorMsg(block, msg: {
  21946                 const msg = try sema.errMsg(block, src, "cannot implicitly convert {s} pointer to {s} pointer", .{
  21947                     pointerSizeString(src_info.flags.size),
  21948                     pointerSizeString(dest_info.flags.size),
  21949                 });
  21950                 errdefer msg.destroy(sema.gpa);
  21951                 if (dest_info.flags.size == .Many and
  21952                     (src_info.flags.size == .Slice or
  21953                     (src_info.flags.size == .One and Type.fromInterned(src_info.child).zigTypeTag(mod) == .Array)))
  21954                 {
  21955                     try sema.errNote(block, src, msg, "use 'ptr' field to convert slice to many pointer", .{});
  21956                 } else {
  21957                     try sema.errNote(block, src, msg, "use @ptrCast to change pointer size", .{});
  21958                 }
  21959                 break :msg msg;
  21960             });
  21961         }
  21962 
  21963         check_child: {
  21964             const src_child = if (dest_info.flags.size == .Slice and src_info.flags.size == .One) blk: {
  21965                 // *[n]T -> []T
  21966                 break :blk Type.fromInterned(src_info.child).childType(mod);
  21967             } else Type.fromInterned(src_info.child);
  21968 
  21969             const dest_child = Type.fromInterned(dest_info.child);
  21970 
  21971             const imc_res = try sema.coerceInMemoryAllowed(
  21972                 block,
  21973                 dest_child,
  21974                 src_child,
  21975                 !dest_info.flags.is_const,
  21976                 mod.getTarget(),
  21977                 src,
  21978                 operand_src,
  21979             );
  21980             if (imc_res == .ok) break :check_child;
  21981             return sema.failWithOwnedErrorMsg(block, msg: {
  21982                 const msg = try sema.errMsg(block, src, "pointer element type '{}' cannot coerce into element type '{}'", .{
  21983                     src_child.fmt(mod),
  21984                     dest_child.fmt(mod),
  21985                 });
  21986                 errdefer msg.destroy(sema.gpa);
  21987                 try imc_res.report(sema, block, src, msg);
  21988                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer element type", .{});
  21989                 break :msg msg;
  21990             });
  21991         }
  21992 
  21993         check_sent: {
  21994             if (dest_info.sentinel == .none) break :check_sent;
  21995             if (src_info.flags.size == .C) break :check_sent;
  21996             if (src_info.sentinel != .none) {
  21997                 const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child);
  21998                 if (dest_info.sentinel == coerced_sent) break :check_sent;
  21999             }
  22000             if (src_slice_like and src_info.flags.size == .One and dest_info.flags.size == .Slice) {
  22001                 // [*]nT -> []T
  22002                 const arr_ty = Type.fromInterned(src_info.child);
  22003                 if (arr_ty.sentinel(mod)) |src_sentinel| {
  22004                     const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_sentinel.toIntern(), dest_info.child);
  22005                     if (dest_info.sentinel == coerced_sent) break :check_sent;
  22006                 }
  22007             }
  22008             return sema.failWithOwnedErrorMsg(block, msg: {
  22009                 const msg = if (src_info.sentinel == .none) blk: {
  22010                     break :blk try sema.errMsg(block, src, "destination pointer requires '{}' sentinel", .{
  22011                         Value.fromInterned(dest_info.sentinel).fmtValue(Type.fromInterned(dest_info.child), mod),
  22012                     });
  22013                 } else blk: {
  22014                     break :blk try sema.errMsg(block, src, "pointer sentinel '{}' cannot coerce into pointer sentinel '{}'", .{
  22015                         Value.fromInterned(src_info.sentinel).fmtValue(Type.fromInterned(src_info.child), mod),
  22016                         Value.fromInterned(dest_info.sentinel).fmtValue(Type.fromInterned(dest_info.child), mod),
  22017                     });
  22018                 };
  22019                 errdefer msg.destroy(sema.gpa);
  22020                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer sentinel", .{});
  22021                 break :msg msg;
  22022             });
  22023         }
  22024 
  22025         if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) {
  22026             return sema.failWithOwnedErrorMsg(block, msg: {
  22027                 const msg = try sema.errMsg(block, src, "pointer host size '{}' cannot coerce into pointer host size '{}'", .{
  22028                     src_info.packed_offset.host_size,
  22029                     dest_info.packed_offset.host_size,
  22030                 });
  22031                 errdefer msg.destroy(sema.gpa);
  22032                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer host size", .{});
  22033                 break :msg msg;
  22034             });
  22035         }
  22036 
  22037         if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) {
  22038             return sema.failWithOwnedErrorMsg(block, msg: {
  22039                 const msg = try sema.errMsg(block, src, "pointer bit offset '{}' cannot coerce into pointer bit offset '{}'", .{
  22040                     src_info.packed_offset.bit_offset,
  22041                     dest_info.packed_offset.bit_offset,
  22042                 });
  22043                 errdefer msg.destroy(sema.gpa);
  22044                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer bit offset", .{});
  22045                 break :msg msg;
  22046             });
  22047         }
  22048 
  22049         check_allowzero: {
  22050             const src_allows_zero = operand_ty.ptrAllowsZero(mod);
  22051             const dest_allows_zero = dest_ty.ptrAllowsZero(mod);
  22052             if (!src_allows_zero) break :check_allowzero;
  22053             if (dest_allows_zero) break :check_allowzero;
  22054 
  22055             return sema.failWithOwnedErrorMsg(block, msg: {
  22056                 const msg = try sema.errMsg(block, src, "'{}' could have null values which are illegal in type '{}'", .{
  22057                     operand_ty.fmt(mod),
  22058                     dest_ty.fmt(mod),
  22059                 });
  22060                 errdefer msg.destroy(sema.gpa);
  22061                 try sema.errNote(block, src, msg, "use @ptrCast to assert the pointer is not null", .{});
  22062                 break :msg msg;
  22063             });
  22064         }
  22065 
  22066         // TODO: vector index?
  22067     }
  22068 
  22069     const src_align = if (src_info.flags.alignment != .none)
  22070         src_info.flags.alignment
  22071     else
  22072         Type.fromInterned(src_info.child).abiAlignment(mod);
  22073 
  22074     const dest_align = if (dest_info.flags.alignment != .none)
  22075         dest_info.flags.alignment
  22076     else
  22077         Type.fromInterned(dest_info.child).abiAlignment(mod);
  22078 
  22079     if (!flags.align_cast) {
  22080         if (dest_align.compare(.gt, src_align)) {
  22081             return sema.failWithOwnedErrorMsg(block, msg: {
  22082                 const msg = try sema.errMsg(block, src, "cast increases pointer alignment", .{});
  22083                 errdefer msg.destroy(sema.gpa);
  22084                 try sema.errNote(block, operand_src, msg, "'{}' has alignment '{d}'", .{
  22085                     operand_ty.fmt(mod), src_align.toByteUnits(0),
  22086                 });
  22087                 try sema.errNote(block, src, msg, "'{}' has alignment '{d}'", .{
  22088                     dest_ty.fmt(mod), dest_align.toByteUnits(0),
  22089                 });
  22090                 try sema.errNote(block, src, msg, "use @alignCast to assert pointer alignment", .{});
  22091                 break :msg msg;
  22092             });
  22093         }
  22094     }
  22095 
  22096     if (!flags.addrspace_cast) {
  22097         if (src_info.flags.address_space != dest_info.flags.address_space) {
  22098             return sema.failWithOwnedErrorMsg(block, msg: {
  22099                 const msg = try sema.errMsg(block, src, "cast changes pointer address space", .{});
  22100                 errdefer msg.destroy(sema.gpa);
  22101                 try sema.errNote(block, operand_src, msg, "'{}' has address space '{s}'", .{
  22102                     operand_ty.fmt(mod), @tagName(src_info.flags.address_space),
  22103                 });
  22104                 try sema.errNote(block, src, msg, "'{}' has address space '{s}'", .{
  22105                     dest_ty.fmt(mod), @tagName(dest_info.flags.address_space),
  22106                 });
  22107                 try sema.errNote(block, src, msg, "use @addrSpaceCast to cast pointer address space", .{});
  22108                 break :msg msg;
  22109             });
  22110         }
  22111     } else {
  22112         // Some address space casts are always disallowed
  22113         if (!target_util.addrSpaceCastIsValid(mod.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) {
  22114             return sema.failWithOwnedErrorMsg(block, msg: {
  22115                 const msg = try sema.errMsg(block, src, "invalid address space cast", .{});
  22116                 errdefer msg.destroy(sema.gpa);
  22117                 try sema.errNote(block, operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{
  22118                     @tagName(src_info.flags.address_space),
  22119                     @tagName(dest_info.flags.address_space),
  22120                 });
  22121                 break :msg msg;
  22122             });
  22123         }
  22124     }
  22125 
  22126     if (!flags.const_cast) {
  22127         if (src_info.flags.is_const and !dest_info.flags.is_const) {
  22128             return sema.failWithOwnedErrorMsg(block, msg: {
  22129                 const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{});
  22130                 errdefer msg.destroy(sema.gpa);
  22131                 try sema.errNote(block, src, msg, "use @constCast to discard const qualifier", .{});
  22132                 break :msg msg;
  22133             });
  22134         }
  22135     }
  22136 
  22137     if (!flags.volatile_cast) {
  22138         if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) {
  22139             return sema.failWithOwnedErrorMsg(block, msg: {
  22140                 const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{});
  22141                 errdefer msg.destroy(sema.gpa);
  22142                 try sema.errNote(block, src, msg, "use @volatileCast to discard volatile qualifier", .{});
  22143                 break :msg msg;
  22144             });
  22145         }
  22146     }
  22147 
  22148     const ptr = if (src_info.flags.size == .Slice and dest_info.flags.size != .Slice) ptr: {
  22149         break :ptr try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty);
  22150     } else operand;
  22151 
  22152     const dest_ptr_ty = if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) blk: {
  22153         // Only convert to a many-pointer at first
  22154         var info = dest_info;
  22155         info.flags.size = .Many;
  22156         const ty = try sema.ptrType(info);
  22157         if (dest_ty.zigTypeTag(mod) == .Optional) {
  22158             break :blk try mod.optionalType(ty.toIntern());
  22159         } else {
  22160             break :blk ty;
  22161         }
  22162     } else dest_ty;
  22163 
  22164     // Cannot do @addrSpaceCast at comptime
  22165     if (!flags.addrspace_cast) {
  22166         if (try sema.resolveValue(ptr)) |ptr_val| {
  22167             if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isUndef(mod)) {
  22168                 return sema.failWithUseOfUndef(block, operand_src);
  22169             }
  22170             if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isNull(mod)) {
  22171                 return sema.fail(block, operand_src, "null pointer casted to type '{}'", .{dest_ty.fmt(mod)});
  22172             }
  22173             if (dest_align.compare(.gt, src_align)) {
  22174                 if (try ptr_val.getUnsignedIntAdvanced(mod, null)) |addr| {
  22175                     if (!dest_align.check(addr)) {
  22176                         return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{
  22177                             addr,
  22178                             dest_align.toByteUnitsOptional().?,
  22179                         });
  22180                     }
  22181                 }
  22182             }
  22183             if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) {
  22184                 if (ptr_val.isUndef(mod)) return mod.undefRef(dest_ty);
  22185                 const arr_len = try mod.intValue(Type.usize, Type.fromInterned(src_info.child).arrayLen(mod));
  22186                 return Air.internedToRef((try mod.intern(.{ .ptr = .{
  22187                     .ty = dest_ty.toIntern(),
  22188                     .addr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr,
  22189                     .len = arr_len.toIntern(),
  22190                 } })));
  22191             } else {
  22192                 assert(dest_ptr_ty.eql(dest_ty, mod));
  22193                 return Air.internedToRef((try mod.getCoerced(ptr_val, dest_ty)).toIntern());
  22194             }
  22195         }
  22196     }
  22197 
  22198     try sema.requireRuntimeBlock(block, src, null);
  22199 
  22200     if (block.wantSafety() and operand_ty.ptrAllowsZero(mod) and !dest_ty.ptrAllowsZero(mod) and
  22201         (try sema.typeHasRuntimeBits(Type.fromInterned(dest_info.child)) or Type.fromInterned(dest_info.child).zigTypeTag(mod) == .Fn))
  22202     {
  22203         const ptr_int = try block.addUnOp(.int_from_ptr, ptr);
  22204         const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
  22205         const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: {
  22206             const len = try sema.analyzeSliceLen(block, operand_src, ptr);
  22207             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
  22208             break :ok try block.addBinOp(.bool_or, len_zero, is_non_zero);
  22209         } else is_non_zero;
  22210         try sema.addSafetyCheck(block, src, ok, .cast_to_null);
  22211     }
  22212 
  22213     if (block.wantSafety() and
  22214         dest_align.compare(.gt, src_align) and
  22215         try sema.typeHasRuntimeBits(Type.fromInterned(dest_info.child)))
  22216     {
  22217         const align_bytes_minus_1 = dest_align.toByteUnitsOptional().? - 1;
  22218         const align_minus_1 = Air.internedToRef((try mod.intValue(Type.usize, align_bytes_minus_1)).toIntern());
  22219         const ptr_int = try block.addUnOp(.int_from_ptr, ptr);
  22220         const remainder = try block.addBinOp(.bit_and, ptr_int, align_minus_1);
  22221         const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  22222         const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: {
  22223             const len = try sema.analyzeSliceLen(block, operand_src, ptr);
  22224             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
  22225             break :ok try block.addBinOp(.bool_or, len_zero, is_aligned);
  22226         } else is_aligned;
  22227         try sema.addSafetyCheck(block, src, ok, .incorrect_alignment);
  22228     }
  22229 
  22230     // If we're going from an array pointer to a slice, this will only be the pointer part!
  22231     const result_ptr = if (flags.addrspace_cast) ptr: {
  22232         // We can't change address spaces with a bitcast, so this requires two instructions
  22233         var intermediate_info = src_info;
  22234         intermediate_info.flags.address_space = dest_info.flags.address_space;
  22235         const intermediate_ptr_ty = try sema.ptrType(intermediate_info);
  22236         const intermediate_ty = if (dest_ptr_ty.zigTypeTag(mod) == .Optional) blk: {
  22237             break :blk try mod.optionalType(intermediate_ptr_ty.toIntern());
  22238         } else intermediate_ptr_ty;
  22239         const intermediate = try block.addInst(.{
  22240             .tag = .addrspace_cast,
  22241             .data = .{ .ty_op = .{
  22242                 .ty = Air.internedToRef(intermediate_ty.toIntern()),
  22243                 .operand = ptr,
  22244             } },
  22245         });
  22246         if (intermediate_ty.eql(dest_ptr_ty, mod)) {
  22247             // We only changed the address space, so no need for a bitcast
  22248             break :ptr intermediate;
  22249         }
  22250         break :ptr try block.addBitCast(dest_ptr_ty, intermediate);
  22251     } else ptr: {
  22252         break :ptr try block.addBitCast(dest_ptr_ty, ptr);
  22253     };
  22254 
  22255     if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) {
  22256         // We have to construct a slice using the operand's child's array length
  22257         // Note that we know from the check at the start of the function that operand_ty is slice-like
  22258         const arr_len = Air.internedToRef((try mod.intValue(Type.usize, Type.fromInterned(src_info.child).arrayLen(mod))).toIntern());
  22259         return block.addInst(.{
  22260             .tag = .slice,
  22261             .data = .{ .ty_pl = .{
  22262                 .ty = Air.internedToRef(dest_ty.toIntern()),
  22263                 .payload = try sema.addExtra(Air.Bin{
  22264                     .lhs = result_ptr,
  22265                     .rhs = arr_len,
  22266                 }),
  22267             } },
  22268         });
  22269     } else {
  22270         assert(dest_ptr_ty.eql(dest_ty, mod));
  22271         try sema.checkKnownAllocPtr(operand, result_ptr);
  22272         return result_ptr;
  22273     }
  22274 }
  22275 
  22276 fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  22277     const mod = sema.mod;
  22278     const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(
  22279         @typeInfo(Zir.Inst.FullPtrCastFlags).Struct.backing_integer.?,
  22280         @truncate(extended.small),
  22281     ));
  22282     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  22283     const src = LazySrcLoc.nodeOffset(extra.node);
  22284     const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node };
  22285     const operand = try sema.resolveInst(extra.operand);
  22286     const operand_ty = sema.typeOf(operand);
  22287     try sema.checkPtrOperand(block, operand_src, operand_ty);
  22288 
  22289     var ptr_info = operand_ty.ptrInfo(mod);
  22290     if (flags.const_cast) ptr_info.flags.is_const = false;
  22291     if (flags.volatile_cast) ptr_info.flags.is_volatile = false;
  22292     const dest_ty = try sema.ptrType(ptr_info);
  22293 
  22294     if (try sema.resolveValue(operand)) |operand_val| {
  22295         return Air.internedToRef((try mod.getCoerced(operand_val, dest_ty)).toIntern());
  22296     }
  22297 
  22298     try sema.requireRuntimeBlock(block, src, null);
  22299     const new_ptr = try block.addBitCast(dest_ty, operand);
  22300     try sema.checkKnownAllocPtr(operand, new_ptr);
  22301     return new_ptr;
  22302 }
  22303 
  22304 fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22305     const mod = sema.mod;
  22306     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  22307     const src = inst_data.src();
  22308     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22309     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  22310     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate");
  22311     const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src);
  22312     const operand = try sema.resolveInst(extra.rhs);
  22313     const operand_ty = sema.typeOf(operand);
  22314     const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
  22315 
  22316     const operand_is_vector = operand_ty.zigTypeTag(mod) == .Vector;
  22317     const dest_is_vector = dest_ty.zigTypeTag(mod) == .Vector;
  22318     if (operand_is_vector != dest_is_vector) {
  22319         return sema.fail(block, operand_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), operand_ty.fmt(mod) });
  22320     }
  22321 
  22322     if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
  22323         return sema.coerce(block, dest_ty, operand, operand_src);
  22324     }
  22325 
  22326     const dest_info = dest_scalar_ty.intInfo(mod);
  22327 
  22328     if (try sema.typeHasOnePossibleValue(dest_ty)) |val| {
  22329         return Air.internedToRef(val.toIntern());
  22330     }
  22331 
  22332     if (operand_scalar_ty.zigTypeTag(mod) != .ComptimeInt) {
  22333         const operand_info = operand_ty.intInfo(mod);
  22334         if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22335             return Air.internedToRef(val.toIntern());
  22336         }
  22337 
  22338         if (operand_info.signedness != dest_info.signedness) {
  22339             return sema.fail(block, operand_src, "expected {s} integer type, found '{}'", .{
  22340                 @tagName(dest_info.signedness), operand_ty.fmt(mod),
  22341             });
  22342         }
  22343         if (operand_info.bits < dest_info.bits) {
  22344             const msg = msg: {
  22345                 const msg = try sema.errMsg(
  22346                     block,
  22347                     src,
  22348                     "destination type '{}' has more bits than source type '{}'",
  22349                     .{ dest_ty.fmt(mod), operand_ty.fmt(mod) },
  22350                 );
  22351                 errdefer msg.destroy(sema.gpa);
  22352                 try sema.errNote(block, src, msg, "destination type has {d} bits", .{
  22353                     dest_info.bits,
  22354                 });
  22355                 try sema.errNote(block, operand_src, msg, "operand type has {d} bits", .{
  22356                     operand_info.bits,
  22357                 });
  22358                 break :msg msg;
  22359             };
  22360             return sema.failWithOwnedErrorMsg(block, msg);
  22361         }
  22362     }
  22363 
  22364     if (try sema.resolveValueIntable(operand)) |val| {
  22365         if (val.isUndef(mod)) return mod.undefRef(dest_ty);
  22366         if (!dest_is_vector) {
  22367             return Air.internedToRef((try mod.getCoerced(
  22368                 try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, mod),
  22369                 dest_ty,
  22370             )).toIntern());
  22371         }
  22372         const elems = try sema.arena.alloc(InternPool.Index, operand_ty.vectorLen(mod));
  22373         for (elems, 0..) |*elem, i| {
  22374             const elem_val = try val.elemValue(mod, i);
  22375             elem.* = try (try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, mod)).intern(dest_scalar_ty, mod);
  22376         }
  22377         return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  22378             .ty = dest_ty.toIntern(),
  22379             .storage = .{ .elems = elems },
  22380         } })));
  22381     }
  22382 
  22383     try sema.requireRuntimeBlock(block, src, operand_src);
  22384     return block.addTyOp(.trunc, dest_ty, operand);
  22385 }
  22386 
  22387 fn zirBitCount(
  22388     sema: *Sema,
  22389     block: *Block,
  22390     inst: Zir.Inst.Index,
  22391     air_tag: Air.Inst.Tag,
  22392     comptime comptimeOp: fn (val: Value, ty: Type, mod: *Module) u64,
  22393 ) CompileError!Air.Inst.Ref {
  22394     const mod = sema.mod;
  22395     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  22396     const src = inst_data.src();
  22397     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22398     const operand = try sema.resolveInst(inst_data.operand);
  22399     const operand_ty = sema.typeOf(operand);
  22400     _ = try sema.checkIntOrVector(block, operand, operand_src);
  22401     const bits = operand_ty.intInfo(mod).bits;
  22402 
  22403     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22404         return Air.internedToRef(val.toIntern());
  22405     }
  22406 
  22407     const result_scalar_ty = try mod.smallestUnsignedInt(bits);
  22408     switch (operand_ty.zigTypeTag(mod)) {
  22409         .Vector => {
  22410             const vec_len = operand_ty.vectorLen(mod);
  22411             const result_ty = try mod.vectorType(.{
  22412                 .len = vec_len,
  22413                 .child = result_scalar_ty.toIntern(),
  22414             });
  22415             if (try sema.resolveValue(operand)) |val| {
  22416                 if (val.isUndef(mod)) return mod.undefRef(result_ty);
  22417 
  22418                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  22419                 const scalar_ty = operand_ty.scalarType(mod);
  22420                 for (elems, 0..) |*elem, i| {
  22421                     const elem_val = try val.elemValue(mod, i);
  22422                     const count = comptimeOp(elem_val, scalar_ty, mod);
  22423                     elem.* = (try mod.intValue(result_scalar_ty, count)).toIntern();
  22424                 }
  22425                 return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  22426                     .ty = result_ty.toIntern(),
  22427                     .storage = .{ .elems = elems },
  22428                 } })));
  22429             } else {
  22430                 try sema.requireRuntimeBlock(block, src, operand_src);
  22431                 return block.addTyOp(air_tag, result_ty, operand);
  22432             }
  22433         },
  22434         .Int => {
  22435             if (try sema.resolveValueResolveLazy(operand)) |val| {
  22436                 if (val.isUndef(mod)) return mod.undefRef(result_scalar_ty);
  22437                 return mod.intRef(result_scalar_ty, comptimeOp(val, operand_ty, mod));
  22438             } else {
  22439                 try sema.requireRuntimeBlock(block, src, operand_src);
  22440                 return block.addTyOp(air_tag, result_scalar_ty, operand);
  22441             }
  22442         },
  22443         else => unreachable,
  22444     }
  22445 }
  22446 
  22447 fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22448     const mod = sema.mod;
  22449     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  22450     const src = inst_data.src();
  22451     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22452     const operand = try sema.resolveInst(inst_data.operand);
  22453     const operand_ty = sema.typeOf(operand);
  22454     const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src);
  22455     const bits = scalar_ty.intInfo(mod).bits;
  22456     if (bits % 8 != 0) {
  22457         return sema.fail(
  22458             block,
  22459             operand_src,
  22460             "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits",
  22461             .{ scalar_ty.fmt(mod), bits },
  22462         );
  22463     }
  22464 
  22465     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22466         return Air.internedToRef(val.toIntern());
  22467     }
  22468 
  22469     switch (operand_ty.zigTypeTag(mod)) {
  22470         .Int => {
  22471             const runtime_src = if (try sema.resolveValue(operand)) |val| {
  22472                 if (val.isUndef(mod)) return mod.undefRef(operand_ty);
  22473                 const result_val = try val.byteSwap(operand_ty, mod, sema.arena);
  22474                 return Air.internedToRef(result_val.toIntern());
  22475             } else operand_src;
  22476 
  22477             try sema.requireRuntimeBlock(block, src, runtime_src);
  22478             return block.addTyOp(.byte_swap, operand_ty, operand);
  22479         },
  22480         .Vector => {
  22481             const runtime_src = if (try sema.resolveValue(operand)) |val| {
  22482                 if (val.isUndef(mod))
  22483                     return mod.undefRef(operand_ty);
  22484 
  22485                 const vec_len = operand_ty.vectorLen(mod);
  22486                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  22487                 for (elems, 0..) |*elem, i| {
  22488                     const elem_val = try val.elemValue(mod, i);
  22489                     elem.* = try (try elem_val.byteSwap(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod);
  22490                 }
  22491                 return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  22492                     .ty = operand_ty.toIntern(),
  22493                     .storage = .{ .elems = elems },
  22494                 } })));
  22495             } else operand_src;
  22496 
  22497             try sema.requireRuntimeBlock(block, src, runtime_src);
  22498             return block.addTyOp(.byte_swap, operand_ty, operand);
  22499         },
  22500         else => unreachable,
  22501     }
  22502 }
  22503 
  22504 fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22505     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  22506     const src = inst_data.src();
  22507     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22508     const operand = try sema.resolveInst(inst_data.operand);
  22509     const operand_ty = sema.typeOf(operand);
  22510     const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src);
  22511 
  22512     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22513         return Air.internedToRef(val.toIntern());
  22514     }
  22515 
  22516     const mod = sema.mod;
  22517     switch (operand_ty.zigTypeTag(mod)) {
  22518         .Int => {
  22519             const runtime_src = if (try sema.resolveValue(operand)) |val| {
  22520                 if (val.isUndef(mod)) return mod.undefRef(operand_ty);
  22521                 const result_val = try val.bitReverse(operand_ty, mod, sema.arena);
  22522                 return Air.internedToRef(result_val.toIntern());
  22523             } else operand_src;
  22524 
  22525             try sema.requireRuntimeBlock(block, src, runtime_src);
  22526             return block.addTyOp(.bit_reverse, operand_ty, operand);
  22527         },
  22528         .Vector => {
  22529             const runtime_src = if (try sema.resolveValue(operand)) |val| {
  22530                 if (val.isUndef(mod))
  22531                     return mod.undefRef(operand_ty);
  22532 
  22533                 const vec_len = operand_ty.vectorLen(mod);
  22534                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  22535                 for (elems, 0..) |*elem, i| {
  22536                     const elem_val = try val.elemValue(mod, i);
  22537                     elem.* = try (try elem_val.bitReverse(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod);
  22538                 }
  22539                 return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  22540                     .ty = operand_ty.toIntern(),
  22541                     .storage = .{ .elems = elems },
  22542                 } })));
  22543             } else operand_src;
  22544 
  22545             try sema.requireRuntimeBlock(block, src, runtime_src);
  22546             return block.addTyOp(.bit_reverse, operand_ty, operand);
  22547         },
  22548         else => unreachable,
  22549     }
  22550 }
  22551 
  22552 fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22553     const offset = try sema.bitOffsetOf(block, inst);
  22554     return sema.mod.intRef(Type.comptime_int, offset);
  22555 }
  22556 
  22557 fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22558     const offset = try sema.bitOffsetOf(block, inst);
  22559     // TODO reminder to make this a compile error for packed structs
  22560     return sema.mod.intRef(Type.comptime_int, offset / 8);
  22561 }
  22562 
  22563 fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 {
  22564     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  22565     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  22566     sema.src = src;
  22567     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  22568     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  22569     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  22570 
  22571     const ty = try sema.resolveType(block, lhs_src, extra.lhs);
  22572     const field_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, .{
  22573         .needed_comptime_reason = "name of field must be comptime-known",
  22574     });
  22575 
  22576     const mod = sema.mod;
  22577     const ip = &mod.intern_pool;
  22578     try sema.resolveTypeLayout(ty);
  22579     switch (ty.zigTypeTag(mod)) {
  22580         .Struct => {},
  22581         else => {
  22582             const msg = msg: {
  22583                 const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(mod)});
  22584                 errdefer msg.destroy(sema.gpa);
  22585                 try sema.addDeclaredHereNote(msg, ty);
  22586                 break :msg msg;
  22587             };
  22588             return sema.failWithOwnedErrorMsg(block, msg);
  22589         },
  22590     }
  22591 
  22592     const field_index = if (ty.isTuple(mod)) blk: {
  22593         if (ip.stringEqlSlice(field_name, "len")) {
  22594             return sema.fail(block, src, "no offset available for 'len' field of tuple", .{});
  22595         }
  22596         break :blk try sema.tupleFieldIndex(block, ty, field_name, rhs_src);
  22597     } else try sema.structFieldIndex(block, ty, field_name, rhs_src);
  22598 
  22599     if (ty.structFieldIsComptime(field_index, mod)) {
  22600         return sema.fail(block, src, "no offset available for comptime field", .{});
  22601     }
  22602 
  22603     switch (ty.containerLayout(mod)) {
  22604         .Packed => {
  22605             var bit_sum: u64 = 0;
  22606             const struct_type = ip.indexToKey(ty.toIntern()).struct_type;
  22607             for (0..struct_type.field_types.len) |i| {
  22608                 if (i == field_index) {
  22609                     return bit_sum;
  22610                 }
  22611                 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  22612                 bit_sum += field_ty.bitSize(mod);
  22613             } else unreachable;
  22614         },
  22615         else => return ty.structFieldOffset(field_index, mod) * 8,
  22616     }
  22617 }
  22618 
  22619 fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
  22620     const mod = sema.mod;
  22621     switch (ty.zigTypeTag(mod)) {
  22622         .Struct, .Enum, .Union, .Opaque => return,
  22623         else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(mod)}),
  22624     }
  22625 }
  22626 
  22627 /// Returns `true` if the type was a comptime_int.
  22628 fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
  22629     const mod = sema.mod;
  22630     switch (try ty.zigTypeTagOrPoison(mod)) {
  22631         .ComptimeInt => return true,
  22632         .Int => return false,
  22633         else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(mod)}),
  22634     }
  22635 }
  22636 
  22637 fn checkInvalidPtrArithmetic(
  22638     sema: *Sema,
  22639     block: *Block,
  22640     src: LazySrcLoc,
  22641     ty: Type,
  22642 ) CompileError!void {
  22643     const mod = sema.mod;
  22644     switch (try ty.zigTypeTagOrPoison(mod)) {
  22645         .Pointer => switch (ty.ptrSize(mod)) {
  22646             .One, .Slice => return,
  22647             .Many, .C => return sema.fail(
  22648                 block,
  22649                 src,
  22650                 "invalid pointer arithmetic operator",
  22651                 .{},
  22652             ),
  22653         },
  22654         else => return,
  22655     }
  22656 }
  22657 
  22658 fn checkArithmeticOp(
  22659     sema: *Sema,
  22660     block: *Block,
  22661     src: LazySrcLoc,
  22662     scalar_tag: std.builtin.TypeId,
  22663     lhs_zig_ty_tag: std.builtin.TypeId,
  22664     rhs_zig_ty_tag: std.builtin.TypeId,
  22665     zir_tag: Zir.Inst.Tag,
  22666 ) CompileError!void {
  22667     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  22668     const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
  22669 
  22670     if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
  22671         return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{
  22672             @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag),
  22673         });
  22674     }
  22675 }
  22676 
  22677 fn checkPtrOperand(
  22678     sema: *Sema,
  22679     block: *Block,
  22680     ty_src: LazySrcLoc,
  22681     ty: Type,
  22682 ) CompileError!void {
  22683     const mod = sema.mod;
  22684     switch (ty.zigTypeTag(mod)) {
  22685         .Pointer => return,
  22686         .Fn => {
  22687             const msg = msg: {
  22688                 const msg = try sema.errMsg(
  22689                     block,
  22690                     ty_src,
  22691                     "expected pointer, found '{}'",
  22692                     .{ty.fmt(mod)},
  22693                 );
  22694                 errdefer msg.destroy(sema.gpa);
  22695 
  22696                 try sema.errNote(block, ty_src, msg, "use '&' to obtain a function pointer", .{});
  22697 
  22698                 break :msg msg;
  22699             };
  22700             return sema.failWithOwnedErrorMsg(block, msg);
  22701         },
  22702         .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return,
  22703         else => {},
  22704     }
  22705     return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)});
  22706 }
  22707 
  22708 fn checkPtrType(
  22709     sema: *Sema,
  22710     block: *Block,
  22711     ty_src: LazySrcLoc,
  22712     ty: Type,
  22713     allow_slice: bool,
  22714 ) CompileError!void {
  22715     const mod = sema.mod;
  22716     switch (ty.zigTypeTag(mod)) {
  22717         .Pointer => if (allow_slice or !ty.isSlice(mod)) return,
  22718         .Fn => {
  22719             const msg = msg: {
  22720                 const msg = try sema.errMsg(
  22721                     block,
  22722                     ty_src,
  22723                     "expected pointer type, found '{}'",
  22724                     .{ty.fmt(mod)},
  22725                 );
  22726                 errdefer msg.destroy(sema.gpa);
  22727 
  22728                 try sema.errNote(block, ty_src, msg, "use '*const ' to make a function pointer type", .{});
  22729 
  22730                 break :msg msg;
  22731             };
  22732             return sema.failWithOwnedErrorMsg(block, msg);
  22733         },
  22734         .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return,
  22735         else => {},
  22736     }
  22737     return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)});
  22738 }
  22739 
  22740 fn checkVectorElemType(
  22741     sema: *Sema,
  22742     block: *Block,
  22743     ty_src: LazySrcLoc,
  22744     ty: Type,
  22745 ) CompileError!void {
  22746     const mod = sema.mod;
  22747     switch (ty.zigTypeTag(mod)) {
  22748         .Int, .Float, .Bool => return,
  22749         else => if (ty.isPtrAtRuntime(mod)) return,
  22750     }
  22751     return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(mod)});
  22752 }
  22753 
  22754 fn checkFloatType(
  22755     sema: *Sema,
  22756     block: *Block,
  22757     ty_src: LazySrcLoc,
  22758     ty: Type,
  22759 ) CompileError!void {
  22760     const mod = sema.mod;
  22761     switch (ty.zigTypeTag(mod)) {
  22762         .ComptimeInt, .ComptimeFloat, .Float => {},
  22763         else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(mod)}),
  22764     }
  22765 }
  22766 
  22767 fn checkNumericType(
  22768     sema: *Sema,
  22769     block: *Block,
  22770     ty_src: LazySrcLoc,
  22771     ty: Type,
  22772 ) CompileError!void {
  22773     const mod = sema.mod;
  22774     switch (ty.zigTypeTag(mod)) {
  22775         .ComptimeFloat, .Float, .ComptimeInt, .Int => {},
  22776         .Vector => switch (ty.childType(mod).zigTypeTag(mod)) {
  22777             .ComptimeFloat, .Float, .ComptimeInt, .Int => {},
  22778             else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}),
  22779         },
  22780         else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(mod)}),
  22781     }
  22782 }
  22783 
  22784 /// Returns the casted pointer.
  22785 fn checkAtomicPtrOperand(
  22786     sema: *Sema,
  22787     block: *Block,
  22788     elem_ty: Type,
  22789     elem_ty_src: LazySrcLoc,
  22790     ptr: Air.Inst.Ref,
  22791     ptr_src: LazySrcLoc,
  22792     ptr_const: bool,
  22793 ) CompileError!Air.Inst.Ref {
  22794     const mod = sema.mod;
  22795     var diag: Module.AtomicPtrAlignmentDiagnostics = .{};
  22796     const alignment = mod.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) {
  22797         error.OutOfMemory => return error.OutOfMemory,
  22798         error.FloatTooBig => return sema.fail(
  22799             block,
  22800             elem_ty_src,
  22801             "expected {d}-bit float type or smaller; found {d}-bit float type",
  22802             .{ diag.max_bits, diag.bits },
  22803         ),
  22804         error.IntTooBig => return sema.fail(
  22805             block,
  22806             elem_ty_src,
  22807             "expected {d}-bit integer type or smaller; found {d}-bit integer type",
  22808             .{ diag.max_bits, diag.bits },
  22809         ),
  22810         error.BadType => return sema.fail(
  22811             block,
  22812             elem_ty_src,
  22813             "expected bool, integer, float, enum, or pointer type; found '{}'",
  22814             .{elem_ty.fmt(mod)},
  22815         ),
  22816     };
  22817 
  22818     var wanted_ptr_data: InternPool.Key.PtrType = .{
  22819         .child = elem_ty.toIntern(),
  22820         .flags = .{
  22821             .alignment = alignment,
  22822             .is_const = ptr_const,
  22823         },
  22824     };
  22825 
  22826     const ptr_ty = sema.typeOf(ptr);
  22827     const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison(mod)) {
  22828         .Pointer => ptr_ty.ptrInfo(mod),
  22829         else => {
  22830             const wanted_ptr_ty = try sema.ptrType(wanted_ptr_data);
  22831             _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
  22832             unreachable;
  22833         },
  22834     };
  22835 
  22836     wanted_ptr_data.flags.address_space = ptr_data.flags.address_space;
  22837     wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero;
  22838     wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile;
  22839 
  22840     const wanted_ptr_ty = try sema.ptrType(wanted_ptr_data);
  22841     const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
  22842 
  22843     return casted_ptr;
  22844 }
  22845 
  22846 fn checkPtrIsNotComptimeMutable(
  22847     sema: *Sema,
  22848     block: *Block,
  22849     ptr_val: Value,
  22850     ptr_src: LazySrcLoc,
  22851     operand_src: LazySrcLoc,
  22852 ) CompileError!void {
  22853     _ = operand_src;
  22854     if (ptr_val.isComptimeMutablePtr(sema.mod)) {
  22855         return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{});
  22856     }
  22857 }
  22858 
  22859 fn checkComptimeVarStore(
  22860     sema: *Sema,
  22861     block: *Block,
  22862     src: LazySrcLoc,
  22863     decl_ref_mut: InternPool.Key.Ptr.Addr.MutDecl,
  22864 ) CompileError!void {
  22865     if (@intFromEnum(decl_ref_mut.runtime_index) < @intFromEnum(block.runtime_index)) {
  22866         if (block.runtime_cond) |cond_src| {
  22867             const msg = msg: {
  22868                 const msg = try sema.errMsg(block, src, "store to comptime variable depends on runtime condition", .{});
  22869                 errdefer msg.destroy(sema.gpa);
  22870                 try sema.errNote(block, cond_src, msg, "runtime condition here", .{});
  22871                 break :msg msg;
  22872             };
  22873             return sema.failWithOwnedErrorMsg(block, msg);
  22874         }
  22875         if (block.runtime_loop) |loop_src| {
  22876             const msg = msg: {
  22877                 const msg = try sema.errMsg(block, src, "cannot store to comptime variable in non-inline loop", .{});
  22878                 errdefer msg.destroy(sema.gpa);
  22879                 try sema.errNote(block, loop_src, msg, "non-inline loop here", .{});
  22880                 break :msg msg;
  22881             };
  22882             return sema.failWithOwnedErrorMsg(block, msg);
  22883         }
  22884         unreachable;
  22885     }
  22886 }
  22887 
  22888 fn checkIntOrVector(
  22889     sema: *Sema,
  22890     block: *Block,
  22891     operand: Air.Inst.Ref,
  22892     operand_src: LazySrcLoc,
  22893 ) CompileError!Type {
  22894     const mod = sema.mod;
  22895     const operand_ty = sema.typeOf(operand);
  22896     switch (try operand_ty.zigTypeTagOrPoison(mod)) {
  22897         .Int => return operand_ty,
  22898         .Vector => {
  22899             const elem_ty = operand_ty.childType(mod);
  22900             switch (try elem_ty.zigTypeTagOrPoison(mod)) {
  22901                 .Int => return elem_ty,
  22902                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{
  22903                     elem_ty.fmt(mod),
  22904                 }),
  22905             }
  22906         },
  22907         else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{
  22908             operand_ty.fmt(mod),
  22909         }),
  22910     }
  22911 }
  22912 
  22913 fn checkIntOrVectorAllowComptime(
  22914     sema: *Sema,
  22915     block: *Block,
  22916     operand_ty: Type,
  22917     operand_src: LazySrcLoc,
  22918 ) CompileError!Type {
  22919     const mod = sema.mod;
  22920     switch (try operand_ty.zigTypeTagOrPoison(mod)) {
  22921         .Int, .ComptimeInt => return operand_ty,
  22922         .Vector => {
  22923             const elem_ty = operand_ty.childType(mod);
  22924             switch (try elem_ty.zigTypeTagOrPoison(mod)) {
  22925                 .Int, .ComptimeInt => return elem_ty,
  22926                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{
  22927                     elem_ty.fmt(mod),
  22928                 }),
  22929             }
  22930         },
  22931         else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{
  22932             operand_ty.fmt(mod),
  22933         }),
  22934     }
  22935 }
  22936 
  22937 const SimdBinOp = struct {
  22938     len: ?usize,
  22939     /// Coerced to `result_ty`.
  22940     lhs: Air.Inst.Ref,
  22941     /// Coerced to `result_ty`.
  22942     rhs: Air.Inst.Ref,
  22943     lhs_val: ?Value,
  22944     rhs_val: ?Value,
  22945     /// Only different than `scalar_ty` when it is a vector operation.
  22946     result_ty: Type,
  22947     scalar_ty: Type,
  22948 };
  22949 
  22950 fn checkSimdBinOp(
  22951     sema: *Sema,
  22952     block: *Block,
  22953     src: LazySrcLoc,
  22954     uncasted_lhs: Air.Inst.Ref,
  22955     uncasted_rhs: Air.Inst.Ref,
  22956     lhs_src: LazySrcLoc,
  22957     rhs_src: LazySrcLoc,
  22958 ) CompileError!SimdBinOp {
  22959     const mod = sema.mod;
  22960     const lhs_ty = sema.typeOf(uncasted_lhs);
  22961     const rhs_ty = sema.typeOf(uncasted_rhs);
  22962 
  22963     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  22964     const vec_len: ?usize = if (lhs_ty.zigTypeTag(mod) == .Vector) lhs_ty.vectorLen(mod) else null;
  22965     const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{
  22966         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  22967     });
  22968     const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src);
  22969     const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src);
  22970 
  22971     return SimdBinOp{
  22972         .len = vec_len,
  22973         .lhs = lhs,
  22974         .rhs = rhs,
  22975         .lhs_val = try sema.resolveValue(lhs),
  22976         .rhs_val = try sema.resolveValue(rhs),
  22977         .result_ty = result_ty,
  22978         .scalar_ty = result_ty.scalarType(mod),
  22979     };
  22980 }
  22981 
  22982 fn checkVectorizableBinaryOperands(
  22983     sema: *Sema,
  22984     block: *Block,
  22985     src: LazySrcLoc,
  22986     lhs_ty: Type,
  22987     rhs_ty: Type,
  22988     lhs_src: LazySrcLoc,
  22989     rhs_src: LazySrcLoc,
  22990 ) CompileError!void {
  22991     const mod = sema.mod;
  22992     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  22993     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  22994     if (lhs_zig_ty_tag != .Vector and rhs_zig_ty_tag != .Vector) return;
  22995 
  22996     const lhs_is_vector = switch (lhs_zig_ty_tag) {
  22997         .Vector, .Array => true,
  22998         else => false,
  22999     };
  23000     const rhs_is_vector = switch (rhs_zig_ty_tag) {
  23001         .Vector, .Array => true,
  23002         else => false,
  23003     };
  23004 
  23005     if (lhs_is_vector and rhs_is_vector) {
  23006         const lhs_len = lhs_ty.arrayLen(mod);
  23007         const rhs_len = rhs_ty.arrayLen(mod);
  23008         if (lhs_len != rhs_len) {
  23009             const msg = msg: {
  23010                 const msg = try sema.errMsg(block, src, "vector length mismatch", .{});
  23011                 errdefer msg.destroy(sema.gpa);
  23012                 try sema.errNote(block, lhs_src, msg, "length {d} here", .{lhs_len});
  23013                 try sema.errNote(block, rhs_src, msg, "length {d} here", .{rhs_len});
  23014                 break :msg msg;
  23015             };
  23016             return sema.failWithOwnedErrorMsg(block, msg);
  23017         }
  23018     } else {
  23019         const msg = msg: {
  23020             const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: '{}' and '{}'", .{
  23021                 lhs_ty.fmt(mod), rhs_ty.fmt(mod),
  23022             });
  23023             errdefer msg.destroy(sema.gpa);
  23024             if (lhs_is_vector) {
  23025                 try sema.errNote(block, lhs_src, msg, "vector here", .{});
  23026                 try sema.errNote(block, rhs_src, msg, "scalar here", .{});
  23027             } else {
  23028                 try sema.errNote(block, lhs_src, msg, "scalar here", .{});
  23029                 try sema.errNote(block, rhs_src, msg, "vector here", .{});
  23030             }
  23031             break :msg msg;
  23032         };
  23033         return sema.failWithOwnedErrorMsg(block, msg);
  23034     }
  23035 }
  23036 
  23037 fn maybeOptionsSrc(sema: *Sema, block: *Block, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc {
  23038     if (base_src == .unneeded) return .unneeded;
  23039     const mod = sema.mod;
  23040     return mod.optionsSrc(mod.declPtr(block.src_decl), base_src, wanted);
  23041 }
  23042 
  23043 fn resolveExportOptions(
  23044     sema: *Sema,
  23045     block: *Block,
  23046     src: LazySrcLoc,
  23047     zir_ref: Zir.Inst.Ref,
  23048 ) CompileError!Module.Export.Options {
  23049     const mod = sema.mod;
  23050     const gpa = sema.gpa;
  23051     const ip = &mod.intern_pool;
  23052     const export_options_ty = try sema.getBuiltinType("ExportOptions");
  23053     const air_ref = try sema.resolveInst(zir_ref);
  23054     const options = try sema.coerce(block, export_options_ty, air_ref, src);
  23055 
  23056     const name_src = sema.maybeOptionsSrc(block, src, "name");
  23057     const linkage_src = sema.maybeOptionsSrc(block, src, "linkage");
  23058     const section_src = sema.maybeOptionsSrc(block, src, "section");
  23059     const visibility_src = sema.maybeOptionsSrc(block, src, "visibility");
  23060 
  23061     const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src);
  23062     const name_val = try sema.resolveConstDefinedValue(block, name_src, name_operand, .{
  23063         .needed_comptime_reason = "name of exported value must be comptime-known",
  23064     });
  23065     const name_ty = Type.slice_const_u8;
  23066     const name = try name_val.toAllocatedBytes(name_ty, sema.arena, mod);
  23067 
  23068     const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src);
  23069     const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_operand, .{
  23070         .needed_comptime_reason = "linkage of exported value must be comptime-known",
  23071     });
  23072     const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val);
  23073 
  23074     const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "section"), section_src);
  23075     const section_opt_val = try sema.resolveConstDefinedValue(block, section_src, section_operand, .{
  23076         .needed_comptime_reason = "linksection of exported value must be comptime-known",
  23077     });
  23078     const section_ty = Type.slice_const_u8;
  23079     const section = if (section_opt_val.optionalValue(mod)) |section_val|
  23080         try section_val.toAllocatedBytes(section_ty, sema.arena, mod)
  23081     else
  23082         null;
  23083 
  23084     const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "visibility"), visibility_src);
  23085     const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_operand, .{
  23086         .needed_comptime_reason = "visibility of exported value must be comptime-known",
  23087     });
  23088     const visibility = mod.toEnum(std.builtin.SymbolVisibility, visibility_val);
  23089 
  23090     if (name.len < 1) {
  23091         return sema.fail(block, name_src, "exported symbol name cannot be empty", .{});
  23092     }
  23093 
  23094     if (visibility != .default and linkage == .Internal) {
  23095         return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{
  23096             name, @tagName(visibility),
  23097         });
  23098     }
  23099 
  23100     return .{
  23101         .name = try ip.getOrPutString(gpa, name),
  23102         .linkage = linkage,
  23103         .section = try ip.getOrPutStringOpt(gpa, section),
  23104         .visibility = visibility,
  23105     };
  23106 }
  23107 
  23108 fn resolveBuiltinEnum(
  23109     sema: *Sema,
  23110     block: *Block,
  23111     src: LazySrcLoc,
  23112     zir_ref: Zir.Inst.Ref,
  23113     comptime name: []const u8,
  23114     reason: NeededComptimeReason,
  23115 ) CompileError!@field(std.builtin, name) {
  23116     const mod = sema.mod;
  23117     const ty = try sema.getBuiltinType(name);
  23118     const air_ref = try sema.resolveInst(zir_ref);
  23119     const coerced = try sema.coerce(block, ty, air_ref, src);
  23120     const val = try sema.resolveConstDefinedValue(block, src, coerced, reason);
  23121     return mod.toEnum(@field(std.builtin, name), val);
  23122 }
  23123 
  23124 fn resolveAtomicOrder(
  23125     sema: *Sema,
  23126     block: *Block,
  23127     src: LazySrcLoc,
  23128     zir_ref: Zir.Inst.Ref,
  23129     reason: NeededComptimeReason,
  23130 ) CompileError!std.builtin.AtomicOrder {
  23131     return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicOrder", reason);
  23132 }
  23133 
  23134 fn resolveAtomicRmwOp(
  23135     sema: *Sema,
  23136     block: *Block,
  23137     src: LazySrcLoc,
  23138     zir_ref: Zir.Inst.Ref,
  23139 ) CompileError!std.builtin.AtomicRmwOp {
  23140     return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicRmwOp", .{
  23141         .needed_comptime_reason = "@atomicRmW operation must be comptime-known",
  23142     });
  23143 }
  23144 
  23145 fn zirCmpxchg(
  23146     sema: *Sema,
  23147     block: *Block,
  23148     extended: Zir.Inst.Extended.InstData,
  23149 ) CompileError!Air.Inst.Ref {
  23150     const mod = sema.mod;
  23151     const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data;
  23152     const air_tag: Air.Inst.Tag = switch (extended.small) {
  23153         0 => .cmpxchg_weak,
  23154         1 => .cmpxchg_strong,
  23155         else => unreachable,
  23156     };
  23157     const src = LazySrcLoc.nodeOffset(extra.node);
  23158     // zig fmt: off
  23159     const elem_ty_src      : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  23160     const ptr_src          : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  23161     const expected_src     : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node };
  23162     const new_value_src    : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node };
  23163     const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = extra.node };
  23164     const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = extra.node };
  23165     // zig fmt: on
  23166     const expected_value = try sema.resolveInst(extra.expected_value);
  23167     const elem_ty = sema.typeOf(expected_value);
  23168     if (elem_ty.zigTypeTag(mod) == .Float) {
  23169         return sema.fail(
  23170             block,
  23171             elem_ty_src,
  23172             "expected bool, integer, enum, or pointer type; found '{}'",
  23173             .{elem_ty.fmt(mod)},
  23174         );
  23175     }
  23176     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  23177     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  23178     const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src);
  23179     const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, .{
  23180         .needed_comptime_reason = "atomic order of cmpxchg success must be comptime-known",
  23181     });
  23182     const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, .{
  23183         .needed_comptime_reason = "atomic order of cmpxchg failure must be comptime-known",
  23184     });
  23185 
  23186     if (@intFromEnum(success_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) {
  23187         return sema.fail(block, success_order_src, "success atomic ordering must be Monotonic or stricter", .{});
  23188     }
  23189     if (@intFromEnum(failure_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) {
  23190         return sema.fail(block, failure_order_src, "failure atomic ordering must be Monotonic or stricter", .{});
  23191     }
  23192     if (@intFromEnum(failure_order) > @intFromEnum(success_order)) {
  23193         return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{});
  23194     }
  23195     if (failure_order == .Release or failure_order == .AcqRel) {
  23196         return sema.fail(block, failure_order_src, "failure atomic ordering must not be Release or AcqRel", .{});
  23197     }
  23198 
  23199     const result_ty = try mod.optionalType(elem_ty.toIntern());
  23200 
  23201     // special case zero bit types
  23202     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
  23203         return Air.internedToRef((try mod.intern(.{ .opt = .{
  23204             .ty = result_ty.toIntern(),
  23205             .val = .none,
  23206         } })));
  23207     }
  23208 
  23209     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  23210         if (try sema.resolveValue(expected_value)) |expected_val| {
  23211             if (try sema.resolveValue(new_value)) |new_val| {
  23212                 if (expected_val.isUndef(mod) or new_val.isUndef(mod)) {
  23213                     // TODO: this should probably cause the memory stored at the pointer
  23214                     // to become undef as well
  23215                     return mod.undefRef(result_ty);
  23216                 }
  23217                 const ptr_ty = sema.typeOf(ptr);
  23218                 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
  23219                 const result_val = try mod.intern(.{ .opt = .{
  23220                     .ty = result_ty.toIntern(),
  23221                     .val = if (stored_val.eql(expected_val, elem_ty, mod)) blk: {
  23222                         try sema.storePtr(block, src, ptr, new_value);
  23223                         break :blk .none;
  23224                     } else stored_val.toIntern(),
  23225                 } });
  23226                 return Air.internedToRef(result_val);
  23227             } else break :rs new_value_src;
  23228         } else break :rs expected_src;
  23229     } else ptr_src;
  23230 
  23231     const flags: u32 = @as(u32, @intFromEnum(success_order)) |
  23232         (@as(u32, @intFromEnum(failure_order)) << 3);
  23233 
  23234     try sema.requireRuntimeBlock(block, src, runtime_src);
  23235     return block.addInst(.{
  23236         .tag = air_tag,
  23237         .data = .{ .ty_pl = .{
  23238             .ty = Air.internedToRef(result_ty.toIntern()),
  23239             .payload = try sema.addExtra(Air.Cmpxchg{
  23240                 .ptr = ptr,
  23241                 .expected_value = expected_value,
  23242                 .new_value = new_value,
  23243                 .flags = flags,
  23244             }),
  23245         } },
  23246     });
  23247 }
  23248 
  23249 fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23250     const mod = sema.mod;
  23251     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23252     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23253     const src = inst_data.src();
  23254     const scalar_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23255     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@splat");
  23256 
  23257     if (!dest_ty.isVector(mod)) return sema.fail(block, src, "expected vector type, found '{}'", .{dest_ty.fmt(mod)});
  23258 
  23259     const operand = try sema.resolveInst(extra.rhs);
  23260     const scalar_ty = dest_ty.childType(mod);
  23261     const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src);
  23262     if (try sema.resolveValue(scalar)) |scalar_val| {
  23263         if (scalar_val.isUndef(mod)) return mod.undefRef(dest_ty);
  23264         return Air.internedToRef((try sema.splat(dest_ty, scalar_val)).toIntern());
  23265     }
  23266 
  23267     try sema.requireRuntimeBlock(block, inst_data.src(), scalar_src);
  23268     return block.addTyOp(.splat, dest_ty, scalar);
  23269 }
  23270 
  23271 fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23272     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23273     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23274     const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23275     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23276     const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp", .{
  23277         .needed_comptime_reason = "@reduce operation must be comptime-known",
  23278     });
  23279     const operand = try sema.resolveInst(extra.rhs);
  23280     const operand_ty = sema.typeOf(operand);
  23281     const mod = sema.mod;
  23282 
  23283     if (operand_ty.zigTypeTag(mod) != .Vector) {
  23284         return sema.fail(block, operand_src, "expected vector, found '{}'", .{operand_ty.fmt(mod)});
  23285     }
  23286 
  23287     const scalar_ty = operand_ty.childType(mod);
  23288 
  23289     // Type-check depending on operation.
  23290     switch (operation) {
  23291         .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(mod)) {
  23292             .Int, .Bool => {},
  23293             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{}'", .{
  23294                 @tagName(operation), operand_ty.fmt(mod),
  23295             }),
  23296         },
  23297         .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(mod)) {
  23298             .Int, .Float => {},
  23299             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{}'", .{
  23300                 @tagName(operation), operand_ty.fmt(mod),
  23301             }),
  23302         },
  23303     }
  23304 
  23305     const vec_len = operand_ty.vectorLen(mod);
  23306     if (vec_len == 0) {
  23307         // TODO re-evaluate if we should introduce a "neutral value" for some operations,
  23308         // e.g. zero for add and one for mul.
  23309         return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{});
  23310     }
  23311 
  23312     if (try sema.resolveValue(operand)) |operand_val| {
  23313         if (operand_val.isUndef(mod)) return mod.undefRef(scalar_ty);
  23314 
  23315         var accum: Value = try operand_val.elemValue(mod, 0);
  23316         var i: u32 = 1;
  23317         while (i < vec_len) : (i += 1) {
  23318             const elem_val = try operand_val.elemValue(mod, i);
  23319             switch (operation) {
  23320                 .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, mod),
  23321                 .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, mod),
  23322                 .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, mod),
  23323                 .Min => accum = accum.numberMin(elem_val, mod),
  23324                 .Max => accum = accum.numberMax(elem_val, mod),
  23325                 .Add => accum = try sema.numberAddWrapScalar(accum, elem_val, scalar_ty),
  23326                 .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, mod),
  23327             }
  23328         }
  23329         return Air.internedToRef(accum.toIntern());
  23330     }
  23331 
  23332     try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
  23333     return block.addInst(.{
  23334         .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  23335         .data = .{ .reduce = .{
  23336             .operand = operand,
  23337             .operation = operation,
  23338         } },
  23339     });
  23340 }
  23341 
  23342 fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23343     const mod = sema.mod;
  23344     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23345     const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
  23346     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23347     const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  23348 
  23349     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  23350     try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
  23351     const a = try sema.resolveInst(extra.a);
  23352     const b = try sema.resolveInst(extra.b);
  23353     var mask = try sema.resolveInst(extra.mask);
  23354     var mask_ty = sema.typeOf(mask);
  23355 
  23356     const mask_len = switch (sema.typeOf(mask).zigTypeTag(mod)) {
  23357         .Array, .Vector => sema.typeOf(mask).arrayLen(mod),
  23358         else => return sema.fail(block, mask_src, "expected vector or array, found '{}'", .{sema.typeOf(mask).fmt(sema.mod)}),
  23359     };
  23360     mask_ty = try mod.vectorType(.{
  23361         .len = @intCast(mask_len),
  23362         .child = .i32_type,
  23363     });
  23364     mask = try sema.coerce(block, mask_ty, mask, mask_src);
  23365     const mask_val = try sema.resolveConstValue(block, mask_src, mask, .{
  23366         .needed_comptime_reason = "shuffle mask must be comptime-known",
  23367     });
  23368     return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(mask_len));
  23369 }
  23370 
  23371 fn analyzeShuffle(
  23372     sema: *Sema,
  23373     block: *Block,
  23374     src_node: i32,
  23375     elem_ty: Type,
  23376     a_arg: Air.Inst.Ref,
  23377     b_arg: Air.Inst.Ref,
  23378     mask: Value,
  23379     mask_len: u32,
  23380 ) CompileError!Air.Inst.Ref {
  23381     const mod = sema.mod;
  23382     const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node };
  23383     const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node };
  23384     const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node };
  23385     var a = a_arg;
  23386     var b = b_arg;
  23387 
  23388     const res_ty = try mod.vectorType(.{
  23389         .len = mask_len,
  23390         .child = elem_ty.toIntern(),
  23391     });
  23392 
  23393     const maybe_a_len = switch (sema.typeOf(a).zigTypeTag(mod)) {
  23394         .Array, .Vector => sema.typeOf(a).arrayLen(mod),
  23395         .Undefined => null,
  23396         else => return sema.fail(block, a_src, "expected vector or array with element type '{}', found '{}'", .{
  23397             elem_ty.fmt(sema.mod),
  23398             sema.typeOf(a).fmt(sema.mod),
  23399         }),
  23400     };
  23401     const maybe_b_len = switch (sema.typeOf(b).zigTypeTag(mod)) {
  23402         .Array, .Vector => sema.typeOf(b).arrayLen(mod),
  23403         .Undefined => null,
  23404         else => return sema.fail(block, b_src, "expected vector or array with element type '{}', found '{}'", .{
  23405             elem_ty.fmt(sema.mod),
  23406             sema.typeOf(b).fmt(sema.mod),
  23407         }),
  23408     };
  23409     if (maybe_a_len == null and maybe_b_len == null) {
  23410         return mod.undefRef(res_ty);
  23411     }
  23412     const a_len: u32 = @intCast(maybe_a_len orelse maybe_b_len.?);
  23413     const b_len: u32 = @intCast(maybe_b_len orelse a_len);
  23414 
  23415     const a_ty = try mod.vectorType(.{
  23416         .len = a_len,
  23417         .child = elem_ty.toIntern(),
  23418     });
  23419     const b_ty = try mod.vectorType(.{
  23420         .len = b_len,
  23421         .child = elem_ty.toIntern(),
  23422     });
  23423 
  23424     if (maybe_a_len == null) a = try mod.undefRef(a_ty) else a = try sema.coerce(block, a_ty, a, a_src);
  23425     if (maybe_b_len == null) b = try mod.undefRef(b_ty) else b = try sema.coerce(block, b_ty, b, b_src);
  23426 
  23427     const operand_info = [2]std.meta.Tuple(&.{ u64, LazySrcLoc, Type }){
  23428         .{ a_len, a_src, a_ty },
  23429         .{ b_len, b_src, b_ty },
  23430     };
  23431 
  23432     for (0..@intCast(mask_len)) |i| {
  23433         const elem = try mask.elemValue(sema.mod, i);
  23434         if (elem.isUndef(mod)) continue;
  23435         const int = elem.toSignedInt(mod);
  23436         var unsigned: u32 = undefined;
  23437         var chosen: u32 = undefined;
  23438         if (int >= 0) {
  23439             unsigned = @intCast(int);
  23440             chosen = 0;
  23441         } else {
  23442             unsigned = @intCast(~int);
  23443             chosen = 1;
  23444         }
  23445         if (unsigned >= operand_info[chosen][0]) {
  23446             const msg = msg: {
  23447                 const msg = try sema.errMsg(block, mask_src, "mask index '{d}' has out-of-bounds selection", .{i});
  23448                 errdefer msg.destroy(sema.gpa);
  23449 
  23450                 try sema.errNote(block, operand_info[chosen][1], msg, "selected index '{d}' out of bounds of '{}'", .{
  23451                     unsigned,
  23452                     operand_info[chosen][2].fmt(sema.mod),
  23453                 });
  23454 
  23455                 if (chosen == 0) {
  23456                     try sema.errNote(block, b_src, msg, "selections from the second vector are specified with negative numbers", .{});
  23457                 }
  23458 
  23459                 break :msg msg;
  23460             };
  23461             return sema.failWithOwnedErrorMsg(block, msg);
  23462         }
  23463     }
  23464 
  23465     if (try sema.resolveValue(a)) |a_val| {
  23466         if (try sema.resolveValue(b)) |b_val| {
  23467             const values = try sema.arena.alloc(InternPool.Index, mask_len);
  23468             for (values, 0..) |*value, i| {
  23469                 const mask_elem_val = try mask.elemValue(sema.mod, i);
  23470                 if (mask_elem_val.isUndef(mod)) {
  23471                     value.* = try mod.intern(.{ .undef = elem_ty.toIntern() });
  23472                     continue;
  23473                 }
  23474                 const int = mask_elem_val.toSignedInt(mod);
  23475                 const unsigned: u32 = @intCast(if (int >= 0) int else ~int);
  23476                 values[i] = try (try (if (int >= 0) a_val else b_val).elemValue(mod, unsigned)).intern(elem_ty, mod);
  23477             }
  23478             return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  23479                 .ty = res_ty.toIntern(),
  23480                 .storage = .{ .elems = values },
  23481             } })));
  23482         }
  23483     }
  23484 
  23485     // All static analysis passed, and not comptime.
  23486     // For runtime codegen, vectors a and b must be the same length. Here we
  23487     // recursively @shuffle the smaller vector to append undefined elements
  23488     // to it up to the length of the longer vector. This recursion terminates
  23489     // in 1 call because these calls to analyzeShuffle guarantee a_len == b_len.
  23490     if (a_len != b_len) {
  23491         const min_len = @min(a_len, b_len);
  23492         const max_src = if (a_len > b_len) a_src else b_src;
  23493         const max_len = try sema.usizeCast(block, max_src, @max(a_len, b_len));
  23494 
  23495         const expand_mask_values = try sema.arena.alloc(InternPool.Index, max_len);
  23496         for (@intCast(0)..@intCast(min_len)) |i| {
  23497             expand_mask_values[i] = (try mod.intValue(Type.comptime_int, i)).toIntern();
  23498         }
  23499         for (@intCast(min_len)..@intCast(max_len)) |i| {
  23500             expand_mask_values[i] = (try mod.intValue(Type.comptime_int, -1)).toIntern();
  23501         }
  23502         const expand_mask = try mod.intern(.{ .aggregate = .{
  23503             .ty = (try mod.vectorType(.{ .len = @intCast(max_len), .child = .comptime_int_type })).toIntern(),
  23504             .storage = .{ .elems = expand_mask_values },
  23505         } });
  23506 
  23507         if (a_len < b_len) {
  23508             const undef = try mod.undefRef(a_ty);
  23509             a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, Value.fromInterned(expand_mask), @intCast(max_len));
  23510         } else {
  23511             const undef = try mod.undefRef(b_ty);
  23512             b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, Value.fromInterned(expand_mask), @intCast(max_len));
  23513         }
  23514     }
  23515 
  23516     return block.addInst(.{
  23517         .tag = .shuffle,
  23518         .data = .{ .ty_pl = .{
  23519             .ty = Air.internedToRef(res_ty.toIntern()),
  23520             .payload = try block.sema.addExtra(Air.Shuffle{
  23521                 .a = a,
  23522                 .b = b,
  23523                 .mask = mask.toIntern(),
  23524                 .mask_len = mask_len,
  23525             }),
  23526         } },
  23527     });
  23528 }
  23529 
  23530 fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  23531     const mod = sema.mod;
  23532     const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data;
  23533 
  23534     const src = LazySrcLoc.nodeOffset(extra.node);
  23535     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  23536     const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  23537     const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node };
  23538     const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node };
  23539 
  23540     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  23541     try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
  23542     const pred_uncoerced = try sema.resolveInst(extra.pred);
  23543     const pred_ty = sema.typeOf(pred_uncoerced);
  23544 
  23545     const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison(mod)) {
  23546         .Vector, .Array => pred_ty.arrayLen(mod),
  23547         else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(mod)}),
  23548     };
  23549     const vec_len: u32 = @intCast(try sema.usizeCast(block, pred_src, vec_len_u64));
  23550 
  23551     const bool_vec_ty = try mod.vectorType(.{
  23552         .len = vec_len,
  23553         .child = .bool_type,
  23554     });
  23555     const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src);
  23556 
  23557     const vec_ty = try mod.vectorType(.{
  23558         .len = vec_len,
  23559         .child = elem_ty.toIntern(),
  23560     });
  23561     const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src);
  23562     const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src);
  23563 
  23564     const maybe_pred = try sema.resolveValue(pred);
  23565     const maybe_a = try sema.resolveValue(a);
  23566     const maybe_b = try sema.resolveValue(b);
  23567 
  23568     const runtime_src = if (maybe_pred) |pred_val| rs: {
  23569         if (pred_val.isUndef(mod)) return mod.undefRef(vec_ty);
  23570 
  23571         if (maybe_a) |a_val| {
  23572             if (a_val.isUndef(mod)) return mod.undefRef(vec_ty);
  23573 
  23574             if (maybe_b) |b_val| {
  23575                 if (b_val.isUndef(mod)) return mod.undefRef(vec_ty);
  23576 
  23577                 const elems = try sema.gpa.alloc(InternPool.Index, vec_len);
  23578                 defer sema.gpa.free(elems);
  23579                 for (elems, 0..) |*elem, i| {
  23580                     const pred_elem_val = try pred_val.elemValue(mod, i);
  23581                     const should_choose_a = pred_elem_val.toBool();
  23582                     elem.* = try (try (if (should_choose_a) a_val else b_val).elemValue(mod, i)).intern(elem_ty, mod);
  23583                 }
  23584 
  23585                 return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  23586                     .ty = vec_ty.toIntern(),
  23587                     .storage = .{ .elems = elems },
  23588                 } })));
  23589             } else {
  23590                 break :rs b_src;
  23591             }
  23592         } else {
  23593             if (maybe_b) |b_val| {
  23594                 if (b_val.isUndef(mod)) return mod.undefRef(vec_ty);
  23595             }
  23596             break :rs a_src;
  23597         }
  23598     } else rs: {
  23599         if (maybe_a) |a_val| {
  23600             if (a_val.isUndef(mod)) return mod.undefRef(vec_ty);
  23601         }
  23602         if (maybe_b) |b_val| {
  23603             if (b_val.isUndef(mod)) return mod.undefRef(vec_ty);
  23604         }
  23605         break :rs pred_src;
  23606     };
  23607 
  23608     try sema.requireRuntimeBlock(block, src, runtime_src);
  23609     return block.addInst(.{
  23610         .tag = .select,
  23611         .data = .{ .pl_op = .{
  23612             .operand = pred,
  23613             .payload = try block.sema.addExtra(Air.Bin{
  23614                 .lhs = a,
  23615                 .rhs = b,
  23616             }),
  23617         } },
  23618     });
  23619 }
  23620 
  23621 fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23622     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23623     const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data;
  23624     // zig fmt: off
  23625     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23626     const ptr_src    : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23627     const order_src  : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  23628     // zig fmt: on
  23629     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  23630     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  23631     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true);
  23632     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{
  23633         .needed_comptime_reason = "atomic order of @atomicLoad must be comptime-known",
  23634     });
  23635 
  23636     switch (order) {
  23637         .Release, .AcqRel => {
  23638             return sema.fail(
  23639                 block,
  23640                 order_src,
  23641                 "@atomicLoad atomic ordering must not be Release or AcqRel",
  23642                 .{},
  23643             );
  23644         },
  23645         else => {},
  23646     }
  23647 
  23648     if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
  23649         return Air.internedToRef(val.toIntern());
  23650     }
  23651 
  23652     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
  23653         if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| {
  23654             return Air.internedToRef(elem_val.toIntern());
  23655         }
  23656     }
  23657 
  23658     try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);
  23659     return block.addInst(.{
  23660         .tag = .atomic_load,
  23661         .data = .{ .atomic_load = .{
  23662             .ptr = ptr,
  23663             .order = order,
  23664         } },
  23665     });
  23666 }
  23667 
  23668 fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23669     const mod = sema.mod;
  23670     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23671     const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data;
  23672     const src = inst_data.src();
  23673     // zig fmt: off
  23674     const elem_ty_src   : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23675     const ptr_src       : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23676     const op_src        : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  23677     const operand_src   : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  23678     const order_src     : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node };
  23679     // zig fmt: on
  23680     const operand = try sema.resolveInst(extra.operand);
  23681     const elem_ty = sema.typeOf(operand);
  23682     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  23683     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  23684     const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation);
  23685 
  23686     switch (elem_ty.zigTypeTag(mod)) {
  23687         .Enum => if (op != .Xchg) {
  23688             return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{});
  23689         },
  23690         .Bool => if (op != .Xchg) {
  23691             return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{});
  23692         },
  23693         .Float => switch (op) {
  23694             .Xchg, .Add, .Sub, .Max, .Min => {},
  23695             else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}),
  23696         },
  23697         else => {},
  23698     }
  23699     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{
  23700         .needed_comptime_reason = "atomic order of @atomicRmW must be comptime-known",
  23701     });
  23702 
  23703     if (order == .Unordered) {
  23704         return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be Unordered", .{});
  23705     }
  23706 
  23707     // special case zero bit types
  23708     if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
  23709         return Air.internedToRef(val.toIntern());
  23710     }
  23711 
  23712     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  23713         const maybe_operand_val = try sema.resolveValue(operand);
  23714         const operand_val = maybe_operand_val orelse {
  23715             try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src);
  23716             break :rs operand_src;
  23717         };
  23718         if (ptr_val.isComptimeMutablePtr(mod)) {
  23719             const ptr_ty = sema.typeOf(ptr);
  23720             const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
  23721             const new_val = switch (op) {
  23722                 // zig fmt: off
  23723                 .Xchg => operand_val,
  23724                 .Add  => try sema.numberAddWrapScalar(stored_val, operand_val, elem_ty),
  23725                 .Sub  => try sema.numberSubWrapScalar(stored_val, operand_val, elem_ty),
  23726                 .And  => try                   stored_val.bitwiseAnd   (operand_val, elem_ty, sema.arena, mod),
  23727                 .Nand => try                   stored_val.bitwiseNand  (operand_val, elem_ty, sema.arena, mod),
  23728                 .Or   => try                   stored_val.bitwiseOr    (operand_val, elem_ty, sema.arena, mod),
  23729                 .Xor  => try                   stored_val.bitwiseXor   (operand_val, elem_ty, sema.arena, mod),
  23730                 .Max  =>                       stored_val.numberMax    (operand_val,                      mod),
  23731                 .Min  =>                       stored_val.numberMin    (operand_val,                      mod),
  23732                 // zig fmt: on
  23733             };
  23734             try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty);
  23735             return Air.internedToRef(stored_val.toIntern());
  23736         } else break :rs ptr_src;
  23737     } else ptr_src;
  23738 
  23739     const flags: u32 = @as(u32, @intFromEnum(order)) | (@as(u32, @intFromEnum(op)) << 3);
  23740 
  23741     try sema.requireRuntimeBlock(block, src, runtime_src);
  23742     return block.addInst(.{
  23743         .tag = .atomic_rmw,
  23744         .data = .{ .pl_op = .{
  23745             .operand = ptr,
  23746             .payload = try sema.addExtra(Air.AtomicRmw{
  23747                 .operand = operand,
  23748                 .flags = flags,
  23749             }),
  23750         } },
  23751     });
  23752 }
  23753 
  23754 fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  23755     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23756     const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
  23757     const src = inst_data.src();
  23758     // zig fmt: off
  23759     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23760     const ptr_src       : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23761     const operand_src   : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  23762     const order_src     : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  23763     // zig fmt: on
  23764     const operand = try sema.resolveInst(extra.operand);
  23765     const elem_ty = sema.typeOf(operand);
  23766     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  23767     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  23768     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{
  23769         .needed_comptime_reason = "atomic order of @atomicStore must be comptime-known",
  23770     });
  23771 
  23772     const air_tag: Air.Inst.Tag = switch (order) {
  23773         .Acquire, .AcqRel => {
  23774             return sema.fail(
  23775                 block,
  23776                 order_src,
  23777                 "@atomicStore atomic ordering must not be Acquire or AcqRel",
  23778                 .{},
  23779             );
  23780         },
  23781         .Unordered => .atomic_store_unordered,
  23782         .Monotonic => .atomic_store_monotonic,
  23783         .Release => .atomic_store_release,
  23784         .SeqCst => .atomic_store_seq_cst,
  23785     };
  23786 
  23787     return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
  23788 }
  23789 
  23790 fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23791     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23792     const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data;
  23793     const src = inst_data.src();
  23794 
  23795     const mulend1_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23796     const mulend2_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  23797     const addend_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  23798 
  23799     const addend = try sema.resolveInst(extra.addend);
  23800     const ty = sema.typeOf(addend);
  23801     const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src);
  23802     const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src);
  23803 
  23804     const maybe_mulend1 = try sema.resolveValue(mulend1);
  23805     const maybe_mulend2 = try sema.resolveValue(mulend2);
  23806     const maybe_addend = try sema.resolveValue(addend);
  23807     const mod = sema.mod;
  23808 
  23809     switch (ty.scalarType(mod).zigTypeTag(mod)) {
  23810         .ComptimeFloat, .Float => {},
  23811         else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(sema.mod)}),
  23812     }
  23813 
  23814     const runtime_src = if (maybe_mulend1) |mulend1_val| rs: {
  23815         if (maybe_mulend2) |mulend2_val| {
  23816             if (mulend2_val.isUndef(mod)) return mod.undefRef(ty);
  23817 
  23818             if (maybe_addend) |addend_val| {
  23819                 if (addend_val.isUndef(mod)) return mod.undefRef(ty);
  23820                 const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, sema.mod);
  23821                 return Air.internedToRef(result_val.toIntern());
  23822             } else {
  23823                 break :rs addend_src;
  23824             }
  23825         } else {
  23826             if (maybe_addend) |addend_val| {
  23827                 if (addend_val.isUndef(mod)) return mod.undefRef(ty);
  23828             }
  23829             break :rs mulend2_src;
  23830         }
  23831     } else rs: {
  23832         if (maybe_mulend2) |mulend2_val| {
  23833             if (mulend2_val.isUndef(mod)) return mod.undefRef(ty);
  23834         }
  23835         if (maybe_addend) |addend_val| {
  23836             if (addend_val.isUndef(mod)) return mod.undefRef(ty);
  23837         }
  23838         break :rs mulend1_src;
  23839     };
  23840 
  23841     try sema.requireRuntimeBlock(block, src, runtime_src);
  23842     return block.addInst(.{
  23843         .tag = .mul_add,
  23844         .data = .{ .pl_op = .{
  23845             .operand = addend,
  23846             .payload = try sema.addExtra(Air.Bin{
  23847                 .lhs = mulend1,
  23848                 .rhs = mulend2,
  23849             }),
  23850         } },
  23851     });
  23852 }
  23853 
  23854 fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23855     const tracy = trace(@src());
  23856     defer tracy.end();
  23857 
  23858     const mod = sema.mod;
  23859     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23860     const modifier_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23861     const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23862     const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  23863     const call_src = inst_data.src();
  23864 
  23865     const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;
  23866     const func = try sema.resolveInst(extra.callee);
  23867 
  23868     const modifier_ty = try sema.getBuiltinType("CallModifier");
  23869     const air_ref = try sema.resolveInst(extra.modifier);
  23870     const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src);
  23871     const modifier_val = try sema.resolveConstDefinedValue(block, modifier_src, modifier_ref, .{
  23872         .needed_comptime_reason = "call modifier must be comptime-known",
  23873     });
  23874     var modifier = mod.toEnum(std.builtin.CallModifier, modifier_val);
  23875     switch (modifier) {
  23876         // These can be upgraded to comptime or nosuspend calls.
  23877         .auto, .never_tail, .no_async => {
  23878             if (block.is_comptime) {
  23879                 if (modifier == .never_tail) {
  23880                     return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{});
  23881                 }
  23882                 modifier = .compile_time;
  23883             } else if (extra.flags.is_nosuspend) {
  23884                 modifier = .no_async;
  23885             }
  23886         },
  23887         // These can be upgraded to comptime. nosuspend bit can be safely ignored.
  23888         .always_inline, .compile_time => {
  23889             _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse {
  23890                 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)});
  23891             };
  23892 
  23893             if (block.is_comptime) {
  23894                 modifier = .compile_time;
  23895             }
  23896         },
  23897         .always_tail => {
  23898             if (block.is_comptime) {
  23899                 modifier = .compile_time;
  23900             }
  23901         },
  23902         .async_kw => {
  23903             if (extra.flags.is_nosuspend) {
  23904                 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{});
  23905             }
  23906             if (block.is_comptime) {
  23907                 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{});
  23908             }
  23909         },
  23910         .never_inline => {
  23911             if (block.is_comptime) {
  23912                 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{});
  23913             }
  23914         },
  23915     }
  23916 
  23917     const args = try sema.resolveInst(extra.args);
  23918 
  23919     const args_ty = sema.typeOf(args);
  23920     if (!args_ty.isTuple(mod) and args_ty.toIntern() != .empty_struct_type) {
  23921         return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(sema.mod)});
  23922     }
  23923 
  23924     const resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(mod));
  23925     for (resolved_args, 0..) |*resolved, i| {
  23926         resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(i), args_ty);
  23927     }
  23928 
  23929     const callee_ty = sema.typeOf(func);
  23930     const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false);
  23931     const ensure_result_used = extra.flags.ensure_result_used;
  23932     return sema.analyzeCall(
  23933         block,
  23934         func,
  23935         func_ty,
  23936         func_src,
  23937         call_src,
  23938         modifier,
  23939         ensure_result_used,
  23940         .{ .call_builtin = .{
  23941             .call_node_offset = inst_data.src_node,
  23942             .args = resolved_args,
  23943         } },
  23944         null,
  23945         .@"@call",
  23946     );
  23947 }
  23948 
  23949 fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23950     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23951     const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data;
  23952     const src = inst_data.src();
  23953     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23954     const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23955     const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  23956 
  23957     const parent_ty = try sema.resolveType(block, ty_src, extra.parent_type);
  23958     const field_name = try sema.resolveConstStringIntern(block, name_src, extra.field_name, .{
  23959         .needed_comptime_reason = "field name must be comptime-known",
  23960     });
  23961     const field_ptr = try sema.resolveInst(extra.field_ptr);
  23962     const field_ptr_ty = sema.typeOf(field_ptr);
  23963     const mod = sema.mod;
  23964     const ip = &mod.intern_pool;
  23965 
  23966     if (parent_ty.zigTypeTag(mod) != .Struct and parent_ty.zigTypeTag(mod) != .Union) {
  23967         return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)});
  23968     }
  23969     try sema.resolveTypeLayout(parent_ty);
  23970 
  23971     const field_index = switch (parent_ty.zigTypeTag(mod)) {
  23972         .Struct => blk: {
  23973             if (parent_ty.isTuple(mod)) {
  23974                 if (ip.stringEqlSlice(field_name, "len")) {
  23975                     return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
  23976                 }
  23977                 break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, name_src);
  23978             } else {
  23979                 break :blk try sema.structFieldIndex(block, parent_ty, field_name, name_src);
  23980             }
  23981         },
  23982         .Union => try sema.unionFieldIndex(block, parent_ty, field_name, name_src),
  23983         else => unreachable,
  23984     };
  23985 
  23986     if (parent_ty.zigTypeTag(mod) == .Struct and parent_ty.structFieldIsComptime(field_index, mod)) {
  23987         return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{});
  23988     }
  23989 
  23990     try sema.checkPtrOperand(block, ptr_src, field_ptr_ty);
  23991     const field_ptr_ty_info = field_ptr_ty.ptrInfo(mod);
  23992 
  23993     var ptr_ty_data: InternPool.Key.PtrType = .{
  23994         .child = parent_ty.structFieldType(field_index, mod).toIntern(),
  23995         .flags = .{
  23996             .address_space = field_ptr_ty_info.flags.address_space,
  23997             .is_const = field_ptr_ty_info.flags.is_const,
  23998         },
  23999     };
  24000 
  24001     if (parent_ty.containerLayout(mod) == .Packed) {
  24002         return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{});
  24003     } else {
  24004         ptr_ty_data.flags.alignment = blk: {
  24005             if (mod.typeToStruct(parent_ty)) |struct_type| {
  24006                 break :blk struct_type.fieldAlign(ip, field_index);
  24007             } else if (mod.typeToUnion(parent_ty)) |union_obj| {
  24008                 break :blk union_obj.fieldAlign(ip, field_index);
  24009             } else {
  24010                 break :blk .none;
  24011             }
  24012         };
  24013     }
  24014 
  24015     const actual_field_ptr_ty = try sema.ptrType(ptr_ty_data);
  24016     const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src);
  24017 
  24018     ptr_ty_data.child = parent_ty.toIntern();
  24019     const result_ptr = try sema.ptrType(ptr_ty_data);
  24020 
  24021     if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| {
  24022         const field = switch (ip.indexToKey(field_ptr_val.toIntern())) {
  24023             .ptr => |ptr| switch (ptr.addr) {
  24024                 .field => |field| field,
  24025                 else => null,
  24026             },
  24027             else => null,
  24028         } orelse return sema.fail(block, ptr_src, "pointer value not based on parent struct", .{});
  24029 
  24030         if (field.index != field_index) {
  24031             const msg = msg: {
  24032                 const msg = try sema.errMsg(
  24033                     block,
  24034                     src,
  24035                     "field '{}' has index '{d}' but pointer value is index '{d}' of struct '{}'",
  24036                     .{
  24037                         field_name.fmt(ip),
  24038                         field_index,
  24039                         field.index,
  24040                         parent_ty.fmt(sema.mod),
  24041                     },
  24042                 );
  24043                 errdefer msg.destroy(sema.gpa);
  24044                 try sema.addDeclaredHereNote(msg, parent_ty);
  24045                 break :msg msg;
  24046             };
  24047             return sema.failWithOwnedErrorMsg(block, msg);
  24048         }
  24049         return Air.internedToRef(field.base);
  24050     }
  24051 
  24052     try sema.requireRuntimeBlock(block, src, ptr_src);
  24053     try sema.queueFullTypeResolution(result_ptr);
  24054     return block.addInst(.{
  24055         .tag = .field_parent_ptr,
  24056         .data = .{ .ty_pl = .{
  24057             .ty = Air.internedToRef(result_ptr.toIntern()),
  24058             .payload = try block.sema.addExtra(Air.FieldParentPtr{
  24059                 .field_ptr = casted_field_ptr,
  24060                 .field_index = @intCast(field_index),
  24061             }),
  24062         } },
  24063     });
  24064 }
  24065 
  24066 fn zirMinMax(
  24067     sema: *Sema,
  24068     block: *Block,
  24069     inst: Zir.Inst.Index,
  24070     comptime air_tag: Air.Inst.Tag,
  24071 ) CompileError!Air.Inst.Ref {
  24072     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24073     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  24074     const src = inst_data.src();
  24075     const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  24076     const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  24077     const lhs = try sema.resolveInst(extra.lhs);
  24078     const rhs = try sema.resolveInst(extra.rhs);
  24079     try sema.checkNumericType(block, lhs_src, sema.typeOf(lhs));
  24080     try sema.checkNumericType(block, rhs_src, sema.typeOf(rhs));
  24081     return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src });
  24082 }
  24083 
  24084 fn zirMinMaxMulti(
  24085     sema: *Sema,
  24086     block: *Block,
  24087     extended: Zir.Inst.Extended.InstData,
  24088     comptime air_tag: Air.Inst.Tag,
  24089 ) CompileError!Air.Inst.Ref {
  24090     const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
  24091     const src_node = extra.data.src_node;
  24092     const src = LazySrcLoc.nodeOffset(src_node);
  24093     const operands = sema.code.refSlice(extra.end, extended.small);
  24094 
  24095     const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
  24096     const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len);
  24097 
  24098     for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| {
  24099         op_src.* = switch (i) {
  24100             0 => .{ .node_offset_builtin_call_arg0 = src_node },
  24101             1 => .{ .node_offset_builtin_call_arg1 = src_node },
  24102             2 => .{ .node_offset_builtin_call_arg2 = src_node },
  24103             3 => .{ .node_offset_builtin_call_arg3 = src_node },
  24104             4 => .{ .node_offset_builtin_call_arg4 = src_node },
  24105             5 => .{ .node_offset_builtin_call_arg5 = src_node },
  24106             else => src, // TODO: better source location
  24107         };
  24108         air_ref.* = try sema.resolveInst(zir_ref);
  24109         try sema.checkNumericType(block, op_src.*, sema.typeOf(air_ref.*));
  24110     }
  24111 
  24112     return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs);
  24113 }
  24114 
  24115 fn analyzeMinMax(
  24116     sema: *Sema,
  24117     block: *Block,
  24118     src: LazySrcLoc,
  24119     comptime air_tag: Air.Inst.Tag,
  24120     operands: []const Air.Inst.Ref,
  24121     operand_srcs: []const LazySrcLoc,
  24122 ) CompileError!Air.Inst.Ref {
  24123     assert(operands.len == operand_srcs.len);
  24124     assert(operands.len > 0);
  24125     const mod = sema.mod;
  24126 
  24127     if (operands.len == 1) return operands[0];
  24128 
  24129     const opFunc = switch (air_tag) {
  24130         .min => Value.numberMin,
  24131         .max => Value.numberMax,
  24132         else => @compileError("unreachable"),
  24133     };
  24134 
  24135     // The set of runtime-known operands. Set up in the loop below.
  24136     var runtime_known = try std.DynamicBitSet.initFull(sema.arena, operands.len);
  24137     // The current minmax value - initially this will always be comptime-known, then we'll add
  24138     // runtime values into the mix later.
  24139     var cur_minmax: ?Air.Inst.Ref = null;
  24140     var cur_minmax_src: LazySrcLoc = undefined; // defined if cur_minmax not null
  24141     // The current known scalar bounds of the value.
  24142     var bounds_status: enum {
  24143         unknown, // We've only seen undef comptime_ints so far, so do not know the bounds.
  24144         defined, // We've seen only integers, so the bounds are defined.
  24145         non_integral, // There are floats in the mix, so the bounds aren't defined.
  24146     } = .unknown;
  24147     var cur_min_scalar: Value = undefined;
  24148     var cur_max_scalar: Value = undefined;
  24149 
  24150     // First, find all comptime-known arguments, and get their min/max
  24151 
  24152     for (operands, operand_srcs, 0..) |operand, operand_src, operand_idx| {
  24153         // Resolve the value now to avoid redundant calls to `checkSimdBinOp` - we'll have to call
  24154         // it in the runtime path anyway since the result type may have been refined
  24155         const unresolved_uncoerced_val = try sema.resolveValue(operand) orelse continue;
  24156         const uncoerced_val = try sema.resolveLazyValue(unresolved_uncoerced_val);
  24157 
  24158         runtime_known.unset(operand_idx);
  24159 
  24160         switch (bounds_status) {
  24161             .unknown, .defined => refine_bounds: {
  24162                 const ty = sema.typeOf(operand);
  24163                 if (!ty.scalarType(mod).isInt(mod) and !ty.scalarType(mod).eql(Type.comptime_int, mod)) {
  24164                     bounds_status = .non_integral;
  24165                     break :refine_bounds;
  24166                 }
  24167                 const scalar_bounds: ?[2]Value = bounds: {
  24168                     if (!ty.isVector(mod)) break :bounds try uncoerced_val.intValueBounds(mod);
  24169                     var cur_bounds: [2]Value = try Value.intValueBounds(try uncoerced_val.elemValue(mod, 0), mod) orelse break :bounds null;
  24170                     const len = try sema.usizeCast(block, src, ty.vectorLen(mod));
  24171                     for (1..len) |i| {
  24172                         const elem = try uncoerced_val.elemValue(mod, i);
  24173                         const elem_bounds = try elem.intValueBounds(mod) orelse break :bounds null;
  24174                         cur_bounds = .{
  24175                             Value.numberMin(elem_bounds[0], cur_bounds[0], mod),
  24176                             Value.numberMax(elem_bounds[1], cur_bounds[1], mod),
  24177                         };
  24178                     }
  24179                     break :bounds cur_bounds;
  24180                 };
  24181                 if (scalar_bounds) |bounds| {
  24182                     if (bounds_status == .unknown) {
  24183                         cur_min_scalar = bounds[0];
  24184                         cur_max_scalar = bounds[1];
  24185                         bounds_status = .defined;
  24186                     } else {
  24187                         cur_min_scalar = opFunc(cur_min_scalar, bounds[0], mod);
  24188                         cur_max_scalar = opFunc(cur_max_scalar, bounds[1], mod);
  24189                     }
  24190                 }
  24191             },
  24192             .non_integral => {},
  24193         }
  24194 
  24195         const cur = cur_minmax orelse {
  24196             cur_minmax = operand;
  24197             cur_minmax_src = operand_src;
  24198             continue;
  24199         };
  24200 
  24201         const simd_op = try sema.checkSimdBinOp(block, src, cur, operand, cur_minmax_src, operand_src);
  24202         const cur_val = try sema.resolveLazyValue(simd_op.lhs_val.?); // cur_minmax is comptime-known
  24203         const operand_val = try sema.resolveLazyValue(simd_op.rhs_val.?); // we checked the operand was resolvable above
  24204 
  24205         const vec_len = simd_op.len orelse {
  24206             const result_val = opFunc(cur_val, operand_val, mod);
  24207             cur_minmax = Air.internedToRef(result_val.toIntern());
  24208             continue;
  24209         };
  24210         const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  24211         for (elems, 0..) |*elem, i| {
  24212             const lhs_elem_val = try cur_val.elemValue(mod, i);
  24213             const rhs_elem_val = try operand_val.elemValue(mod, i);
  24214             const uncoerced_elem = opFunc(lhs_elem_val, rhs_elem_val, mod);
  24215             elem.* = (try mod.getCoerced(uncoerced_elem, simd_op.scalar_ty)).toIntern();
  24216         }
  24217         cur_minmax = Air.internedToRef((try mod.intern(.{ .aggregate = .{
  24218             .ty = simd_op.result_ty.toIntern(),
  24219             .storage = .{ .elems = elems },
  24220         } })));
  24221     }
  24222 
  24223     const opt_runtime_idx = runtime_known.findFirstSet();
  24224 
  24225     if (cur_minmax) |ct_minmax_ref| refine: {
  24226         // Refine the comptime-known result type based on the bounds. This isn't strictly necessary
  24227         // in the runtime case, since we'll refine the type again later, but keeping things as small
  24228         // as possible will allow us to emit more optimal AIR (if all the runtime operands have
  24229         // smaller types than the non-refined comptime type).
  24230 
  24231         const val = (try sema.resolveValue(ct_minmax_ref)).?;
  24232         const orig_ty = sema.typeOf(ct_minmax_ref);
  24233 
  24234         if (opt_runtime_idx == null and orig_ty.scalarType(mod).eql(Type.comptime_int, mod)) {
  24235             // If all arguments were `comptime_int`, and there are no runtime args, we'll preserve that type
  24236             break :refine;
  24237         }
  24238 
  24239         // We can't refine float types
  24240         if (orig_ty.scalarType(mod).isAnyFloat()) break :refine;
  24241 
  24242         assert(bounds_status == .defined); // there was a non-comptime-int integral comptime-known arg
  24243 
  24244         const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar);
  24245         const refined_ty = if (orig_ty.isVector(mod)) try mod.vectorType(.{
  24246             .len = orig_ty.vectorLen(mod),
  24247             .child = refined_scalar_ty.toIntern(),
  24248         }) else refined_scalar_ty;
  24249 
  24250         // Apply the refined type to the current value
  24251         if (std.debug.runtime_safety) {
  24252             assert(try sema.intFitsInType(val, refined_ty, null));
  24253         }
  24254         cur_minmax = try sema.coerceInMemory(val, refined_ty);
  24255     }
  24256 
  24257     const runtime_idx = opt_runtime_idx orelse return cur_minmax.?;
  24258     const runtime_src = operand_srcs[runtime_idx];
  24259     try sema.requireRuntimeBlock(block, src, runtime_src);
  24260 
  24261     // Now, iterate over runtime operands, emitting a min/max instruction for each. We'll refine the
  24262     // type again at the end, based on the comptime-known bound.
  24263 
  24264     // If the comptime-known part is undef we can avoid emitting actual instructions later
  24265     const known_undef = if (cur_minmax) |operand| blk: {
  24266         const val = (try sema.resolveValue(operand)).?;
  24267         break :blk val.isUndef(mod);
  24268     } else false;
  24269 
  24270     if (cur_minmax == null) {
  24271         // No comptime operands - use the first operand as the starting value
  24272         assert(bounds_status == .unknown);
  24273         assert(runtime_idx == 0);
  24274         cur_minmax = operands[0];
  24275         cur_minmax_src = runtime_src;
  24276         runtime_known.unset(0); // don't look at this operand in the loop below
  24277         const scalar_ty = sema.typeOf(cur_minmax.?).scalarType(mod);
  24278         if (scalar_ty.isInt(mod)) {
  24279             cur_min_scalar = try scalar_ty.minInt(mod, scalar_ty);
  24280             cur_max_scalar = try scalar_ty.maxInt(mod, scalar_ty);
  24281             bounds_status = .defined;
  24282         } else {
  24283             bounds_status = .non_integral;
  24284         }
  24285     }
  24286 
  24287     var it = runtime_known.iterator(.{});
  24288     while (it.next()) |idx| {
  24289         const lhs = cur_minmax.?;
  24290         const lhs_src = cur_minmax_src;
  24291         const rhs = operands[idx];
  24292         const rhs_src = operand_srcs[idx];
  24293         const simd_op = try sema.checkSimdBinOp(block, src, lhs, rhs, lhs_src, rhs_src);
  24294         if (known_undef) {
  24295             cur_minmax = try mod.undefRef(simd_op.result_ty);
  24296         } else {
  24297             cur_minmax = try block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs);
  24298         }
  24299         // Compute the bounds of this type
  24300         switch (bounds_status) {
  24301             .unknown, .defined => refine_bounds: {
  24302                 const scalar_ty = sema.typeOf(rhs).scalarType(mod);
  24303                 if (scalar_ty.isAnyFloat()) {
  24304                     bounds_status = .non_integral;
  24305                     break :refine_bounds;
  24306                 }
  24307                 const scalar_min = try scalar_ty.minInt(mod, scalar_ty);
  24308                 const scalar_max = try scalar_ty.maxInt(mod, scalar_ty);
  24309                 if (bounds_status == .unknown) {
  24310                     cur_min_scalar = scalar_min;
  24311                     cur_max_scalar = scalar_max;
  24312                     bounds_status = .defined;
  24313                 } else {
  24314                     cur_min_scalar = opFunc(cur_min_scalar, scalar_min, mod);
  24315                     cur_max_scalar = opFunc(cur_max_scalar, scalar_max, mod);
  24316                 }
  24317             },
  24318             .non_integral => {},
  24319         }
  24320     }
  24321 
  24322     // Finally, refine the type based on the known bounds.
  24323     const unrefined_ty = sema.typeOf(cur_minmax.?);
  24324     if (unrefined_ty.scalarType(mod).isAnyFloat()) {
  24325         // We can't refine floats, so we're done.
  24326         return cur_minmax.?;
  24327     }
  24328     assert(bounds_status == .defined); // there were integral runtime operands
  24329     const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar);
  24330     const refined_ty = if (unrefined_ty.isVector(mod)) try mod.vectorType(.{
  24331         .len = unrefined_ty.vectorLen(mod),
  24332         .child = refined_scalar_ty.toIntern(),
  24333     }) else refined_scalar_ty;
  24334 
  24335     if (!refined_ty.eql(unrefined_ty, mod)) {
  24336         // We've reduced the type - cast the result down
  24337         return block.addTyOp(.intcast, refined_ty, cur_minmax.?);
  24338     }
  24339 
  24340     return cur_minmax.?;
  24341 }
  24342 
  24343 fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref {
  24344     const mod = sema.mod;
  24345     const ptr_ty = sema.typeOf(ptr);
  24346     const info = ptr_ty.ptrInfo(mod);
  24347     if (info.flags.size == .One) {
  24348         // Already an array pointer.
  24349         return ptr;
  24350     }
  24351     const new_ty = try sema.ptrType(.{
  24352         .child = (try mod.arrayType(.{
  24353             .len = len,
  24354             .sentinel = info.sentinel,
  24355             .child = info.child,
  24356         })).toIntern(),
  24357         .flags = .{
  24358             .alignment = info.flags.alignment,
  24359             .is_const = info.flags.is_const,
  24360             .is_volatile = info.flags.is_volatile,
  24361             .is_allowzero = info.flags.is_allowzero,
  24362             .address_space = info.flags.address_space,
  24363         },
  24364     });
  24365     const non_slice_ptr = if (info.flags.size == .Slice)
  24366         try block.addTyOp(.slice_ptr, ptr_ty.slicePtrFieldType(mod), ptr)
  24367     else
  24368         ptr;
  24369     return block.addBitCast(new_ty, non_slice_ptr);
  24370 }
  24371 
  24372 fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  24373     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24374     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  24375     const src = inst_data.src();
  24376     const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  24377     const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  24378     const dest_ptr = try sema.resolveInst(extra.lhs);
  24379     const src_ptr = try sema.resolveInst(extra.rhs);
  24380     const dest_ty = sema.typeOf(dest_ptr);
  24381     const src_ty = sema.typeOf(src_ptr);
  24382     const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr);
  24383     const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr);
  24384     const target = sema.mod.getTarget();
  24385     const mod = sema.mod;
  24386 
  24387     if (dest_ty.isConstPtr(mod)) {
  24388         return sema.fail(block, dest_src, "cannot memcpy to constant pointer", .{});
  24389     }
  24390 
  24391     if (dest_len == .none and src_len == .none) {
  24392         const msg = msg: {
  24393             const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{});
  24394             errdefer msg.destroy(sema.gpa);
  24395             try sema.errNote(block, dest_src, msg, "destination type '{}' provides no length", .{
  24396                 dest_ty.fmt(sema.mod),
  24397             });
  24398             try sema.errNote(block, src_src, msg, "source type '{}' provides no length", .{
  24399                 src_ty.fmt(sema.mod),
  24400             });
  24401             break :msg msg;
  24402         };
  24403         return sema.failWithOwnedErrorMsg(block, msg);
  24404     }
  24405 
  24406     var len_val: ?Value = null;
  24407 
  24408     if (dest_len != .none and src_len != .none) check: {
  24409         // If we can check at compile-time, no need for runtime safety.
  24410         if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
  24411             len_val = dest_len_val;
  24412             if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  24413                 if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) {
  24414                     const msg = msg: {
  24415                         const msg = try sema.errMsg(block, src, "non-matching @memcpy lengths", .{});
  24416                         errdefer msg.destroy(sema.gpa);
  24417                         try sema.errNote(block, dest_src, msg, "length {} here", .{
  24418                             dest_len_val.fmtValue(Type.usize, sema.mod),
  24419                         });
  24420                         try sema.errNote(block, src_src, msg, "length {} here", .{
  24421                             src_len_val.fmtValue(Type.usize, sema.mod),
  24422                         });
  24423                         break :msg msg;
  24424                     };
  24425                     return sema.failWithOwnedErrorMsg(block, msg);
  24426                 }
  24427                 break :check;
  24428             }
  24429         } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  24430             len_val = src_len_val;
  24431         }
  24432 
  24433         if (block.wantSafety()) {
  24434             const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);
  24435             try sema.addSafetyCheck(block, src, ok, .memcpy_len_mismatch);
  24436         }
  24437     } else if (dest_len != .none) {
  24438         if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
  24439             len_val = dest_len_val;
  24440         }
  24441     } else if (src_len != .none) {
  24442         if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  24443             len_val = src_len_val;
  24444         }
  24445     }
  24446 
  24447     const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: {
  24448         if (!dest_ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src;
  24449         if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |_| {
  24450             const len_u64 = (try len_val.?.getUnsignedIntAdvanced(mod, sema)).?;
  24451             const len = try sema.usizeCast(block, dest_src, len_u64);
  24452             for (0..len) |i| {
  24453                 const elem_index = try mod.intRef(Type.usize, i);
  24454                 const dest_elem_ptr = try sema.elemPtrOneLayerOnly(
  24455                     block,
  24456                     src,
  24457                     dest_ptr,
  24458                     elem_index,
  24459                     src,
  24460                     true, // init
  24461                     false, // oob_safety
  24462                 );
  24463                 const src_elem_ptr = try sema.elemPtrOneLayerOnly(
  24464                     block,
  24465                     src,
  24466                     src_ptr,
  24467                     elem_index,
  24468                     src,
  24469                     false, // init
  24470                     false, // oob_safety
  24471                 );
  24472                 const uncoerced_elem = try sema.analyzeLoad(block, src, src_elem_ptr, src_src);
  24473                 try sema.storePtr2(
  24474                     block,
  24475                     src,
  24476                     dest_elem_ptr,
  24477                     dest_src,
  24478                     uncoerced_elem,
  24479                     src_src,
  24480                     .store,
  24481                 );
  24482             }
  24483             return;
  24484         } else break :rs src_src;
  24485     } else dest_src;
  24486 
  24487     // If in-memory coercion is not allowed, explode this memcpy call into a
  24488     // for loop that copies element-wise.
  24489     // Likewise if this is an iterable rather than a pointer, do the same
  24490     // lowering. The AIR instruction requires pointers with element types of
  24491     // equal ABI size.
  24492 
  24493     if (dest_ty.zigTypeTag(mod) != .Pointer or src_ty.zigTypeTag(mod) != .Pointer) {
  24494         return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{});
  24495     }
  24496 
  24497     const dest_elem_ty = dest_ty.elemType2(mod);
  24498     const src_elem_ty = src_ty.elemType2(mod);
  24499     if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) {
  24500         return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{});
  24501     }
  24502 
  24503     // If the length is comptime-known, then upgrade src and destination types
  24504     // into pointer-to-array. At this point we know they are both pointers
  24505     // already.
  24506     var new_dest_ptr = dest_ptr;
  24507     var new_src_ptr = src_ptr;
  24508     if (len_val) |val| {
  24509         const len = val.toUnsignedInt(mod);
  24510         if (len == 0) {
  24511             // This AIR instruction guarantees length > 0 if it is comptime-known.
  24512             return;
  24513         }
  24514         new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len);
  24515         new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);
  24516     }
  24517 
  24518     if (dest_len != .none) {
  24519         // Change the src from slice to a many pointer, to avoid multiple ptr
  24520         // slice extractions in AIR instructions.
  24521         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  24522         if (new_src_ptr_ty.isSlice(mod)) {
  24523             new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
  24524         }
  24525     } else if (dest_len == .none and len_val == null) {
  24526         // Change the dest to a slice, since its type must have the length.
  24527         const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr);
  24528         new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, .unneeded, dest_src, dest_src, dest_src, false);
  24529         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  24530         if (new_src_ptr_ty.isSlice(mod)) {
  24531             new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
  24532         }
  24533     }
  24534 
  24535     try sema.requireRuntimeBlock(block, src, runtime_src);
  24536 
  24537     // Aliasing safety check.
  24538     if (block.wantSafety()) {
  24539         const len = if (len_val) |v|
  24540             Air.internedToRef(v.toIntern())
  24541         else if (dest_len != .none)
  24542             dest_len
  24543         else
  24544             src_len;
  24545 
  24546         // Extract raw pointer from dest slice. The AIR instructions could support them, but
  24547         // it would cause redundant machine code instructions.
  24548         const new_dest_ptr_ty = sema.typeOf(new_dest_ptr);
  24549         const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(mod))
  24550             try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty)
  24551         else if (new_dest_ptr_ty.ptrSize(mod) == .One) ptr: {
  24552             var dest_manyptr_ty_key = mod.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type;
  24553             assert(dest_manyptr_ty_key.flags.size == .One);
  24554             dest_manyptr_ty_key.child = dest_elem_ty.toIntern();
  24555             dest_manyptr_ty_key.flags.size = .Many;
  24556             break :ptr try sema.coerceCompatiblePtrs(block, try sema.ptrType(dest_manyptr_ty_key), new_dest_ptr, dest_src);
  24557         } else new_dest_ptr;
  24558 
  24559         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  24560         const raw_src_ptr = if (new_src_ptr_ty.isSlice(mod))
  24561             try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty)
  24562         else if (new_src_ptr_ty.ptrSize(mod) == .One) ptr: {
  24563             var src_manyptr_ty_key = mod.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type;
  24564             assert(src_manyptr_ty_key.flags.size == .One);
  24565             src_manyptr_ty_key.child = src_elem_ty.toIntern();
  24566             src_manyptr_ty_key.flags.size = .Many;
  24567             break :ptr try sema.coerceCompatiblePtrs(block, try sema.ptrType(src_manyptr_ty_key), new_src_ptr, src_src);
  24568         } else new_src_ptr;
  24569 
  24570         // ok1: dest >= src + len
  24571         // ok2: src >= dest + len
  24572         const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src);
  24573         const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src);
  24574         const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
  24575         const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
  24576         const ok = try block.addBinOp(.bool_or, ok1, ok2);
  24577         try sema.addSafetyCheck(block, src, ok, .memcpy_alias);
  24578     }
  24579 
  24580     _ = try block.addInst(.{
  24581         .tag = .memcpy,
  24582         .data = .{ .bin_op = .{
  24583             .lhs = new_dest_ptr,
  24584             .rhs = new_src_ptr,
  24585         } },
  24586     });
  24587 }
  24588 
  24589 fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  24590     const mod = sema.mod;
  24591     const gpa = sema.gpa;
  24592     const ip = &mod.intern_pool;
  24593     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24594     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  24595     const src = inst_data.src();
  24596     const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  24597     const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  24598     const dest_ptr = try sema.resolveInst(extra.lhs);
  24599     const uncoerced_elem = try sema.resolveInst(extra.rhs);
  24600     const dest_ptr_ty = sema.typeOf(dest_ptr);
  24601     try checkMemOperand(sema, block, dest_src, dest_ptr_ty);
  24602 
  24603     if (dest_ptr_ty.isConstPtr(mod)) {
  24604         return sema.fail(block, dest_src, "cannot memset constant pointer", .{});
  24605     }
  24606 
  24607     const dest_elem_ty: Type = dest_elem_ty: {
  24608         const ptr_info = dest_ptr_ty.ptrInfo(mod);
  24609         switch (ptr_info.flags.size) {
  24610             .Slice => break :dest_elem_ty Type.fromInterned(ptr_info.child),
  24611             .One => {
  24612                 if (Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Array) {
  24613                     break :dest_elem_ty Type.fromInterned(ptr_info.child).childType(mod);
  24614                 }
  24615             },
  24616             .Many, .C => {},
  24617         }
  24618         return sema.failWithOwnedErrorMsg(block, msg: {
  24619             const msg = try sema.errMsg(block, src, "unknown @memset length", .{});
  24620             errdefer msg.destroy(sema.gpa);
  24621             try sema.errNote(block, dest_src, msg, "destination type '{}' provides no length", .{
  24622                 dest_ptr_ty.fmt(mod),
  24623             });
  24624             break :msg msg;
  24625         });
  24626     };
  24627 
  24628     const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src);
  24629 
  24630     const runtime_src = rs: {
  24631         const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src;
  24632         const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, "len"), dest_src);
  24633         const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src;
  24634         const len_u64 = (try len_val.getUnsignedIntAdvanced(mod, sema)).?;
  24635         const len = try sema.usizeCast(block, dest_src, len_u64);
  24636         if (len == 0) {
  24637             // This AIR instruction guarantees length > 0 if it is comptime-known.
  24638             return;
  24639         }
  24640 
  24641         if (!ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src;
  24642         const elem_val = try sema.resolveValue(elem) orelse break :rs value_src;
  24643         const array_ty = try mod.arrayType(.{
  24644             .child = dest_elem_ty.toIntern(),
  24645             .len = len_u64,
  24646         });
  24647         const array_val = Value.fromInterned((try mod.intern(.{ .aggregate = .{
  24648             .ty = array_ty.toIntern(),
  24649             .storage = .{ .repeated_elem = elem_val.toIntern() },
  24650         } })));
  24651         const array_ptr_ty = ty: {
  24652             var info = dest_ptr_ty.ptrInfo(mod);
  24653             info.flags.size = .One;
  24654             info.child = array_ty.toIntern();
  24655             break :ty try mod.ptrType(info);
  24656         };
  24657         const raw_ptr_val = if (dest_ptr_ty.isSlice(mod)) ptr_val.slicePtr(mod) else ptr_val;
  24658         const array_ptr_val = try mod.getCoerced(raw_ptr_val, array_ptr_ty);
  24659         return sema.storePtrVal(block, src, array_ptr_val, array_val, array_ty);
  24660     };
  24661 
  24662     try sema.requireRuntimeBlock(block, src, runtime_src);
  24663     _ = try block.addInst(.{
  24664         .tag = if (block.wantSafety()) .memset_safe else .memset,
  24665         .data = .{ .bin_op = .{
  24666             .lhs = dest_ptr,
  24667             .rhs = elem,
  24668         } },
  24669     });
  24670 }
  24671 
  24672 fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  24673     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  24674     const src = LazySrcLoc.nodeOffset(extra.node);
  24675     return sema.failWithUseOfAsync(block, src);
  24676 }
  24677 
  24678 fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  24679     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  24680     const src = inst_data.src();
  24681     return sema.failWithUseOfAsync(block, src);
  24682 }
  24683 
  24684 fn zirAwait(
  24685     sema: *Sema,
  24686     block: *Block,
  24687     inst: Zir.Inst.Index,
  24688 ) CompileError!Air.Inst.Ref {
  24689     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  24690     const src = inst_data.src();
  24691 
  24692     return sema.failWithUseOfAsync(block, src);
  24693 }
  24694 
  24695 fn zirAwaitNosuspend(
  24696     sema: *Sema,
  24697     block: *Block,
  24698     extended: Zir.Inst.Extended.InstData,
  24699 ) CompileError!Air.Inst.Ref {
  24700     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  24701     const src = LazySrcLoc.nodeOffset(extra.node);
  24702 
  24703     return sema.failWithUseOfAsync(block, src);
  24704 }
  24705 
  24706 fn zirVarExtended(
  24707     sema: *Sema,
  24708     block: *Block,
  24709     extended: Zir.Inst.Extended.InstData,
  24710 ) CompileError!Air.Inst.Ref {
  24711     const mod = sema.mod;
  24712     const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
  24713     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 };
  24714     const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 };
  24715     const small: Zir.Inst.ExtendedVar.Small = @bitCast(extended.small);
  24716 
  24717     var extra_index: usize = extra.end;
  24718 
  24719     const lib_name = if (small.has_lib_name) lib_name: {
  24720         const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
  24721         extra_index += 1;
  24722         try sema.handleExternLibName(block, ty_src, lib_name);
  24723         break :lib_name lib_name;
  24724     } else null;
  24725 
  24726     // ZIR supports encoding this information but it is not used; the information
  24727     // is encoded via the Decl entry.
  24728     assert(!small.has_align);
  24729 
  24730     const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: {
  24731         const init_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  24732         extra_index += 1;
  24733         break :blk try sema.resolveInst(init_ref);
  24734     } else .none;
  24735 
  24736     const have_ty = extra.data.var_type != .none;
  24737     const var_ty = if (have_ty)
  24738         try sema.resolveType(block, ty_src, extra.data.var_type)
  24739     else
  24740         sema.typeOf(uncasted_init);
  24741 
  24742     const init_val = if (uncasted_init != .none) blk: {
  24743         const init = if (have_ty)
  24744             try sema.coerce(block, var_ty, uncasted_init, init_src)
  24745         else
  24746             uncasted_init;
  24747 
  24748         break :blk ((try sema.resolveValue(init)) orelse {
  24749             return sema.failWithNeededComptime(block, init_src, .{
  24750                 .needed_comptime_reason = "container level variable initializers must be comptime-known",
  24751             });
  24752         }).toIntern();
  24753     } else .none;
  24754 
  24755     try sema.validateVarType(block, ty_src, var_ty, small.is_extern);
  24756 
  24757     return Air.internedToRef((try mod.intern(.{ .variable = .{
  24758         .ty = var_ty.toIntern(),
  24759         .init = init_val,
  24760         .decl = sema.owner_decl_index,
  24761         .lib_name = try mod.intern_pool.getOrPutStringOpt(sema.gpa, lib_name),
  24762         .is_extern = small.is_extern,
  24763         .is_const = small.is_const,
  24764         .is_threadlocal = small.is_threadlocal,
  24765         .is_weak_linkage = false,
  24766     } })));
  24767 }
  24768 
  24769 fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  24770     const tracy = trace(@src());
  24771     defer tracy.end();
  24772 
  24773     const mod = sema.mod;
  24774     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24775     const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
  24776     const target = mod.getTarget();
  24777 
  24778     const align_src: LazySrcLoc = .{ .node_offset_fn_type_align = inst_data.src_node };
  24779     const addrspace_src: LazySrcLoc = .{ .node_offset_fn_type_addrspace = inst_data.src_node };
  24780     const section_src: LazySrcLoc = .{ .node_offset_fn_type_section = inst_data.src_node };
  24781     const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node };
  24782     const ret_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node };
  24783     const has_body = extra.data.body_len != 0;
  24784 
  24785     var extra_index: usize = extra.end;
  24786 
  24787     const lib_name: ?[]const u8 = if (extra.data.bits.has_lib_name) blk: {
  24788         const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
  24789         extra_index += 1;
  24790         break :blk lib_name;
  24791     } else null;
  24792 
  24793     if (has_body and
  24794         (extra.data.bits.has_align_body or extra.data.bits.has_align_ref) and
  24795         !target_util.supportsFunctionAlignment(target))
  24796     {
  24797         return sema.fail(block, align_src, "target does not support function alignment", .{});
  24798     }
  24799 
  24800     const @"align": ?Alignment = if (extra.data.bits.has_align_body) blk: {
  24801         const body_len = sema.code.extra[extra_index];
  24802         extra_index += 1;
  24803         const body = sema.code.bodySlice(extra_index, body_len);
  24804         extra_index += body.len;
  24805 
  24806         const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29, .{
  24807             .needed_comptime_reason = "alignment must be comptime-known",
  24808         });
  24809         if (val.isGenericPoison()) {
  24810             break :blk null;
  24811         }
  24812         const alignment = try sema.validateAlignAllowZero(block, align_src, val.toUnsignedInt(mod));
  24813         const default = target_util.defaultFunctionAlignment(target);
  24814         break :blk if (alignment == default) .none else alignment;
  24815     } else if (extra.data.bits.has_align_ref) blk: {
  24816         const align_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  24817         extra_index += 1;
  24818         const align_tv = sema.resolveInstConst(block, align_src, align_ref, .{
  24819             .needed_comptime_reason = "alignment must be comptime-known",
  24820         }) catch |err| switch (err) {
  24821             error.GenericPoison => {
  24822                 break :blk null;
  24823             },
  24824             else => |e| return e,
  24825         };
  24826         const alignment = try sema.validateAlignAllowZero(block, align_src, align_tv.val.toUnsignedInt(mod));
  24827         const default = target_util.defaultFunctionAlignment(target);
  24828         break :blk if (alignment == default) .none else alignment;
  24829     } else .none;
  24830 
  24831     const @"addrspace": ?std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: {
  24832         const body_len = sema.code.extra[extra_index];
  24833         extra_index += 1;
  24834         const body = sema.code.bodySlice(extra_index, body_len);
  24835         extra_index += body.len;
  24836 
  24837         const addrspace_ty = try sema.getBuiltinType("AddressSpace");
  24838         const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty, .{
  24839             .needed_comptime_reason = "addrspace must be comptime-known",
  24840         });
  24841         if (val.isGenericPoison()) {
  24842             break :blk null;
  24843         }
  24844         break :blk mod.toEnum(std.builtin.AddressSpace, val);
  24845     } else if (extra.data.bits.has_addrspace_ref) blk: {
  24846         const addrspace_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  24847         extra_index += 1;
  24848         const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref, .{
  24849             .needed_comptime_reason = "addrspace must be comptime-known",
  24850         }) catch |err| switch (err) {
  24851             error.GenericPoison => {
  24852                 break :blk null;
  24853             },
  24854             else => |e| return e,
  24855         };
  24856         break :blk mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val);
  24857     } else target_util.defaultAddressSpace(target, .function);
  24858 
  24859     const section: Section = if (extra.data.bits.has_section_body) blk: {
  24860         const body_len = sema.code.extra[extra_index];
  24861         extra_index += 1;
  24862         const body = sema.code.bodySlice(extra_index, body_len);
  24863         extra_index += body.len;
  24864 
  24865         const ty = Type.slice_const_u8;
  24866         const val = try sema.resolveGenericBody(block, section_src, body, inst, ty, .{
  24867             .needed_comptime_reason = "linksection must be comptime-known",
  24868         });
  24869         if (val.isGenericPoison()) {
  24870             break :blk .generic;
  24871         }
  24872         break :blk .{ .explicit = try val.toIpString(ty, mod) };
  24873     } else if (extra.data.bits.has_section_ref) blk: {
  24874         const section_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  24875         extra_index += 1;
  24876         const section_name = sema.resolveConstStringIntern(block, section_src, section_ref, .{
  24877             .needed_comptime_reason = "linksection must be comptime-known",
  24878         }) catch |err| switch (err) {
  24879             error.GenericPoison => {
  24880                 break :blk .generic;
  24881             },
  24882             else => |e| return e,
  24883         };
  24884         break :blk .{ .explicit = section_name };
  24885     } else .default;
  24886 
  24887     const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
  24888         const body_len = sema.code.extra[extra_index];
  24889         extra_index += 1;
  24890         const body = sema.code.bodySlice(extra_index, body_len);
  24891         extra_index += body.len;
  24892 
  24893         const cc_ty = try sema.getBuiltinType("CallingConvention");
  24894         const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, .{
  24895             .needed_comptime_reason = "calling convention must be comptime-known",
  24896         });
  24897         if (val.isGenericPoison()) {
  24898             break :blk null;
  24899         }
  24900         break :blk mod.toEnum(std.builtin.CallingConvention, val);
  24901     } else if (extra.data.bits.has_cc_ref) blk: {
  24902         const cc_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  24903         extra_index += 1;
  24904         const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref, .{
  24905             .needed_comptime_reason = "calling convention must be comptime-known",
  24906         }) catch |err| switch (err) {
  24907             error.GenericPoison => {
  24908                 break :blk null;
  24909             },
  24910             else => |e| return e,
  24911         };
  24912         break :blk mod.toEnum(std.builtin.CallingConvention, cc_tv.val);
  24913     } else if (sema.owner_decl.is_exported and has_body)
  24914         .C
  24915     else
  24916         .Unspecified;
  24917 
  24918     const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
  24919         const body_len = sema.code.extra[extra_index];
  24920         extra_index += 1;
  24921         const body = sema.code.bodySlice(extra_index, body_len);
  24922         extra_index += body.len;
  24923 
  24924         const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type, .{
  24925             .needed_comptime_reason = "return type must be comptime-known",
  24926         });
  24927         const ty = val.toType();
  24928         break :blk ty;
  24929     } else if (extra.data.bits.has_ret_ty_ref) blk: {
  24930         const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  24931         extra_index += 1;
  24932         const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref, .{
  24933             .needed_comptime_reason = "return type must be comptime-known",
  24934         }) catch |err| switch (err) {
  24935             error.GenericPoison => {
  24936                 break :blk Type.generic_poison;
  24937             },
  24938             else => |e| return e,
  24939         };
  24940         const ty = ret_ty_tv.val.toType();
  24941         break :blk ty;
  24942     } else Type.void;
  24943 
  24944     const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: {
  24945         const x = sema.code.extra[extra_index];
  24946         extra_index += 1;
  24947         break :blk x;
  24948     } else 0;
  24949 
  24950     var src_locs: Zir.Inst.Func.SrcLocs = undefined;
  24951     if (has_body) {
  24952         extra_index += extra.data.body_len;
  24953         src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
  24954     }
  24955 
  24956     const is_var_args = extra.data.bits.is_var_args;
  24957     const is_inferred_error = extra.data.bits.is_inferred_error;
  24958     const is_extern = extra.data.bits.is_extern;
  24959     const is_noinline = extra.data.bits.is_noinline;
  24960 
  24961     return sema.funcCommon(
  24962         block,
  24963         inst_data.src_node,
  24964         inst,
  24965         @"align",
  24966         @"addrspace",
  24967         section,
  24968         cc,
  24969         ret_ty,
  24970         is_var_args,
  24971         is_inferred_error,
  24972         is_extern,
  24973         has_body,
  24974         src_locs,
  24975         lib_name,
  24976         noalias_bits,
  24977         is_noinline,
  24978     );
  24979 }
  24980 
  24981 fn zirCUndef(
  24982     sema: *Sema,
  24983     block: *Block,
  24984     extended: Zir.Inst.Extended.InstData,
  24985 ) CompileError!Air.Inst.Ref {
  24986     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  24987     const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24988 
  24989     const name = try sema.resolveConstString(block, src, extra.operand, .{
  24990         .needed_comptime_reason = "name of macro being undefined must be comptime-known",
  24991     });
  24992     try block.c_import_buf.?.writer().print("#undef {s}\n", .{name});
  24993     return .void_value;
  24994 }
  24995 
  24996 fn zirCInclude(
  24997     sema: *Sema,
  24998     block: *Block,
  24999     extended: Zir.Inst.Extended.InstData,
  25000 ) CompileError!Air.Inst.Ref {
  25001     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25002     const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  25003 
  25004     const name = try sema.resolveConstString(block, src, extra.operand, .{
  25005         .needed_comptime_reason = "path being included must be comptime-known",
  25006     });
  25007     try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name});
  25008     return .void_value;
  25009 }
  25010 
  25011 fn zirCDefine(
  25012     sema: *Sema,
  25013     block: *Block,
  25014     extended: Zir.Inst.Extended.InstData,
  25015 ) CompileError!Air.Inst.Ref {
  25016     const mod = sema.mod;
  25017     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25018     const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  25019     const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  25020 
  25021     const name = try sema.resolveConstString(block, name_src, extra.lhs, .{
  25022         .needed_comptime_reason = "name of macro being undefined must be comptime-known",
  25023     });
  25024     const rhs = try sema.resolveInst(extra.rhs);
  25025     if (sema.typeOf(rhs).zigTypeTag(mod) != .Void) {
  25026         const value = try sema.resolveConstString(block, val_src, extra.rhs, .{
  25027             .needed_comptime_reason = "value of macro being undefined must be comptime-known",
  25028         });
  25029         try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value });
  25030     } else {
  25031         try block.c_import_buf.?.writer().print("#define {s}\n", .{name});
  25032     }
  25033     return .void_value;
  25034 }
  25035 
  25036 fn zirWasmMemorySize(
  25037     sema: *Sema,
  25038     block: *Block,
  25039     extended: Zir.Inst.Extended.InstData,
  25040 ) CompileError!Air.Inst.Ref {
  25041     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25042     const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  25043     const builtin_src = LazySrcLoc.nodeOffset(extra.node);
  25044     const target = sema.mod.getTarget();
  25045     if (!target.isWasm()) {
  25046         return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
  25047     }
  25048 
  25049     const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.operand, Type.u32, .{
  25050         .needed_comptime_reason = "wasm memory size index must be comptime-known",
  25051     }));
  25052     try sema.requireRuntimeBlock(block, builtin_src, null);
  25053     return block.addInst(.{
  25054         .tag = .wasm_memory_size,
  25055         .data = .{ .pl_op = .{
  25056             .operand = .none,
  25057             .payload = index,
  25058         } },
  25059     });
  25060 }
  25061 
  25062 fn zirWasmMemoryGrow(
  25063     sema: *Sema,
  25064     block: *Block,
  25065     extended: Zir.Inst.Extended.InstData,
  25066 ) CompileError!Air.Inst.Ref {
  25067     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25068     const builtin_src = LazySrcLoc.nodeOffset(extra.node);
  25069     const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  25070     const delta_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  25071     const target = sema.mod.getTarget();
  25072     if (!target.isWasm()) {
  25073         return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
  25074     }
  25075 
  25076     const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.lhs, Type.u32, .{
  25077         .needed_comptime_reason = "wasm memory size index must be comptime-known",
  25078     }));
  25079     const delta = try sema.coerce(block, Type.u32, try sema.resolveInst(extra.rhs), delta_src);
  25080 
  25081     try sema.requireRuntimeBlock(block, builtin_src, null);
  25082     return block.addInst(.{
  25083         .tag = .wasm_memory_grow,
  25084         .data = .{ .pl_op = .{
  25085             .operand = delta,
  25086             .payload = index,
  25087         } },
  25088     });
  25089 }
  25090 
  25091 fn resolvePrefetchOptions(
  25092     sema: *Sema,
  25093     block: *Block,
  25094     src: LazySrcLoc,
  25095     zir_ref: Zir.Inst.Ref,
  25096 ) CompileError!std.builtin.PrefetchOptions {
  25097     const mod = sema.mod;
  25098     const gpa = sema.gpa;
  25099     const ip = &mod.intern_pool;
  25100     const options_ty = try sema.getBuiltinType("PrefetchOptions");
  25101     const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src);
  25102 
  25103     const rw_src = sema.maybeOptionsSrc(block, src, "rw");
  25104     const locality_src = sema.maybeOptionsSrc(block, src, "locality");
  25105     const cache_src = sema.maybeOptionsSrc(block, src, "cache");
  25106 
  25107     const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "rw"), rw_src);
  25108     const rw_val = try sema.resolveConstDefinedValue(block, rw_src, rw, .{
  25109         .needed_comptime_reason = "prefetch read/write must be comptime-known",
  25110     });
  25111 
  25112     const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "locality"), locality_src);
  25113     const locality_val = try sema.resolveConstDefinedValue(block, locality_src, locality, .{
  25114         .needed_comptime_reason = "prefetch locality must be comptime-known",
  25115     });
  25116 
  25117     const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "cache"), cache_src);
  25118     const cache_val = try sema.resolveConstDefinedValue(block, cache_src, cache, .{
  25119         .needed_comptime_reason = "prefetch cache must be comptime-known",
  25120     });
  25121 
  25122     return std.builtin.PrefetchOptions{
  25123         .rw = mod.toEnum(std.builtin.PrefetchOptions.Rw, rw_val),
  25124         .locality = @intCast(locality_val.toUnsignedInt(mod)),
  25125         .cache = mod.toEnum(std.builtin.PrefetchOptions.Cache, cache_val),
  25126     };
  25127 }
  25128 
  25129 fn zirPrefetch(
  25130     sema: *Sema,
  25131     block: *Block,
  25132     extended: Zir.Inst.Extended.InstData,
  25133 ) CompileError!Air.Inst.Ref {
  25134     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25135     const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  25136     const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  25137     const ptr = try sema.resolveInst(extra.lhs);
  25138     try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr));
  25139 
  25140     const options = sema.resolvePrefetchOptions(block, .unneeded, extra.rhs) catch |err| switch (err) {
  25141         error.NeededSourceLocation => {
  25142             _ = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs);
  25143             unreachable;
  25144         },
  25145         else => |e| return e,
  25146     };
  25147 
  25148     if (!block.is_comptime) {
  25149         _ = try block.addInst(.{
  25150             .tag = .prefetch,
  25151             .data = .{ .prefetch = .{
  25152                 .ptr = ptr,
  25153                 .rw = options.rw,
  25154                 .locality = options.locality,
  25155                 .cache = options.cache,
  25156             } },
  25157         });
  25158     }
  25159 
  25160     return .void_value;
  25161 }
  25162 
  25163 fn resolveExternOptions(
  25164     sema: *Sema,
  25165     block: *Block,
  25166     src: LazySrcLoc,
  25167     zir_ref: Zir.Inst.Ref,
  25168 ) CompileError!struct {
  25169     name: InternPool.NullTerminatedString,
  25170     library_name: InternPool.OptionalNullTerminatedString = .none,
  25171     linkage: std.builtin.GlobalLinkage = .Strong,
  25172     is_thread_local: bool = false,
  25173 } {
  25174     const mod = sema.mod;
  25175     const gpa = sema.gpa;
  25176     const ip = &mod.intern_pool;
  25177     const options_inst = try sema.resolveInst(zir_ref);
  25178     const extern_options_ty = try sema.getBuiltinType("ExternOptions");
  25179     const options = try sema.coerce(block, extern_options_ty, options_inst, src);
  25180 
  25181     const name_src = sema.maybeOptionsSrc(block, src, "name");
  25182     const library_src = sema.maybeOptionsSrc(block, src, "library");
  25183     const linkage_src = sema.maybeOptionsSrc(block, src, "linkage");
  25184     const thread_local_src = sema.maybeOptionsSrc(block, src, "thread_local");
  25185 
  25186     const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src);
  25187     const name_val = try sema.resolveConstDefinedValue(block, name_src, name_ref, .{
  25188         .needed_comptime_reason = "name of the extern symbol must be comptime-known",
  25189     });
  25190     const name = try name_val.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod);
  25191 
  25192     const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "library_name"), library_src);
  25193     const library_name_val = try sema.resolveConstDefinedValue(block, library_src, library_name_inst, .{
  25194         .needed_comptime_reason = "library in which extern symbol is must be comptime-known",
  25195     });
  25196 
  25197     const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src);
  25198     const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{
  25199         .needed_comptime_reason = "linkage of the extern symbol must be comptime-known",
  25200     });
  25201     const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val);
  25202 
  25203     const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "is_thread_local"), thread_local_src);
  25204     const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{
  25205         .needed_comptime_reason = "threadlocality of the extern symbol must be comptime-known",
  25206     });
  25207 
  25208     const library_name = if (library_name_val.optionalValue(mod)) |library_name_payload| library_name: {
  25209         const library_name = try library_name_payload.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod);
  25210         if (library_name.len == 0) {
  25211             return sema.fail(block, library_src, "library name cannot be empty", .{});
  25212         }
  25213         try sema.handleExternLibName(block, library_src, library_name);
  25214         break :library_name library_name;
  25215     } else null;
  25216 
  25217     if (name.len == 0) {
  25218         return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
  25219     }
  25220 
  25221     if (linkage != .Weak and linkage != .Strong) {
  25222         return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{});
  25223     }
  25224 
  25225     return .{
  25226         .name = try ip.getOrPutString(gpa, name),
  25227         .library_name = try ip.getOrPutStringOpt(gpa, library_name),
  25228         .linkage = linkage,
  25229         .is_thread_local = is_thread_local_val.toBool(),
  25230     };
  25231 }
  25232 
  25233 fn zirBuiltinExtern(
  25234     sema: *Sema,
  25235     block: *Block,
  25236     extended: Zir.Inst.Extended.InstData,
  25237 ) CompileError!Air.Inst.Ref {
  25238     const mod = sema.mod;
  25239     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25240     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  25241     const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  25242 
  25243     var ty = try sema.resolveType(block, ty_src, extra.lhs);
  25244     if (!ty.isPtrAtRuntime(mod)) {
  25245         return sema.fail(block, ty_src, "expected (optional) pointer", .{});
  25246     }
  25247     if (!try sema.validateExternType(ty, .other)) {
  25248         const msg = msg: {
  25249             const msg = try sema.errMsg(block, ty_src, "extern symbol cannot have type '{}'", .{ty.fmt(mod)});
  25250             errdefer msg.destroy(sema.gpa);
  25251             const src_decl = sema.mod.declPtr(block.src_decl);
  25252             try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), ty, .other);
  25253             break :msg msg;
  25254         };
  25255         return sema.failWithOwnedErrorMsg(block, msg);
  25256     }
  25257 
  25258     const options = sema.resolveExternOptions(block, .unneeded, extra.rhs) catch |err| switch (err) {
  25259         error.NeededSourceLocation => {
  25260             _ = try sema.resolveExternOptions(block, options_src, extra.rhs);
  25261             unreachable;
  25262         },
  25263         else => |e| return e,
  25264     };
  25265 
  25266     if (options.linkage == .Weak and !ty.ptrAllowsZero(mod)) {
  25267         ty = try mod.optionalType(ty.toIntern());
  25268     }
  25269     const ptr_info = ty.ptrInfo(mod);
  25270 
  25271     // TODO check duplicate extern
  25272 
  25273     const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, .none);
  25274     errdefer mod.destroyDecl(new_decl_index);
  25275     const new_decl = mod.declPtr(new_decl_index);
  25276     new_decl.name = options.name;
  25277 
  25278     {
  25279         const new_var = try mod.intern(.{ .variable = .{
  25280             .ty = ptr_info.child,
  25281             .init = .none,
  25282             .decl = sema.owner_decl_index,
  25283             .lib_name = options.library_name,
  25284             .is_extern = true,
  25285             .is_const = ptr_info.flags.is_const,
  25286             .is_threadlocal = options.is_thread_local,
  25287             .is_weak_linkage = options.linkage == .Weak,
  25288         } });
  25289 
  25290         new_decl.src_line = sema.owner_decl.src_line;
  25291         // We only access this decl through the decl_ref with the correct type created
  25292         // below, so this type doesn't matter
  25293         new_decl.ty = Type.fromInterned(ptr_info.child);
  25294         new_decl.val = Value.fromInterned(new_var);
  25295         new_decl.alignment = .none;
  25296         new_decl.@"linksection" = .none;
  25297         new_decl.has_tv = true;
  25298         new_decl.analysis = .complete;
  25299         new_decl.generation = mod.generation;
  25300     }
  25301 
  25302     try sema.ensureDeclAnalyzed(new_decl_index);
  25303 
  25304     return Air.internedToRef((try mod.getCoerced(Value.fromInterned((try mod.intern(.{ .ptr = .{
  25305         .ty = switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  25306             .ptr_type => ty.toIntern(),
  25307             .opt_type => |child_type| child_type,
  25308             else => unreachable,
  25309         },
  25310         .addr = .{ .decl = new_decl_index },
  25311     } }))), ty)).toIntern());
  25312 }
  25313 
  25314 fn zirWorkItem(
  25315     sema: *Sema,
  25316     block: *Block,
  25317     extended: Zir.Inst.Extended.InstData,
  25318     zir_tag: Zir.Inst.Extended,
  25319 ) CompileError!Air.Inst.Ref {
  25320     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25321     const dimension_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  25322     const builtin_src = LazySrcLoc.nodeOffset(extra.node);
  25323     const target = sema.mod.getTarget();
  25324 
  25325     switch (target.cpu.arch) {
  25326         // TODO: Allow for other GPU targets.
  25327         .amdgcn => {},
  25328         else => {
  25329             return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)});
  25330         },
  25331     }
  25332 
  25333     const dimension: u32 = @intCast(try sema.resolveInt(block, dimension_src, extra.operand, Type.u32, .{
  25334         .needed_comptime_reason = "dimension must be comptime-known",
  25335     }));
  25336     try sema.requireRuntimeBlock(block, builtin_src, null);
  25337 
  25338     return block.addInst(.{
  25339         .tag = switch (zir_tag) {
  25340             .work_item_id => .work_item_id,
  25341             .work_group_size => .work_group_size,
  25342             .work_group_id => .work_group_id,
  25343             else => unreachable,
  25344         },
  25345         .data = .{ .pl_op = .{
  25346             .operand = .none,
  25347             .payload = dimension,
  25348         } },
  25349     });
  25350 }
  25351 
  25352 fn zirInComptime(
  25353     sema: *Sema,
  25354     block: *Block,
  25355 ) CompileError!Air.Inst.Ref {
  25356     _ = sema;
  25357     return if (block.is_comptime) .bool_true else .bool_false;
  25358 }
  25359 
  25360 fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
  25361     if (block.is_comptime) {
  25362         const msg = msg: {
  25363             const msg = try sema.errMsg(block, src, "unable to evaluate comptime expression", .{});
  25364             errdefer msg.destroy(sema.gpa);
  25365 
  25366             if (runtime_src) |some| {
  25367                 try sema.errNote(block, some, msg, "operation is runtime due to this operand", .{});
  25368             }
  25369             if (block.comptime_reason) |some| {
  25370                 try some.explain(sema, msg);
  25371             }
  25372             break :msg msg;
  25373         };
  25374         return sema.failWithOwnedErrorMsg(block, msg);
  25375     }
  25376 }
  25377 
  25378 /// Emit a compile error if type cannot be used for a runtime variable.
  25379 fn validateVarType(
  25380     sema: *Sema,
  25381     block: *Block,
  25382     src: LazySrcLoc,
  25383     var_ty: Type,
  25384     is_extern: bool,
  25385 ) CompileError!void {
  25386     const mod = sema.mod;
  25387     if (is_extern) {
  25388         if (!try sema.validateExternType(var_ty, .other)) {
  25389             const msg = msg: {
  25390                 const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)});
  25391                 errdefer msg.destroy(sema.gpa);
  25392                 const src_decl = mod.declPtr(block.src_decl);
  25393                 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), var_ty, .other);
  25394                 break :msg msg;
  25395             };
  25396             return sema.failWithOwnedErrorMsg(block, msg);
  25397         }
  25398     } else {
  25399         if (var_ty.zigTypeTag(mod) == .Opaque) {
  25400             return sema.fail(
  25401                 block,
  25402                 src,
  25403                 "non-extern variable with opaque type '{}'",
  25404                 .{var_ty.fmt(mod)},
  25405             );
  25406         }
  25407     }
  25408 
  25409     if (!try sema.typeRequiresComptime(var_ty)) return;
  25410 
  25411     const msg = msg: {
  25412         const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(mod)});
  25413         errdefer msg.destroy(sema.gpa);
  25414 
  25415         const src_decl = mod.declPtr(block.src_decl);
  25416         try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), var_ty);
  25417         if (var_ty.zigTypeTag(mod) == .ComptimeInt or var_ty.zigTypeTag(mod) == .ComptimeFloat) {
  25418             try sema.errNote(block, src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{});
  25419         }
  25420 
  25421         break :msg msg;
  25422     };
  25423     return sema.failWithOwnedErrorMsg(block, msg);
  25424 }
  25425 
  25426 const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void);
  25427 
  25428 fn explainWhyTypeIsComptime(
  25429     sema: *Sema,
  25430     msg: *Module.ErrorMsg,
  25431     src_loc: Module.SrcLoc,
  25432     ty: Type,
  25433 ) CompileError!void {
  25434     var type_set = TypeSet{};
  25435     defer type_set.deinit(sema.gpa);
  25436 
  25437     try sema.resolveTypeFully(ty);
  25438     return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set);
  25439 }
  25440 
  25441 fn explainWhyTypeIsComptimeInner(
  25442     sema: *Sema,
  25443     msg: *Module.ErrorMsg,
  25444     src_loc: Module.SrcLoc,
  25445     ty: Type,
  25446     type_set: *TypeSet,
  25447 ) CompileError!void {
  25448     const mod = sema.mod;
  25449     const ip = &mod.intern_pool;
  25450     switch (ty.zigTypeTag(mod)) {
  25451         .Bool,
  25452         .Int,
  25453         .Float,
  25454         .ErrorSet,
  25455         .Enum,
  25456         .Frame,
  25457         .AnyFrame,
  25458         .Void,
  25459         => return,
  25460 
  25461         .Fn => {
  25462             try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{
  25463                 ty.fmt(sema.mod),
  25464             });
  25465         },
  25466 
  25467         .Type => {
  25468             try mod.errNoteNonLazy(src_loc, msg, "types are not available at runtime", .{});
  25469         },
  25470 
  25471         .ComptimeFloat,
  25472         .ComptimeInt,
  25473         .EnumLiteral,
  25474         .NoReturn,
  25475         .Undefined,
  25476         .Null,
  25477         => return,
  25478 
  25479         .Opaque => {
  25480             try mod.errNoteNonLazy(src_loc, msg, "opaque type '{}' has undefined size", .{ty.fmt(sema.mod)});
  25481         },
  25482 
  25483         .Array, .Vector => {
  25484             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set);
  25485         },
  25486         .Pointer => {
  25487             const elem_ty = ty.elemType2(mod);
  25488             if (elem_ty.zigTypeTag(mod) == .Fn) {
  25489                 const fn_info = mod.typeToFunc(elem_ty).?;
  25490                 if (fn_info.is_generic) {
  25491                     try mod.errNoteNonLazy(src_loc, msg, "function is generic", .{});
  25492                 }
  25493                 switch (fn_info.cc) {
  25494                     .Inline => try mod.errNoteNonLazy(src_loc, msg, "function has inline calling convention", .{}),
  25495                     else => {},
  25496                 }
  25497                 if (Type.fromInterned(fn_info.return_type).comptimeOnly(mod)) {
  25498                     try mod.errNoteNonLazy(src_loc, msg, "function has a comptime-only return type", .{});
  25499                 }
  25500                 return;
  25501             }
  25502             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set);
  25503         },
  25504 
  25505         .Optional => {
  25506             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(mod), type_set);
  25507         },
  25508         .ErrorUnion => {
  25509             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(mod), type_set);
  25510         },
  25511 
  25512         .Struct => {
  25513             if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
  25514 
  25515             if (mod.typeToStruct(ty)) |struct_type| {
  25516                 for (0..struct_type.field_types.len) |i| {
  25517                     const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  25518                     const field_src_loc = mod.fieldSrcLoc(struct_type.decl.unwrap().?, .{
  25519                         .index = i,
  25520                         .range = .type,
  25521                     });
  25522 
  25523                     if (try sema.typeRequiresComptime(field_ty)) {
  25524                         try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{});
  25525                         try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field_ty, type_set);
  25526                     }
  25527                 }
  25528             }
  25529             // TODO tuples
  25530         },
  25531 
  25532         .Union => {
  25533             if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
  25534 
  25535             if (mod.typeToUnion(ty)) |union_obj| {
  25536                 for (0..union_obj.field_types.len) |i| {
  25537                     const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[i]);
  25538                     const field_src_loc = mod.fieldSrcLoc(union_obj.decl, .{
  25539                         .index = i,
  25540                         .range = .type,
  25541                     });
  25542 
  25543                     if (try sema.typeRequiresComptime(field_ty)) {
  25544                         try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{});
  25545                         try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field_ty, type_set);
  25546                     }
  25547                 }
  25548             }
  25549         },
  25550     }
  25551 }
  25552 
  25553 const ExternPosition = enum {
  25554     ret_ty,
  25555     param_ty,
  25556     union_field,
  25557     struct_field,
  25558     element,
  25559     other,
  25560 };
  25561 
  25562 /// Returns true if `ty` is allowed in extern types.
  25563 /// Does *NOT* require `ty` to be resolved in any way.
  25564 /// Calls `resolveTypeLayout` for packed containers.
  25565 fn validateExternType(
  25566     sema: *Sema,
  25567     ty: Type,
  25568     position: ExternPosition,
  25569 ) !bool {
  25570     const mod = sema.mod;
  25571     switch (ty.zigTypeTag(mod)) {
  25572         .Type,
  25573         .ComptimeFloat,
  25574         .ComptimeInt,
  25575         .EnumLiteral,
  25576         .Undefined,
  25577         .Null,
  25578         .ErrorUnion,
  25579         .ErrorSet,
  25580         .Frame,
  25581         => return false,
  25582         .Void => return position == .union_field or position == .ret_ty or position == .struct_field or position == .element,
  25583         .NoReturn => return position == .ret_ty,
  25584         .Opaque,
  25585         .Bool,
  25586         .Float,
  25587         .AnyFrame,
  25588         => return true,
  25589         .Pointer => {
  25590             if (ty.childType(mod).zigTypeTag(mod) == .Fn) {
  25591                 return ty.isConstPtr(mod) and try sema.validateExternType(ty.childType(mod), .other);
  25592             }
  25593             return !(ty.isSlice(mod) or try sema.typeRequiresComptime(ty));
  25594         },
  25595         .Int => switch (ty.intInfo(mod).bits) {
  25596             0, 8, 16, 32, 64, 128 => return true,
  25597             else => return false,
  25598         },
  25599         .Fn => {
  25600             if (position != .other) return false;
  25601             const target = sema.mod.getTarget();
  25602             // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI.
  25603             // The goal is to experiment with more integrated CPU/GPU code.
  25604             if (ty.fnCallingConvention(mod) == .Kernel and (target.cpu.arch == .nvptx or target.cpu.arch == .nvptx64)) {
  25605                 return true;
  25606             }
  25607             return !target_util.fnCallConvAllowsZigTypes(target, ty.fnCallingConvention(mod));
  25608         },
  25609         .Enum => {
  25610             return sema.validateExternType(ty.intTagType(mod), position);
  25611         },
  25612         .Struct, .Union => switch (ty.containerLayout(mod)) {
  25613             .Extern => return true,
  25614             .Packed => {
  25615                 const bit_size = try ty.bitSizeAdvanced(mod, sema);
  25616                 switch (bit_size) {
  25617                     0, 8, 16, 32, 64, 128 => return true,
  25618                     else => return false,
  25619                 }
  25620             },
  25621             .Auto => return !(try sema.typeHasRuntimeBits(ty)),
  25622         },
  25623         .Array => {
  25624             if (position == .ret_ty or position == .param_ty) return false;
  25625             return sema.validateExternType(ty.elemType2(mod), .element);
  25626         },
  25627         .Vector => return sema.validateExternType(ty.elemType2(mod), .element),
  25628         .Optional => return ty.isPtrLikeOptional(mod),
  25629     }
  25630 }
  25631 
  25632 fn explainWhyTypeIsNotExtern(
  25633     sema: *Sema,
  25634     msg: *Module.ErrorMsg,
  25635     src_loc: Module.SrcLoc,
  25636     ty: Type,
  25637     position: ExternPosition,
  25638 ) CompileError!void {
  25639     const mod = sema.mod;
  25640     switch (ty.zigTypeTag(mod)) {
  25641         .Opaque,
  25642         .Bool,
  25643         .Float,
  25644         .AnyFrame,
  25645         => return,
  25646 
  25647         .Type,
  25648         .ComptimeFloat,
  25649         .ComptimeInt,
  25650         .EnumLiteral,
  25651         .Undefined,
  25652         .Null,
  25653         .ErrorUnion,
  25654         .ErrorSet,
  25655         .Frame,
  25656         => return,
  25657 
  25658         .Pointer => {
  25659             if (ty.isSlice(mod)) {
  25660                 try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{});
  25661             } else {
  25662                 const pointee_ty = ty.childType(mod);
  25663                 if (!ty.isConstPtr(mod) and pointee_ty.zigTypeTag(mod) == .Fn) {
  25664                     try mod.errNoteNonLazy(src_loc, msg, "pointer to extern function must be 'const'", .{});
  25665                 } else if (try sema.typeRequiresComptime(ty)) {
  25666                     try mod.errNoteNonLazy(src_loc, msg, "pointer to comptime-only type '{}'", .{pointee_ty.fmt(sema.mod)});
  25667                     try sema.explainWhyTypeIsComptime(msg, src_loc, ty);
  25668                 }
  25669                 try sema.explainWhyTypeIsNotExtern(msg, src_loc, pointee_ty, .other);
  25670             }
  25671         },
  25672         .Void => try mod.errNoteNonLazy(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}),
  25673         .NoReturn => try mod.errNoteNonLazy(src_loc, msg, "'noreturn' is only allowed as a return type", .{}),
  25674         .Int => if (!std.math.isPowerOfTwo(ty.intInfo(mod).bits)) {
  25675             try mod.errNoteNonLazy(src_loc, msg, "only integers with 0 or power of two bits are extern compatible", .{});
  25676         } else {
  25677             try mod.errNoteNonLazy(src_loc, msg, "only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible", .{});
  25678         },
  25679         .Fn => {
  25680             if (position != .other) {
  25681                 try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{});
  25682                 try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{});
  25683                 return;
  25684             }
  25685             switch (ty.fnCallingConvention(mod)) {
  25686                 .Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}),
  25687                 .Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}),
  25688                 .Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}),
  25689                 else => return,
  25690             }
  25691         },
  25692         .Enum => {
  25693             const tag_ty = ty.intTagType(mod);
  25694             try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)});
  25695             try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position);
  25696         },
  25697         .Struct => try mod.errNoteNonLazy(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}),
  25698         .Union => try mod.errNoteNonLazy(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}),
  25699         .Array => {
  25700             if (position == .ret_ty) {
  25701                 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{});
  25702             } else if (position == .param_ty) {
  25703                 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{});
  25704             }
  25705             try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element);
  25706         },
  25707         .Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element),
  25708         .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
  25709     }
  25710 }
  25711 
  25712 /// Returns true if `ty` is allowed in packed types.
  25713 /// Does not require `ty` to be resolved in any way, but may resolve whether it is comptime-only.
  25714 fn validatePackedType(sema: *Sema, ty: Type) !bool {
  25715     const mod = sema.mod;
  25716     switch (ty.zigTypeTag(mod)) {
  25717         .Type,
  25718         .ComptimeFloat,
  25719         .ComptimeInt,
  25720         .EnumLiteral,
  25721         .Undefined,
  25722         .Null,
  25723         .ErrorUnion,
  25724         .ErrorSet,
  25725         .Frame,
  25726         .NoReturn,
  25727         .Opaque,
  25728         .AnyFrame,
  25729         .Fn,
  25730         .Array,
  25731         => return false,
  25732         .Optional => return ty.isPtrLikeOptional(mod),
  25733         .Void,
  25734         .Bool,
  25735         .Float,
  25736         .Int,
  25737         .Vector,
  25738         .Enum,
  25739         => return true,
  25740         .Pointer => return !ty.isSlice(mod) and !try sema.typeRequiresComptime(ty),
  25741         .Struct, .Union => return ty.containerLayout(mod) == .Packed,
  25742     }
  25743 }
  25744 
  25745 fn explainWhyTypeIsNotPacked(
  25746     sema: *Sema,
  25747     msg: *Module.ErrorMsg,
  25748     src_loc: Module.SrcLoc,
  25749     ty: Type,
  25750 ) CompileError!void {
  25751     const mod = sema.mod;
  25752     switch (ty.zigTypeTag(mod)) {
  25753         .Void,
  25754         .Bool,
  25755         .Float,
  25756         .Int,
  25757         .Vector,
  25758         .Enum,
  25759         => return,
  25760         .Type,
  25761         .ComptimeFloat,
  25762         .ComptimeInt,
  25763         .EnumLiteral,
  25764         .Undefined,
  25765         .Null,
  25766         .Frame,
  25767         .NoReturn,
  25768         .Opaque,
  25769         .ErrorUnion,
  25770         .ErrorSet,
  25771         .AnyFrame,
  25772         .Optional,
  25773         .Array,
  25774         => try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}),
  25775         .Pointer => if (ty.isSlice(mod)) {
  25776             try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{});
  25777         } else {
  25778             try mod.errNoteNonLazy(src_loc, msg, "comptime-only pointer has no guaranteed in-memory representation", .{});
  25779             try sema.explainWhyTypeIsComptime(msg, src_loc, ty);
  25780         },
  25781         .Fn => {
  25782             try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{});
  25783             try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{});
  25784         },
  25785         .Struct => try mod.errNoteNonLazy(src_loc, msg, "only packed structs layout are allowed in packed types", .{}),
  25786         .Union => try mod.errNoteNonLazy(src_loc, msg, "only packed unions layout are allowed in packed types", .{}),
  25787     }
  25788 }
  25789 
  25790 fn prepareSimplePanic(sema: *Sema, block: *Block) !void {
  25791     const mod = sema.mod;
  25792 
  25793     if (mod.panic_func_index == .none) {
  25794         const decl_index = (try sema.getBuiltinDecl(block, "panic"));
  25795         // decl_index may be an alias; we must find the decl that actually
  25796         // owns the function.
  25797         try sema.ensureDeclAnalyzed(decl_index);
  25798         const tv = try mod.declPtr(decl_index).typedValue();
  25799         assert(tv.ty.zigTypeTag(mod) == .Fn);
  25800         assert(try sema.fnHasRuntimeBits(tv.ty));
  25801         const func_index = tv.val.toIntern();
  25802         try mod.ensureFuncBodyAnalysisQueued(func_index);
  25803         mod.panic_func_index = func_index;
  25804     }
  25805 
  25806     if (mod.null_stack_trace == .none) {
  25807         const stack_trace_ty = try sema.getBuiltinType("StackTrace");
  25808         try sema.resolveTypeFields(stack_trace_ty);
  25809         const target = mod.getTarget();
  25810         const ptr_stack_trace_ty = try sema.ptrType(.{
  25811             .child = stack_trace_ty.toIntern(),
  25812             .flags = .{
  25813                 .address_space = target_util.defaultAddressSpace(target, .global_constant),
  25814             },
  25815         });
  25816         const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern());
  25817         mod.null_stack_trace = try mod.intern(.{ .opt = .{
  25818             .ty = opt_ptr_stack_trace_ty.toIntern(),
  25819             .val = .none,
  25820         } });
  25821     }
  25822 }
  25823 
  25824 /// Backends depend on panic decls being available when lowering safety-checked
  25825 /// instructions. This function ensures the panic function will be available to
  25826 /// be called during that time.
  25827 fn preparePanicId(sema: *Sema, block: *Block, panic_id: Module.PanicId) !InternPool.DeclIndex {
  25828     const mod = sema.mod;
  25829     const gpa = sema.gpa;
  25830     if (mod.panic_messages[@intFromEnum(panic_id)].unwrap()) |x| return x;
  25831 
  25832     try sema.prepareSimplePanic(block);
  25833 
  25834     const panic_messages_ty = try sema.getBuiltinType("panic_messages");
  25835     const msg_decl_index = (try sema.namespaceLookup(
  25836         block,
  25837         sema.src,
  25838         panic_messages_ty.getNamespaceIndex(mod).unwrap().?,
  25839         try mod.intern_pool.getOrPutString(gpa, @tagName(panic_id)),
  25840     )).?;
  25841     try sema.ensureDeclAnalyzed(msg_decl_index);
  25842     mod.panic_messages[@intFromEnum(panic_id)] = msg_decl_index.toOptional();
  25843     return msg_decl_index;
  25844 }
  25845 
  25846 fn addSafetyCheck(
  25847     sema: *Sema,
  25848     parent_block: *Block,
  25849     src: LazySrcLoc,
  25850     ok: Air.Inst.Ref,
  25851     panic_id: Module.PanicId,
  25852 ) !void {
  25853     const gpa = sema.gpa;
  25854     assert(!parent_block.is_comptime);
  25855 
  25856     var fail_block: Block = .{
  25857         .parent = parent_block,
  25858         .sema = sema,
  25859         .src_decl = parent_block.src_decl,
  25860         .namespace = parent_block.namespace,
  25861         .wip_capture_scope = parent_block.wip_capture_scope,
  25862         .instructions = .{},
  25863         .inlining = parent_block.inlining,
  25864         .is_comptime = false,
  25865     };
  25866 
  25867     defer fail_block.instructions.deinit(gpa);
  25868 
  25869     try sema.safetyPanic(&fail_block, src, panic_id);
  25870     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  25871 }
  25872 
  25873 fn addSafetyCheckExtra(
  25874     sema: *Sema,
  25875     parent_block: *Block,
  25876     ok: Air.Inst.Ref,
  25877     fail_block: *Block,
  25878 ) !void {
  25879     const gpa = sema.gpa;
  25880 
  25881     try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
  25882 
  25883     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
  25884         1 + // The main block only needs space for the cond_br.
  25885         @typeInfo(Air.CondBr).Struct.fields.len +
  25886         1 + // The ok branch of the cond_br only needs space for the br.
  25887         fail_block.instructions.items.len);
  25888 
  25889     try sema.air_instructions.ensureUnusedCapacity(gpa, 3);
  25890     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  25891     const cond_br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1);
  25892     const br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(cond_br_inst) + 1);
  25893     sema.air_instructions.appendAssumeCapacity(.{
  25894         .tag = .block,
  25895         .data = .{ .ty_pl = .{
  25896             .ty = .void_type,
  25897             .payload = sema.addExtraAssumeCapacity(Air.Block{
  25898                 .body_len = 1,
  25899             }),
  25900         } },
  25901     });
  25902     sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst));
  25903 
  25904     sema.air_instructions.appendAssumeCapacity(.{
  25905         .tag = .cond_br,
  25906         .data = .{ .pl_op = .{
  25907             .operand = ok,
  25908             .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  25909                 .then_body_len = 1,
  25910                 .else_body_len = @intCast(fail_block.instructions.items.len),
  25911             }),
  25912         } },
  25913     });
  25914     sema.air_extra.appendAssumeCapacity(@intFromEnum(br_inst));
  25915     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(fail_block.instructions.items));
  25916 
  25917     sema.air_instructions.appendAssumeCapacity(.{
  25918         .tag = .br,
  25919         .data = .{ .br = .{
  25920             .block_inst = block_inst,
  25921             .operand = .void_value,
  25922         } },
  25923     });
  25924 
  25925     parent_block.instructions.appendAssumeCapacity(block_inst);
  25926 }
  25927 
  25928 fn panicWithMsg(sema: *Sema, block: *Block, src: LazySrcLoc, msg_inst: Air.Inst.Ref, operation: CallOperation) !void {
  25929     const mod = sema.mod;
  25930 
  25931     if (!mod.backendSupportsFeature(.panic_fn)) {
  25932         _ = try block.addNoOp(.trap);
  25933         return;
  25934     }
  25935 
  25936     try sema.prepareSimplePanic(block);
  25937 
  25938     const panic_func = mod.funcInfo(mod.panic_func_index);
  25939     const panic_fn = try sema.analyzeDeclVal(block, src, panic_func.owner_decl);
  25940     const null_stack_trace = Air.internedToRef(mod.null_stack_trace);
  25941 
  25942     const opt_usize_ty = try mod.optionalType(.usize_type);
  25943     const null_ret_addr = Air.internedToRef((try mod.intern(.{ .opt = .{
  25944         .ty = opt_usize_ty.toIntern(),
  25945         .val = .none,
  25946     } })));
  25947     try sema.callBuiltin(block, src, panic_fn, .auto, &.{ msg_inst, null_stack_trace, null_ret_addr }, operation);
  25948 }
  25949 
  25950 fn panicUnwrapError(
  25951     sema: *Sema,
  25952     parent_block: *Block,
  25953     src: LazySrcLoc,
  25954     operand: Air.Inst.Ref,
  25955     unwrap_err_tag: Air.Inst.Tag,
  25956     is_non_err_tag: Air.Inst.Tag,
  25957 ) !void {
  25958     assert(!parent_block.is_comptime);
  25959     const ok = try parent_block.addUnOp(is_non_err_tag, operand);
  25960     if (!sema.mod.comp.formatted_panics) {
  25961         return sema.addSafetyCheck(parent_block, src, ok, .unwrap_error);
  25962     }
  25963     const gpa = sema.gpa;
  25964 
  25965     var fail_block: Block = .{
  25966         .parent = parent_block,
  25967         .sema = sema,
  25968         .src_decl = parent_block.src_decl,
  25969         .namespace = parent_block.namespace,
  25970         .wip_capture_scope = parent_block.wip_capture_scope,
  25971         .instructions = .{},
  25972         .inlining = parent_block.inlining,
  25973         .is_comptime = false,
  25974     };
  25975 
  25976     defer fail_block.instructions.deinit(gpa);
  25977 
  25978     {
  25979         if (!sema.mod.backendSupportsFeature(.panic_unwrap_error)) {
  25980             _ = try fail_block.addNoOp(.trap);
  25981         } else {
  25982             const panic_fn = try sema.getBuiltin("panicUnwrapError");
  25983             const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
  25984             const err_return_trace = try sema.getErrorReturnTrace(&fail_block);
  25985             const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
  25986             try sema.callBuiltin(&fail_block, src, panic_fn, .auto, &args, .@"safety check");
  25987         }
  25988     }
  25989     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  25990 }
  25991 
  25992 fn panicIndexOutOfBounds(
  25993     sema: *Sema,
  25994     parent_block: *Block,
  25995     src: LazySrcLoc,
  25996     index: Air.Inst.Ref,
  25997     len: Air.Inst.Ref,
  25998     cmp_op: Air.Inst.Tag,
  25999 ) !void {
  26000     assert(!parent_block.is_comptime);
  26001     const ok = try parent_block.addBinOp(cmp_op, index, len);
  26002     if (!sema.mod.comp.formatted_panics) {
  26003         return sema.addSafetyCheck(parent_block, src, ok, .index_out_of_bounds);
  26004     }
  26005     try sema.safetyCheckFormatted(parent_block, src, ok, "panicOutOfBounds", &.{ index, len });
  26006 }
  26007 
  26008 fn panicInactiveUnionField(
  26009     sema: *Sema,
  26010     parent_block: *Block,
  26011     src: LazySrcLoc,
  26012     active_tag: Air.Inst.Ref,
  26013     wanted_tag: Air.Inst.Ref,
  26014 ) !void {
  26015     assert(!parent_block.is_comptime);
  26016     const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
  26017     if (!sema.mod.comp.formatted_panics) {
  26018         return sema.addSafetyCheck(parent_block, src, ok, .inactive_union_field);
  26019     }
  26020     try sema.safetyCheckFormatted(parent_block, src, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
  26021 }
  26022 
  26023 fn panicSentinelMismatch(
  26024     sema: *Sema,
  26025     parent_block: *Block,
  26026     src: LazySrcLoc,
  26027     maybe_sentinel: ?Value,
  26028     sentinel_ty: Type,
  26029     ptr: Air.Inst.Ref,
  26030     sentinel_index: Air.Inst.Ref,
  26031 ) !void {
  26032     assert(!parent_block.is_comptime);
  26033     const mod = sema.mod;
  26034     const expected_sentinel_val = maybe_sentinel orelse return;
  26035     const expected_sentinel = Air.internedToRef(expected_sentinel_val.toIntern());
  26036 
  26037     const ptr_ty = sema.typeOf(ptr);
  26038     const actual_sentinel = if (ptr_ty.isSlice(mod))
  26039         try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index)
  26040     else blk: {
  26041         const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null);
  26042         const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty);
  26043         break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr);
  26044     };
  26045 
  26046     const ok = if (sentinel_ty.zigTypeTag(mod) == .Vector) ok: {
  26047         const eql =
  26048             try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq);
  26049         break :ok try parent_block.addInst(.{
  26050             .tag = .reduce,
  26051             .data = .{ .reduce = .{
  26052                 .operand = eql,
  26053                 .operation = .And,
  26054             } },
  26055         });
  26056     } else if (sentinel_ty.isSelfComparable(mod, true))
  26057         try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel)
  26058     else {
  26059         const panic_fn = try sema.getBuiltin("checkNonScalarSentinel");
  26060         const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
  26061         try sema.callBuiltin(parent_block, src, panic_fn, .auto, &args, .@"safety check");
  26062         return;
  26063     };
  26064 
  26065     if (!sema.mod.comp.formatted_panics) {
  26066         return sema.addSafetyCheck(parent_block, src, ok, .sentinel_mismatch);
  26067     }
  26068     try sema.safetyCheckFormatted(parent_block, src, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel });
  26069 }
  26070 
  26071 fn safetyCheckFormatted(
  26072     sema: *Sema,
  26073     parent_block: *Block,
  26074     src: LazySrcLoc,
  26075     ok: Air.Inst.Ref,
  26076     func: []const u8,
  26077     args: []const Air.Inst.Ref,
  26078 ) CompileError!void {
  26079     assert(sema.mod.comp.formatted_panics);
  26080     const gpa = sema.gpa;
  26081 
  26082     var fail_block: Block = .{
  26083         .parent = parent_block,
  26084         .sema = sema,
  26085         .src_decl = parent_block.src_decl,
  26086         .namespace = parent_block.namespace,
  26087         .wip_capture_scope = parent_block.wip_capture_scope,
  26088         .instructions = .{},
  26089         .inlining = parent_block.inlining,
  26090         .is_comptime = false,
  26091     };
  26092 
  26093     defer fail_block.instructions.deinit(gpa);
  26094 
  26095     if (!sema.mod.backendSupportsFeature(.safety_check_formatted)) {
  26096         _ = try fail_block.addNoOp(.trap);
  26097     } else {
  26098         const panic_fn = try sema.getBuiltin(func);
  26099         try sema.callBuiltin(&fail_block, src, panic_fn, .auto, args, .@"safety check");
  26100     }
  26101     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  26102 }
  26103 
  26104 fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Module.PanicId) CompileError!void {
  26105     const msg_decl_index = try sema.preparePanicId(block, panic_id);
  26106     const msg_inst = try sema.analyzeDeclVal(block, src, msg_decl_index);
  26107     try sema.panicWithMsg(block, src, msg_inst, .@"safety check");
  26108 }
  26109 
  26110 fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
  26111     sema.branch_count += 1;
  26112     if (sema.branch_count > sema.branch_quota) {
  26113         const msg = try sema.errMsg(
  26114             block,
  26115             src,
  26116             "evaluation exceeded {d} backwards branches",
  26117             .{sema.branch_quota},
  26118         );
  26119         try sema.errNote(
  26120             block,
  26121             src,
  26122             msg,
  26123             "use @setEvalBranchQuota() to raise the branch limit from {d}",
  26124             .{sema.branch_quota},
  26125         );
  26126         return sema.failWithOwnedErrorMsg(block, msg);
  26127     }
  26128 }
  26129 
  26130 fn fieldVal(
  26131     sema: *Sema,
  26132     block: *Block,
  26133     src: LazySrcLoc,
  26134     object: Air.Inst.Ref,
  26135     field_name: InternPool.NullTerminatedString,
  26136     field_name_src: LazySrcLoc,
  26137 ) CompileError!Air.Inst.Ref {
  26138     // When editing this function, note that there is corresponding logic to be edited
  26139     // in `fieldPtr`. This function takes a value and returns a value.
  26140 
  26141     const mod = sema.mod;
  26142     const ip = &mod.intern_pool;
  26143     const object_src = src; // TODO better source location
  26144     const object_ty = sema.typeOf(object);
  26145 
  26146     // Zig allows dereferencing a single pointer during field lookup. Note that
  26147     // we don't actually need to generate the dereference some field lookups, like the
  26148     // length of arrays and other comptime operations.
  26149     const is_pointer_to = object_ty.isSinglePointer(mod);
  26150 
  26151     const inner_ty = if (is_pointer_to)
  26152         object_ty.childType(mod)
  26153     else
  26154         object_ty;
  26155 
  26156     switch (inner_ty.zigTypeTag(mod)) {
  26157         .Array => {
  26158             if (ip.stringEqlSlice(field_name, "len")) {
  26159                 return Air.internedToRef((try mod.intValue(Type.usize, inner_ty.arrayLen(mod))).toIntern());
  26160             } else if (ip.stringEqlSlice(field_name, "ptr") and is_pointer_to) {
  26161                 const ptr_info = object_ty.ptrInfo(mod);
  26162                 const result_ty = try sema.ptrType(.{
  26163                     .child = Type.fromInterned(ptr_info.child).childType(mod).toIntern(),
  26164                     .sentinel = if (inner_ty.sentinel(mod)) |s| s.toIntern() else .none,
  26165                     .flags = .{
  26166                         .size = .Many,
  26167                         .alignment = ptr_info.flags.alignment,
  26168                         .is_const = ptr_info.flags.is_const,
  26169                         .is_volatile = ptr_info.flags.is_volatile,
  26170                         .is_allowzero = ptr_info.flags.is_allowzero,
  26171                         .address_space = ptr_info.flags.address_space,
  26172                         .vector_index = ptr_info.flags.vector_index,
  26173                     },
  26174                     .packed_offset = ptr_info.packed_offset,
  26175                 });
  26176                 return sema.coerce(block, result_ty, object, src);
  26177             } else {
  26178                 return sema.fail(
  26179                     block,
  26180                     field_name_src,
  26181                     "no member named '{}' in '{}'",
  26182                     .{ field_name.fmt(ip), object_ty.fmt(mod) },
  26183                 );
  26184             }
  26185         },
  26186         .Pointer => {
  26187             const ptr_info = inner_ty.ptrInfo(mod);
  26188             if (ptr_info.flags.size == .Slice) {
  26189                 if (ip.stringEqlSlice(field_name, "ptr")) {
  26190                     const slice = if (is_pointer_to)
  26191                         try sema.analyzeLoad(block, src, object, object_src)
  26192                     else
  26193                         object;
  26194                     return sema.analyzeSlicePtr(block, object_src, slice, inner_ty);
  26195                 } else if (ip.stringEqlSlice(field_name, "len")) {
  26196                     const slice = if (is_pointer_to)
  26197                         try sema.analyzeLoad(block, src, object, object_src)
  26198                     else
  26199                         object;
  26200                     return sema.analyzeSliceLen(block, src, slice);
  26201                 } else {
  26202                     return sema.fail(
  26203                         block,
  26204                         field_name_src,
  26205                         "no member named '{}' in '{}'",
  26206                         .{ field_name.fmt(ip), object_ty.fmt(mod) },
  26207                     );
  26208                 }
  26209             }
  26210         },
  26211         .Type => {
  26212             const dereffed_type = if (is_pointer_to)
  26213                 try sema.analyzeLoad(block, src, object, object_src)
  26214             else
  26215                 object;
  26216 
  26217             const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
  26218             const child_type = val.toType();
  26219 
  26220             switch (try child_type.zigTypeTagOrPoison(mod)) {
  26221                 .ErrorSet => {
  26222                     switch (ip.indexToKey(child_type.toIntern())) {
  26223                         .error_set_type => |error_set_type| blk: {
  26224                             if (error_set_type.nameIndex(ip, field_name) != null) break :blk;
  26225                             const msg = msg: {
  26226                                 const msg = try sema.errMsg(block, src, "no error named '{}' in '{}'", .{
  26227                                     field_name.fmt(ip), child_type.fmt(mod),
  26228                                 });
  26229                                 errdefer msg.destroy(sema.gpa);
  26230                                 try sema.addDeclaredHereNote(msg, child_type);
  26231                                 break :msg msg;
  26232                             };
  26233                             return sema.failWithOwnedErrorMsg(block, msg);
  26234                         },
  26235                         .inferred_error_set_type => {
  26236                             return sema.fail(block, src, "TODO handle inferred error sets here", .{});
  26237                         },
  26238                         .simple_type => |t| {
  26239                             assert(t == .anyerror);
  26240                             _ = try mod.getErrorValue(field_name);
  26241                         },
  26242                         else => unreachable,
  26243                     }
  26244 
  26245                     const error_set_type = if (!child_type.isAnyError(mod))
  26246                         child_type
  26247                     else
  26248                         try mod.singleErrorSetType(field_name);
  26249                     return Air.internedToRef((try mod.intern(.{ .err = .{
  26250                         .ty = error_set_type.toIntern(),
  26251                         .name = field_name,
  26252                     } })));
  26253                 },
  26254                 .Union => {
  26255                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  26256                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
  26257                             return inst;
  26258                         }
  26259                     }
  26260                     try sema.resolveTypeFields(child_type);
  26261                     if (child_type.unionTagType(mod)) |enum_ty| {
  26262                         if (enum_ty.enumFieldIndex(field_name, mod)) |field_index_usize| {
  26263                             const field_index: u32 = @intCast(field_index_usize);
  26264                             return Air.internedToRef((try mod.enumValueFieldIndex(enum_ty, field_index)).toIntern());
  26265                         }
  26266                     }
  26267                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26268                 },
  26269                 .Enum => {
  26270                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  26271                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
  26272                             return inst;
  26273                         }
  26274                     }
  26275                     const field_index_usize = child_type.enumFieldIndex(field_name, mod) orelse
  26276                         return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26277                     const field_index: u32 = @intCast(field_index_usize);
  26278                     const enum_val = try mod.enumValueFieldIndex(child_type, field_index);
  26279                     return Air.internedToRef(enum_val.toIntern());
  26280                 },
  26281                 .Struct, .Opaque => {
  26282                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  26283                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
  26284                             return inst;
  26285                         }
  26286                     }
  26287                     return sema.failWithBadMemberAccess(block, child_type, src, field_name);
  26288                 },
  26289                 else => {
  26290                     const msg = msg: {
  26291                         const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(mod)});
  26292                         errdefer msg.destroy(sema.gpa);
  26293                         if (child_type.isSlice(mod)) try sema.errNote(block, src, msg, "slice values have 'len' and 'ptr' members", .{});
  26294                         if (child_type.zigTypeTag(mod) == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{});
  26295                         break :msg msg;
  26296                     };
  26297                     return sema.failWithOwnedErrorMsg(block, msg);
  26298                 },
  26299             }
  26300         },
  26301         .Struct => if (is_pointer_to) {
  26302             // Avoid loading the entire struct by fetching a pointer and loading that
  26303             const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
  26304             return sema.analyzeLoad(block, src, field_ptr, object_src);
  26305         } else {
  26306             return sema.structFieldVal(block, src, object, field_name, field_name_src, inner_ty);
  26307         },
  26308         .Union => if (is_pointer_to) {
  26309             // Avoid loading the entire union by fetching a pointer and loading that
  26310             const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
  26311             return sema.analyzeLoad(block, src, field_ptr, object_src);
  26312         } else {
  26313             return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
  26314         },
  26315         else => {},
  26316     }
  26317     return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
  26318 }
  26319 
  26320 fn fieldPtr(
  26321     sema: *Sema,
  26322     block: *Block,
  26323     src: LazySrcLoc,
  26324     object_ptr: Air.Inst.Ref,
  26325     field_name: InternPool.NullTerminatedString,
  26326     field_name_src: LazySrcLoc,
  26327     initializing: bool,
  26328 ) CompileError!Air.Inst.Ref {
  26329     // When editing this function, note that there is corresponding logic to be edited
  26330     // in `fieldVal`. This function takes a pointer and returns a pointer.
  26331 
  26332     const mod = sema.mod;
  26333     const ip = &mod.intern_pool;
  26334     const object_ptr_src = src; // TODO better source location
  26335     const object_ptr_ty = sema.typeOf(object_ptr);
  26336     const object_ty = switch (object_ptr_ty.zigTypeTag(mod)) {
  26337         .Pointer => object_ptr_ty.childType(mod),
  26338         else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(mod)}),
  26339     };
  26340 
  26341     // Zig allows dereferencing a single pointer during field lookup. Note that
  26342     // we don't actually need to generate the dereference some field lookups, like the
  26343     // length of arrays and other comptime operations.
  26344     const is_pointer_to = object_ty.isSinglePointer(mod);
  26345 
  26346     const inner_ty = if (is_pointer_to)
  26347         object_ty.childType(mod)
  26348     else
  26349         object_ty;
  26350 
  26351     switch (inner_ty.zigTypeTag(mod)) {
  26352         .Array => {
  26353             if (ip.stringEqlSlice(field_name, "len")) {
  26354                 const int_val = try mod.intValue(Type.usize, inner_ty.arrayLen(mod));
  26355                 return anonDeclRef(sema, int_val.toIntern());
  26356             } else {
  26357                 return sema.fail(
  26358                     block,
  26359                     field_name_src,
  26360                     "no member named '{}' in '{}'",
  26361                     .{ field_name.fmt(ip), object_ty.fmt(mod) },
  26362                 );
  26363             }
  26364         },
  26365         .Pointer => if (inner_ty.isSlice(mod)) {
  26366             const inner_ptr = if (is_pointer_to)
  26367                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  26368             else
  26369                 object_ptr;
  26370 
  26371             const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty;
  26372 
  26373             if (ip.stringEqlSlice(field_name, "ptr")) {
  26374                 const slice_ptr_ty = inner_ty.slicePtrFieldType(mod);
  26375 
  26376                 const result_ty = try sema.ptrType(.{
  26377                     .child = slice_ptr_ty.toIntern(),
  26378                     .flags = .{
  26379                         .is_const = !attr_ptr_ty.ptrIsMutable(mod),
  26380                         .is_volatile = attr_ptr_ty.isVolatilePtr(mod),
  26381                         .address_space = attr_ptr_ty.ptrAddressSpace(mod),
  26382                     },
  26383                 });
  26384 
  26385                 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
  26386                     return Air.internedToRef((try mod.intern(.{ .ptr = .{
  26387                         .ty = result_ty.toIntern(),
  26388                         .addr = .{ .field = .{
  26389                             .base = val.toIntern(),
  26390                             .index = Value.slice_ptr_index,
  26391                         } },
  26392                     } })));
  26393                 }
  26394                 try sema.requireRuntimeBlock(block, src, null);
  26395 
  26396                 const field_ptr = try block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
  26397                 try sema.checkKnownAllocPtr(inner_ptr, field_ptr);
  26398                 return field_ptr;
  26399             } else if (ip.stringEqlSlice(field_name, "len")) {
  26400                 const result_ty = try sema.ptrType(.{
  26401                     .child = .usize_type,
  26402                     .flags = .{
  26403                         .is_const = !attr_ptr_ty.ptrIsMutable(mod),
  26404                         .is_volatile = attr_ptr_ty.isVolatilePtr(mod),
  26405                         .address_space = attr_ptr_ty.ptrAddressSpace(mod),
  26406                     },
  26407                 });
  26408 
  26409                 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
  26410                     return Air.internedToRef((try mod.intern(.{ .ptr = .{
  26411                         .ty = result_ty.toIntern(),
  26412                         .addr = .{ .field = .{
  26413                             .base = val.toIntern(),
  26414                             .index = Value.slice_len_index,
  26415                         } },
  26416                     } })));
  26417                 }
  26418                 try sema.requireRuntimeBlock(block, src, null);
  26419 
  26420                 const field_ptr = try block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr);
  26421                 try sema.checkKnownAllocPtr(inner_ptr, field_ptr);
  26422                 return field_ptr;
  26423             } else {
  26424                 return sema.fail(
  26425                     block,
  26426                     field_name_src,
  26427                     "no member named '{}' in '{}'",
  26428                     .{ field_name.fmt(ip), object_ty.fmt(mod) },
  26429                 );
  26430             }
  26431         },
  26432         .Type => {
  26433             _ = try sema.resolveConstDefinedValue(block, .unneeded, object_ptr, undefined);
  26434             const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
  26435             const inner = if (is_pointer_to)
  26436                 try sema.analyzeLoad(block, src, result, object_ptr_src)
  26437             else
  26438                 result;
  26439 
  26440             const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
  26441             const child_type = val.toType();
  26442 
  26443             switch (child_type.zigTypeTag(mod)) {
  26444                 .ErrorSet => {
  26445                     switch (ip.indexToKey(child_type.toIntern())) {
  26446                         .error_set_type => |error_set_type| blk: {
  26447                             if (error_set_type.nameIndex(ip, field_name) != null) {
  26448                                 break :blk;
  26449                             }
  26450                             return sema.fail(block, src, "no error named '{}' in '{}'", .{
  26451                                 field_name.fmt(ip), child_type.fmt(mod),
  26452                             });
  26453                         },
  26454                         .inferred_error_set_type => {
  26455                             return sema.fail(block, src, "TODO handle inferred error sets here", .{});
  26456                         },
  26457                         .simple_type => |t| {
  26458                             assert(t == .anyerror);
  26459                             _ = try mod.getErrorValue(field_name);
  26460                         },
  26461                         else => unreachable,
  26462                     }
  26463 
  26464                     const error_set_type = if (!child_type.isAnyError(mod))
  26465                         child_type
  26466                     else
  26467                         try mod.singleErrorSetType(field_name);
  26468                     return anonDeclRef(sema, try mod.intern(.{ .err = .{
  26469                         .ty = error_set_type.toIntern(),
  26470                         .name = field_name,
  26471                     } }));
  26472                 },
  26473                 .Union => {
  26474                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  26475                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
  26476                             return inst;
  26477                         }
  26478                     }
  26479                     try sema.resolveTypeFields(child_type);
  26480                     if (child_type.unionTagType(mod)) |enum_ty| {
  26481                         if (enum_ty.enumFieldIndex(field_name, mod)) |field_index| {
  26482                             const field_index_u32: u32 = @intCast(field_index);
  26483                             const idx_val = try mod.enumValueFieldIndex(enum_ty, field_index_u32);
  26484                             return anonDeclRef(sema, idx_val.toIntern());
  26485                         }
  26486                     }
  26487                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26488                 },
  26489                 .Enum => {
  26490                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  26491                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
  26492                             return inst;
  26493                         }
  26494                     }
  26495                     const field_index = child_type.enumFieldIndex(field_name, mod) orelse {
  26496                         return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26497                     };
  26498                     const field_index_u32: u32 = @intCast(field_index);
  26499                     const idx_val = try mod.enumValueFieldIndex(child_type, field_index_u32);
  26500                     return anonDeclRef(sema, idx_val.toIntern());
  26501                 },
  26502                 .Struct, .Opaque => {
  26503                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  26504                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
  26505                             return inst;
  26506                         }
  26507                     }
  26508                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26509                 },
  26510                 else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(mod)}),
  26511             }
  26512         },
  26513         .Struct => {
  26514             const inner_ptr = if (is_pointer_to)
  26515                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  26516             else
  26517                 object_ptr;
  26518             const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
  26519             try sema.checkKnownAllocPtr(inner_ptr, field_ptr);
  26520             return field_ptr;
  26521         },
  26522         .Union => {
  26523             const inner_ptr = if (is_pointer_to)
  26524                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  26525             else
  26526                 object_ptr;
  26527             const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
  26528             try sema.checkKnownAllocPtr(inner_ptr, field_ptr);
  26529             return field_ptr;
  26530         },
  26531         else => {},
  26532     }
  26533     return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
  26534 }
  26535 
  26536 const ResolvedFieldCallee = union(enum) {
  26537     /// The LHS of the call was an actual field with this value.
  26538     direct: Air.Inst.Ref,
  26539     /// This is a method call, with the function and first argument given.
  26540     method: struct {
  26541         func_inst: Air.Inst.Ref,
  26542         arg0_inst: Air.Inst.Ref,
  26543     },
  26544 };
  26545 
  26546 fn fieldCallBind(
  26547     sema: *Sema,
  26548     block: *Block,
  26549     src: LazySrcLoc,
  26550     raw_ptr: Air.Inst.Ref,
  26551     field_name: InternPool.NullTerminatedString,
  26552     field_name_src: LazySrcLoc,
  26553 ) CompileError!ResolvedFieldCallee {
  26554     // When editing this function, note that there is corresponding logic to be edited
  26555     // in `fieldVal`. This function takes a pointer and returns a pointer.
  26556 
  26557     const mod = sema.mod;
  26558     const ip = &mod.intern_pool;
  26559     const raw_ptr_src = src; // TODO better source location
  26560     const raw_ptr_ty = sema.typeOf(raw_ptr);
  26561     const inner_ty = if (raw_ptr_ty.zigTypeTag(mod) == .Pointer and (raw_ptr_ty.ptrSize(mod) == .One or raw_ptr_ty.ptrSize(mod) == .C))
  26562         raw_ptr_ty.childType(mod)
  26563     else
  26564         return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(mod)});
  26565 
  26566     // Optionally dereference a second pointer to get the concrete type.
  26567     const is_double_ptr = inner_ty.zigTypeTag(mod) == .Pointer and inner_ty.ptrSize(mod) == .One;
  26568     const concrete_ty = if (is_double_ptr) inner_ty.childType(mod) else inner_ty;
  26569     const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty;
  26570     const object_ptr = if (is_double_ptr)
  26571         try sema.analyzeLoad(block, src, raw_ptr, src)
  26572     else
  26573         raw_ptr;
  26574 
  26575     find_field: {
  26576         switch (concrete_ty.zigTypeTag(mod)) {
  26577             .Struct => {
  26578                 try sema.resolveTypeFields(concrete_ty);
  26579                 if (mod.typeToStruct(concrete_ty)) |struct_type| {
  26580                     const field_index = struct_type.nameIndex(ip, field_name) orelse
  26581                         break :find_field;
  26582                     const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]);
  26583 
  26584                     return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr);
  26585                 } else if (concrete_ty.isTuple(mod)) {
  26586                     if (ip.stringEqlSlice(field_name, "len")) {
  26587                         return .{ .direct = try mod.intRef(Type.usize, concrete_ty.structFieldCount(mod)) };
  26588                     }
  26589                     if (field_name.toUnsigned(ip)) |field_index| {
  26590                         if (field_index >= concrete_ty.structFieldCount(mod)) break :find_field;
  26591                         return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.structFieldType(field_index, mod), field_index, object_ptr);
  26592                     }
  26593                 } else {
  26594                     const max = concrete_ty.structFieldCount(mod);
  26595                     for (0..max) |i_usize| {
  26596                         const i: u32 = @intCast(i_usize);
  26597                         if (field_name == concrete_ty.structFieldName(i, mod).unwrap().?) {
  26598                             return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.structFieldType(i, mod), i, object_ptr);
  26599                         }
  26600                     }
  26601                 }
  26602             },
  26603             .Union => {
  26604                 try sema.resolveTypeFields(concrete_ty);
  26605                 const union_obj = mod.typeToUnion(concrete_ty).?;
  26606                 const field_index = union_obj.nameIndex(ip, field_name) orelse break :find_field;
  26607                 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
  26608 
  26609                 return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr);
  26610             },
  26611             .Type => {
  26612                 const namespace = try sema.analyzeLoad(block, src, object_ptr, src);
  26613                 return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) };
  26614             },
  26615             else => {},
  26616         }
  26617     }
  26618 
  26619     // If we get here, we need to look for a decl in the struct type instead.
  26620     const found_decl = switch (concrete_ty.zigTypeTag(mod)) {
  26621         .Struct, .Opaque, .Union, .Enum => found_decl: {
  26622             if (concrete_ty.getNamespaceIndex(mod).unwrap()) |namespace| {
  26623                 if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| {
  26624                     try sema.addReferencedBy(block, src, decl_idx);
  26625                     const decl_val = try sema.analyzeDeclVal(block, src, decl_idx);
  26626                     const decl_type = sema.typeOf(decl_val);
  26627                     if (mod.typeToFunc(decl_type)) |func_type| f: {
  26628                         if (func_type.param_types.len == 0) break :f;
  26629 
  26630                         const first_param_type = Type.fromInterned(func_type.param_types.get(ip)[0]);
  26631                         // zig fmt: off
  26632                         if (first_param_type.isGenericPoison() or (
  26633                                 first_param_type.zigTypeTag(mod) == .Pointer and
  26634                                 (first_param_type.ptrSize(mod) == .One or
  26635                                 first_param_type.ptrSize(mod) == .C) and
  26636                                 first_param_type.childType(mod).eql(concrete_ty, mod)))
  26637                         {
  26638                         // zig fmt: on
  26639                             // Note that if the param type is generic poison, we know that it must
  26640                             // specifically be `anytype` since it's the first parameter, meaning we
  26641                             // can safely assume it can be a pointer.
  26642                             // TODO: bound fn calls on rvalues should probably
  26643                             // generate a by-value argument somehow.
  26644                             return .{ .method = .{
  26645                                 .func_inst = decl_val,
  26646                                 .arg0_inst = object_ptr,
  26647                             } };
  26648                         } else if (first_param_type.eql(concrete_ty, mod)) {
  26649                             const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  26650                             return .{ .method = .{
  26651                                 .func_inst = decl_val,
  26652                                 .arg0_inst = deref,
  26653                             } };
  26654                         } else if (first_param_type.zigTypeTag(mod) == .Optional) {
  26655                             const child = first_param_type.optionalChild(mod);
  26656                             if (child.eql(concrete_ty, mod)) {
  26657                                 const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  26658                                 return .{ .method = .{
  26659                                     .func_inst = decl_val,
  26660                                     .arg0_inst = deref,
  26661                                 } };
  26662                             } else if (child.zigTypeTag(mod) == .Pointer and
  26663                                 child.ptrSize(mod) == .One and
  26664                                 child.childType(mod).eql(concrete_ty, mod))
  26665                             {
  26666                                 return .{ .method = .{
  26667                                     .func_inst = decl_val,
  26668                                     .arg0_inst = object_ptr,
  26669                                 } };
  26670                             }
  26671                         } else if (first_param_type.zigTypeTag(mod) == .ErrorUnion and
  26672                             first_param_type.errorUnionPayload(mod).eql(concrete_ty, mod))
  26673                         {
  26674                             const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  26675                             return .{ .method = .{
  26676                                 .func_inst = decl_val,
  26677                                 .arg0_inst = deref,
  26678                             } };
  26679                         }
  26680                     }
  26681                     break :found_decl decl_idx;
  26682                 }
  26683             }
  26684             break :found_decl null;
  26685         },
  26686         else => null,
  26687     };
  26688 
  26689     const msg = msg: {
  26690         const msg = try sema.errMsg(block, src, "no field or member function named '{}' in '{}'", .{
  26691             field_name.fmt(ip),
  26692             concrete_ty.fmt(mod),
  26693         });
  26694         errdefer msg.destroy(sema.gpa);
  26695         try sema.addDeclaredHereNote(msg, concrete_ty);
  26696         if (found_decl) |decl_idx| {
  26697             const decl = mod.declPtr(decl_idx);
  26698             try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "'{}' is not a member function", .{field_name.fmt(ip)});
  26699         }
  26700         break :msg msg;
  26701     };
  26702     return sema.failWithOwnedErrorMsg(block, msg);
  26703 }
  26704 
  26705 fn finishFieldCallBind(
  26706     sema: *Sema,
  26707     block: *Block,
  26708     src: LazySrcLoc,
  26709     ptr_ty: Type,
  26710     field_ty: Type,
  26711     field_index: u32,
  26712     object_ptr: Air.Inst.Ref,
  26713 ) CompileError!ResolvedFieldCallee {
  26714     const mod = sema.mod;
  26715     const ptr_field_ty = try sema.ptrType(.{
  26716         .child = field_ty.toIntern(),
  26717         .flags = .{
  26718             .is_const = !ptr_ty.ptrIsMutable(mod),
  26719             .address_space = ptr_ty.ptrAddressSpace(mod),
  26720         },
  26721     });
  26722 
  26723     const container_ty = ptr_ty.childType(mod);
  26724     if (container_ty.zigTypeTag(mod) == .Struct) {
  26725         if (container_ty.structFieldIsComptime(field_index, mod)) {
  26726             try sema.resolveStructFieldInits(container_ty);
  26727             const default_val = (try container_ty.structFieldValueComptime(mod, field_index)).?;
  26728             return .{ .direct = Air.internedToRef(default_val.toIntern()) };
  26729         }
  26730     }
  26731 
  26732     if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| {
  26733         const pointer = Air.internedToRef((try mod.intern(.{ .ptr = .{
  26734             .ty = ptr_field_ty.toIntern(),
  26735             .addr = .{ .field = .{
  26736                 .base = struct_ptr_val.toIntern(),
  26737                 .index = field_index,
  26738             } },
  26739         } })));
  26740         return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) };
  26741     }
  26742 
  26743     try sema.requireRuntimeBlock(block, src, null);
  26744     const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty);
  26745     return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) };
  26746 }
  26747 
  26748 fn namespaceLookup(
  26749     sema: *Sema,
  26750     block: *Block,
  26751     src: LazySrcLoc,
  26752     namespace: InternPool.NamespaceIndex,
  26753     decl_name: InternPool.NullTerminatedString,
  26754 ) CompileError!?InternPool.DeclIndex {
  26755     const mod = sema.mod;
  26756     const gpa = sema.gpa;
  26757     if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
  26758         const decl = mod.declPtr(decl_index);
  26759         if (!decl.is_pub and decl.getFileScope(mod) != block.getFileScope(mod)) {
  26760             const msg = msg: {
  26761                 const msg = try sema.errMsg(block, src, "'{}' is not marked 'pub'", .{
  26762                     decl_name.fmt(&mod.intern_pool),
  26763                 });
  26764                 errdefer msg.destroy(gpa);
  26765                 try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "declared here", .{});
  26766                 break :msg msg;
  26767             };
  26768             return sema.failWithOwnedErrorMsg(block, msg);
  26769         }
  26770         return decl_index;
  26771     }
  26772     return null;
  26773 }
  26774 
  26775 fn namespaceLookupRef(
  26776     sema: *Sema,
  26777     block: *Block,
  26778     src: LazySrcLoc,
  26779     namespace: InternPool.NamespaceIndex,
  26780     decl_name: InternPool.NullTerminatedString,
  26781 ) CompileError!?Air.Inst.Ref {
  26782     const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
  26783     try sema.addReferencedBy(block, src, decl);
  26784     return try sema.analyzeDeclRef(decl);
  26785 }
  26786 
  26787 fn namespaceLookupVal(
  26788     sema: *Sema,
  26789     block: *Block,
  26790     src: LazySrcLoc,
  26791     namespace: InternPool.NamespaceIndex,
  26792     decl_name: InternPool.NullTerminatedString,
  26793 ) CompileError!?Air.Inst.Ref {
  26794     const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
  26795     return try sema.analyzeDeclVal(block, src, decl);
  26796 }
  26797 
  26798 fn structFieldPtr(
  26799     sema: *Sema,
  26800     block: *Block,
  26801     src: LazySrcLoc,
  26802     struct_ptr: Air.Inst.Ref,
  26803     field_name: InternPool.NullTerminatedString,
  26804     field_name_src: LazySrcLoc,
  26805     struct_ty: Type,
  26806     initializing: bool,
  26807 ) CompileError!Air.Inst.Ref {
  26808     const mod = sema.mod;
  26809     const ip = &mod.intern_pool;
  26810     assert(struct_ty.zigTypeTag(mod) == .Struct);
  26811 
  26812     try sema.resolveTypeFields(struct_ty);
  26813     try sema.resolveStructLayout(struct_ty);
  26814 
  26815     if (struct_ty.isTuple(mod)) {
  26816         if (ip.stringEqlSlice(field_name, "len")) {
  26817             const len_inst = try mod.intRef(Type.usize, struct_ty.structFieldCount(mod));
  26818             return sema.analyzeRef(block, src, len_inst);
  26819         }
  26820         const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
  26821         return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing);
  26822     } else if (struct_ty.isAnonStruct(mod)) {
  26823         const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
  26824         return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing);
  26825     }
  26826 
  26827     const struct_type = mod.typeToStruct(struct_ty).?;
  26828 
  26829     const field_index = struct_type.nameIndex(ip, field_name) orelse
  26830         return sema.failWithBadStructFieldAccess(block, struct_type, field_name_src, field_name);
  26831 
  26832     return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, field_name_src, struct_ty, initializing);
  26833 }
  26834 
  26835 fn structFieldPtrByIndex(
  26836     sema: *Sema,
  26837     block: *Block,
  26838     src: LazySrcLoc,
  26839     struct_ptr: Air.Inst.Ref,
  26840     field_index: u32,
  26841     field_src: LazySrcLoc,
  26842     struct_ty: Type,
  26843     initializing: bool,
  26844 ) CompileError!Air.Inst.Ref {
  26845     const mod = sema.mod;
  26846     const ip = &mod.intern_pool;
  26847     if (struct_ty.isAnonStruct(mod)) {
  26848         return sema.tupleFieldPtr(block, src, struct_ptr, field_src, field_index, initializing);
  26849     }
  26850 
  26851     const struct_type = mod.typeToStruct(struct_ty).?;
  26852     const field_ty = struct_type.field_types.get(ip)[field_index];
  26853     const struct_ptr_ty = sema.typeOf(struct_ptr);
  26854     const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod);
  26855 
  26856     var ptr_ty_data: InternPool.Key.PtrType = .{
  26857         .child = field_ty,
  26858         .flags = .{
  26859             .is_const = struct_ptr_ty_info.flags.is_const,
  26860             .is_volatile = struct_ptr_ty_info.flags.is_volatile,
  26861             .address_space = struct_ptr_ty_info.flags.address_space,
  26862         },
  26863     };
  26864 
  26865     const target = mod.getTarget();
  26866 
  26867     const parent_align = if (struct_ptr_ty_info.flags.alignment != .none)
  26868         struct_ptr_ty_info.flags.alignment
  26869     else
  26870         try sema.typeAbiAlignment(Type.fromInterned(struct_ptr_ty_info.child));
  26871 
  26872     if (struct_type.layout == .Packed) {
  26873         comptime assert(Type.packed_struct_layout_version == 2);
  26874 
  26875         var running_bits: u16 = 0;
  26876         for (0..struct_type.field_types.len) |i| {
  26877             const f_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  26878             if (!(try sema.typeHasRuntimeBits(f_ty))) continue;
  26879 
  26880             if (i == field_index) {
  26881                 ptr_ty_data.packed_offset.bit_offset = running_bits;
  26882             }
  26883             running_bits += @intCast(f_ty.bitSize(mod));
  26884         }
  26885         ptr_ty_data.packed_offset.host_size = (running_bits + 7) / 8;
  26886 
  26887         // If this is a packed struct embedded in another one, we need to offset
  26888         // the bits against each other.
  26889         if (struct_ptr_ty_info.packed_offset.host_size != 0) {
  26890             ptr_ty_data.packed_offset.host_size = struct_ptr_ty_info.packed_offset.host_size;
  26891             ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset;
  26892         }
  26893 
  26894         ptr_ty_data.flags.alignment = parent_align;
  26895 
  26896         // If the field happens to be byte-aligned, simplify the pointer type.
  26897         // The pointee type bit size must match its ABI byte size so that loads and stores
  26898         // do not interfere with the surrounding packed bits.
  26899         // We do not attempt this with big-endian targets yet because of nested
  26900         // structs and floats. I need to double-check the desired behavior for big endian
  26901         // targets before adding the necessary complications to this code. This will not
  26902         // cause miscompilations; it only means the field pointer uses bit masking when it
  26903         // might not be strictly necessary.
  26904         if (parent_align != .none and ptr_ty_data.packed_offset.bit_offset % 8 == 0 and
  26905             target.cpu.arch.endian() == .little)
  26906         {
  26907             const elem_size_bytes = try sema.typeAbiSize(Type.fromInterned(ptr_ty_data.child));
  26908             const elem_size_bits = Type.fromInterned(ptr_ty_data.child).bitSize(mod);
  26909             if (elem_size_bytes * 8 == elem_size_bits) {
  26910                 const byte_offset = ptr_ty_data.packed_offset.bit_offset / 8;
  26911                 const new_align: Alignment = @enumFromInt(@ctz(byte_offset | parent_align.toByteUnitsOptional().?));
  26912                 assert(new_align != .none);
  26913                 ptr_ty_data.flags.alignment = new_align;
  26914                 ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 };
  26915             }
  26916         }
  26917     } else if (struct_type.layout == .Extern) {
  26918         // For extern structs, field alignment might be bigger than type's
  26919         // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the
  26920         // second field is aligned as u32.
  26921         const field_offset = struct_ty.structFieldOffset(field_index, mod);
  26922         ptr_ty_data.flags.alignment = if (parent_align == .none)
  26923             .none
  26924         else
  26925             @enumFromInt(@min(@intFromEnum(parent_align), @ctz(field_offset)));
  26926     } else {
  26927         // Our alignment is capped at the field alignment.
  26928         const field_align = try sema.structFieldAlignment(
  26929             struct_type.fieldAlign(ip, field_index),
  26930             Type.fromInterned(field_ty),
  26931             struct_type.layout,
  26932         );
  26933         ptr_ty_data.flags.alignment = if (struct_ptr_ty_info.flags.alignment == .none)
  26934             field_align
  26935         else
  26936             field_align.min(parent_align);
  26937     }
  26938 
  26939     const ptr_field_ty = try sema.ptrType(ptr_ty_data);
  26940 
  26941     if (struct_type.fieldIsComptime(ip, field_index)) {
  26942         try sema.resolveStructFieldInits(struct_ty);
  26943         const val = try mod.intern(.{ .ptr = .{
  26944             .ty = ptr_field_ty.toIntern(),
  26945             .addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] },
  26946         } });
  26947         return Air.internedToRef(val);
  26948     }
  26949 
  26950     if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
  26951         const val = try mod.intern(.{ .ptr = .{
  26952             .ty = ptr_field_ty.toIntern(),
  26953             .addr = .{ .field = .{
  26954                 .base = try struct_ptr_val.intern(struct_ptr_ty, mod),
  26955                 .index = field_index,
  26956             } },
  26957         } });
  26958         return Air.internedToRef(val);
  26959     }
  26960 
  26961     try sema.requireRuntimeBlock(block, src, null);
  26962     return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty);
  26963 }
  26964 
  26965 fn structFieldVal(
  26966     sema: *Sema,
  26967     block: *Block,
  26968     src: LazySrcLoc,
  26969     struct_byval: Air.Inst.Ref,
  26970     field_name: InternPool.NullTerminatedString,
  26971     field_name_src: LazySrcLoc,
  26972     struct_ty: Type,
  26973 ) CompileError!Air.Inst.Ref {
  26974     const mod = sema.mod;
  26975     const ip = &mod.intern_pool;
  26976     assert(struct_ty.zigTypeTag(mod) == .Struct);
  26977 
  26978     try sema.resolveTypeFields(struct_ty);
  26979 
  26980     switch (ip.indexToKey(struct_ty.toIntern())) {
  26981         .struct_type => |struct_type| {
  26982             if (struct_type.isTuple(ip))
  26983                 return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty);
  26984 
  26985             const field_index = struct_type.nameIndex(ip, field_name) orelse
  26986                 return sema.failWithBadStructFieldAccess(block, struct_type, field_name_src, field_name);
  26987             if (struct_type.fieldIsComptime(ip, field_index)) {
  26988                 try sema.resolveStructFieldInits(struct_ty);
  26989                 return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]);
  26990             }
  26991 
  26992             const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]);
  26993 
  26994             if (try sema.resolveValue(struct_byval)) |struct_val| {
  26995                 if (struct_val.isUndef(mod)) return mod.undefRef(field_ty);
  26996                 if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| {
  26997                     return Air.internedToRef(opv.toIntern());
  26998                 }
  26999                 return Air.internedToRef((try struct_val.fieldValue(mod, field_index)).toIntern());
  27000             }
  27001 
  27002             try sema.requireRuntimeBlock(block, src, null);
  27003             try sema.resolveTypeLayout(field_ty);
  27004             return block.addStructFieldVal(struct_byval, field_index, field_ty);
  27005         },
  27006         .anon_struct_type => |anon_struct| {
  27007             if (anon_struct.names.len == 0) {
  27008                 return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty);
  27009             } else {
  27010                 const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
  27011                 return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty);
  27012             }
  27013         },
  27014         else => unreachable,
  27015     }
  27016 }
  27017 
  27018 fn tupleFieldVal(
  27019     sema: *Sema,
  27020     block: *Block,
  27021     src: LazySrcLoc,
  27022     tuple_byval: Air.Inst.Ref,
  27023     field_name: InternPool.NullTerminatedString,
  27024     field_name_src: LazySrcLoc,
  27025     tuple_ty: Type,
  27026 ) CompileError!Air.Inst.Ref {
  27027     const mod = sema.mod;
  27028     if (mod.intern_pool.stringEqlSlice(field_name, "len")) {
  27029         return mod.intRef(Type.usize, tuple_ty.structFieldCount(mod));
  27030     }
  27031     const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src);
  27032     return sema.tupleFieldValByIndex(block, src, tuple_byval, field_index, tuple_ty);
  27033 }
  27034 
  27035 /// Asserts that `field_name` is not "len".
  27036 fn tupleFieldIndex(
  27037     sema: *Sema,
  27038     block: *Block,
  27039     tuple_ty: Type,
  27040     field_name: InternPool.NullTerminatedString,
  27041     field_name_src: LazySrcLoc,
  27042 ) CompileError!u32 {
  27043     const mod = sema.mod;
  27044     assert(!mod.intern_pool.stringEqlSlice(field_name, "len"));
  27045     if (field_name.toUnsigned(&mod.intern_pool)) |field_index| {
  27046         if (field_index < tuple_ty.structFieldCount(mod)) return field_index;
  27047         return sema.fail(block, field_name_src, "index '{}' out of bounds of tuple '{}'", .{
  27048             field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod),
  27049         });
  27050     }
  27051 
  27052     return sema.fail(block, field_name_src, "no field named '{}' in tuple '{}'", .{
  27053         field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod),
  27054     });
  27055 }
  27056 
  27057 fn tupleFieldValByIndex(
  27058     sema: *Sema,
  27059     block: *Block,
  27060     src: LazySrcLoc,
  27061     tuple_byval: Air.Inst.Ref,
  27062     field_index: u32,
  27063     tuple_ty: Type,
  27064 ) CompileError!Air.Inst.Ref {
  27065     const mod = sema.mod;
  27066     const field_ty = tuple_ty.structFieldType(field_index, mod);
  27067 
  27068     if (tuple_ty.structFieldIsComptime(field_index, mod))
  27069         try sema.resolveStructFieldInits(tuple_ty);
  27070     if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| {
  27071         return Air.internedToRef(default_value.toIntern());
  27072     }
  27073 
  27074     if (try sema.resolveValue(tuple_byval)) |tuple_val| {
  27075         if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| {
  27076             return Air.internedToRef(opv.toIntern());
  27077         }
  27078         return switch (mod.intern_pool.indexToKey(tuple_val.toIntern())) {
  27079             .undef => mod.undefRef(field_ty),
  27080             .aggregate => |aggregate| Air.internedToRef(switch (aggregate.storage) {
  27081                 .bytes => |bytes| try mod.intValue(Type.u8, bytes[0]),
  27082                 .elems => |elems| Value.fromInterned(elems[field_index]),
  27083                 .repeated_elem => |elem| Value.fromInterned(elem),
  27084             }.toIntern()),
  27085             else => unreachable,
  27086         };
  27087     }
  27088 
  27089     try sema.requireRuntimeBlock(block, src, null);
  27090     try sema.resolveTypeLayout(field_ty);
  27091     return block.addStructFieldVal(tuple_byval, field_index, field_ty);
  27092 }
  27093 
  27094 fn unionFieldPtr(
  27095     sema: *Sema,
  27096     block: *Block,
  27097     src: LazySrcLoc,
  27098     union_ptr: Air.Inst.Ref,
  27099     field_name: InternPool.NullTerminatedString,
  27100     field_name_src: LazySrcLoc,
  27101     union_ty: Type,
  27102     initializing: bool,
  27103 ) CompileError!Air.Inst.Ref {
  27104     const mod = sema.mod;
  27105     const ip = &mod.intern_pool;
  27106 
  27107     assert(union_ty.zigTypeTag(mod) == .Union);
  27108 
  27109     const union_ptr_ty = sema.typeOf(union_ptr);
  27110     const union_ptr_info = union_ptr_ty.ptrInfo(mod);
  27111     try sema.resolveTypeFields(union_ty);
  27112     const union_obj = mod.typeToUnion(union_ty).?;
  27113     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
  27114     const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
  27115     const ptr_field_ty = try sema.ptrType(.{
  27116         .child = field_ty.toIntern(),
  27117         .flags = .{
  27118             .is_const = union_ptr_info.flags.is_const,
  27119             .is_volatile = union_ptr_info.flags.is_volatile,
  27120             .address_space = union_ptr_info.flags.address_space,
  27121             .alignment = if (union_obj.getLayout(ip) == .Auto) blk: {
  27122                 const union_align = if (union_ptr_info.flags.alignment != .none)
  27123                     union_ptr_info.flags.alignment
  27124                 else
  27125                     try sema.typeAbiAlignment(union_ty);
  27126                 const field_align = try sema.unionFieldAlignment(union_obj, field_index);
  27127                 break :blk union_align.min(field_align);
  27128             } else union_ptr_info.flags.alignment,
  27129         },
  27130         .packed_offset = union_ptr_info.packed_offset,
  27131     });
  27132     const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, mod).?);
  27133 
  27134     if (initializing and field_ty.zigTypeTag(mod) == .NoReturn) {
  27135         const msg = msg: {
  27136             const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{});
  27137             errdefer msg.destroy(sema.gpa);
  27138 
  27139             try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{
  27140                 field_name.fmt(ip),
  27141             });
  27142             try sema.addDeclaredHereNote(msg, union_ty);
  27143             break :msg msg;
  27144         };
  27145         return sema.failWithOwnedErrorMsg(block, msg);
  27146     }
  27147 
  27148     if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
  27149         switch (union_obj.getLayout(ip)) {
  27150             .Auto => if (!initializing) {
  27151                 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse
  27152                     break :ct;
  27153                 if (union_val.isUndef(mod)) {
  27154                     return sema.failWithUseOfUndef(block, src);
  27155                 }
  27156                 const un = ip.indexToKey(union_val.toIntern()).un;
  27157                 const field_tag = try mod.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27158                 const tag_matches = un.tag == field_tag.toIntern();
  27159                 if (!tag_matches) {
  27160                     const msg = msg: {
  27161                         const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), mod).?;
  27162                         const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, mod);
  27163                         const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{
  27164                             field_name.fmt(ip),
  27165                             active_field_name.fmt(ip),
  27166                         });
  27167                         errdefer msg.destroy(sema.gpa);
  27168                         try sema.addDeclaredHereNote(msg, union_ty);
  27169                         break :msg msg;
  27170                     };
  27171                     return sema.failWithOwnedErrorMsg(block, msg);
  27172                 }
  27173             },
  27174             .Packed, .Extern => {},
  27175         }
  27176         return Air.internedToRef((try mod.intern(.{ .ptr = .{
  27177             .ty = ptr_field_ty.toIntern(),
  27178             .addr = .{ .field = .{
  27179                 .base = union_ptr_val.toIntern(),
  27180                 .index = field_index,
  27181             } },
  27182         } })));
  27183     }
  27184 
  27185     try sema.requireRuntimeBlock(block, src, null);
  27186     if (!initializing and union_obj.getLayout(ip) == .Auto and block.wantSafety() and
  27187         union_ty.unionTagTypeSafety(mod) != null and union_obj.field_names.len > 1)
  27188     {
  27189         const wanted_tag_val = try mod.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27190         const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern());
  27191         // TODO would it be better if get_union_tag supported pointers to unions?
  27192         const union_val = try block.addTyOp(.load, union_ty, union_ptr);
  27193         const active_tag = try block.addTyOp(.get_union_tag, Type.fromInterned(union_obj.enum_tag_ty), union_val);
  27194         try sema.panicInactiveUnionField(block, src, active_tag, wanted_tag);
  27195     }
  27196     if (field_ty.zigTypeTag(mod) == .NoReturn) {
  27197         _ = try block.addNoOp(.unreach);
  27198         return .unreachable_value;
  27199     }
  27200     return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);
  27201 }
  27202 
  27203 fn unionFieldVal(
  27204     sema: *Sema,
  27205     block: *Block,
  27206     src: LazySrcLoc,
  27207     union_byval: Air.Inst.Ref,
  27208     field_name: InternPool.NullTerminatedString,
  27209     field_name_src: LazySrcLoc,
  27210     union_ty: Type,
  27211 ) CompileError!Air.Inst.Ref {
  27212     const mod = sema.mod;
  27213     const ip = &mod.intern_pool;
  27214     assert(union_ty.zigTypeTag(mod) == .Union);
  27215 
  27216     try sema.resolveTypeFields(union_ty);
  27217     const union_obj = mod.typeToUnion(union_ty).?;
  27218     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
  27219     const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
  27220     const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, mod).?);
  27221 
  27222     if (try sema.resolveValue(union_byval)) |union_val| {
  27223         if (union_val.isUndef(mod)) return mod.undefRef(field_ty);
  27224 
  27225         const un = ip.indexToKey(union_val.toIntern()).un;
  27226         const field_tag = try mod.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27227         const tag_matches = un.tag == field_tag.toIntern();
  27228         switch (union_obj.getLayout(ip)) {
  27229             .Auto => {
  27230                 if (tag_matches) {
  27231                     return Air.internedToRef(un.val);
  27232                 } else {
  27233                     const msg = msg: {
  27234                         const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), mod).?;
  27235                         const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, mod);
  27236                         const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{
  27237                             field_name.fmt(ip), active_field_name.fmt(ip),
  27238                         });
  27239                         errdefer msg.destroy(sema.gpa);
  27240                         try sema.addDeclaredHereNote(msg, union_ty);
  27241                         break :msg msg;
  27242                     };
  27243                     return sema.failWithOwnedErrorMsg(block, msg);
  27244                 }
  27245             },
  27246             .Packed, .Extern => |layout| {
  27247                 if (tag_matches) {
  27248                     return Air.internedToRef(un.val);
  27249                 } else {
  27250                     const old_ty = if (un.tag == .none)
  27251                         Type.fromInterned(ip.typeOf(un.val))
  27252                     else
  27253                         union_ty.unionFieldType(Value.fromInterned(un.tag), mod).?;
  27254 
  27255                     if (try sema.bitCastUnionFieldVal(block, src, Value.fromInterned(un.val), old_ty, field_ty, layout)) |new_val| {
  27256                         return Air.internedToRef(new_val.toIntern());
  27257                     }
  27258                 }
  27259             },
  27260         }
  27261     }
  27262 
  27263     try sema.requireRuntimeBlock(block, src, null);
  27264     if (union_obj.getLayout(ip) == .Auto and block.wantSafety() and
  27265         union_ty.unionTagTypeSafety(mod) != null and union_obj.field_names.len > 1)
  27266     {
  27267         const wanted_tag_val = try mod.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27268         const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern());
  27269         const active_tag = try block.addTyOp(.get_union_tag, Type.fromInterned(union_obj.enum_tag_ty), union_byval);
  27270         try sema.panicInactiveUnionField(block, src, active_tag, wanted_tag);
  27271     }
  27272     if (field_ty.zigTypeTag(mod) == .NoReturn) {
  27273         _ = try block.addNoOp(.unreach);
  27274         return .unreachable_value;
  27275     }
  27276     try sema.resolveTypeLayout(field_ty);
  27277     return block.addStructFieldVal(union_byval, field_index, field_ty);
  27278 }
  27279 
  27280 fn elemPtr(
  27281     sema: *Sema,
  27282     block: *Block,
  27283     src: LazySrcLoc,
  27284     indexable_ptr: Air.Inst.Ref,
  27285     elem_index: Air.Inst.Ref,
  27286     elem_index_src: LazySrcLoc,
  27287     init: bool,
  27288     oob_safety: bool,
  27289 ) CompileError!Air.Inst.Ref {
  27290     const mod = sema.mod;
  27291     const indexable_ptr_src = src; // TODO better source location
  27292     const indexable_ptr_ty = sema.typeOf(indexable_ptr);
  27293 
  27294     const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(mod)) {
  27295         .Pointer => indexable_ptr_ty.childType(mod),
  27296         else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(mod)}),
  27297     };
  27298     try checkIndexable(sema, block, src, indexable_ty);
  27299 
  27300     const elem_ptr = switch (indexable_ty.zigTypeTag(mod)) {
  27301         .Array, .Vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
  27302         .Struct => blk: {
  27303             // Tuple field access.
  27304             const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{
  27305                 .needed_comptime_reason = "tuple field access index must be comptime-known",
  27306             });
  27307             const index: u32 = @intCast(index_val.toUnsignedInt(mod));
  27308             break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
  27309         },
  27310         else => {
  27311             const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
  27312             return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety);
  27313         },
  27314     };
  27315 
  27316     try sema.checkKnownAllocPtr(indexable_ptr, elem_ptr);
  27317     return elem_ptr;
  27318 }
  27319 
  27320 /// Asserts that the type of indexable is pointer.
  27321 fn elemPtrOneLayerOnly(
  27322     sema: *Sema,
  27323     block: *Block,
  27324     src: LazySrcLoc,
  27325     indexable: Air.Inst.Ref,
  27326     elem_index: Air.Inst.Ref,
  27327     elem_index_src: LazySrcLoc,
  27328     init: bool,
  27329     oob_safety: bool,
  27330 ) CompileError!Air.Inst.Ref {
  27331     const indexable_src = src; // TODO better source location
  27332     const indexable_ty = sema.typeOf(indexable);
  27333     const mod = sema.mod;
  27334 
  27335     try checkIndexable(sema, block, src, indexable_ty);
  27336 
  27337     switch (indexable_ty.ptrSize(mod)) {
  27338         .Slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  27339         .Many, .C => {
  27340             const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
  27341             const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  27342             const runtime_src = rs: {
  27343                 const ptr_val = maybe_ptr_val orelse break :rs indexable_src;
  27344                 const index_val = maybe_index_val orelse break :rs elem_index_src;
  27345                 const index: usize = @intCast(index_val.toUnsignedInt(mod));
  27346                 const result_ty = try sema.elemPtrType(indexable_ty, index);
  27347                 const elem_ptr = try ptr_val.elemPtr(result_ty, index, mod);
  27348                 return Air.internedToRef(elem_ptr.toIntern());
  27349             };
  27350             const result_ty = try sema.elemPtrType(indexable_ty, null);
  27351 
  27352             try sema.requireRuntimeBlock(block, src, runtime_src);
  27353             return block.addPtrElemPtr(indexable, elem_index, result_ty);
  27354         },
  27355         .One => {
  27356             const child_ty = indexable_ty.childType(mod);
  27357             const elem_ptr = switch (child_ty.zigTypeTag(mod)) {
  27358                 .Array, .Vector => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety),
  27359                 .Struct => blk: {
  27360                     assert(child_ty.isTuple(mod));
  27361                     const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{
  27362                         .needed_comptime_reason = "tuple field access index must be comptime-known",
  27363                     });
  27364                     const index: u32 = @intCast(index_val.toUnsignedInt(mod));
  27365                     break :blk try sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false);
  27366                 },
  27367                 else => unreachable, // Guaranteed by checkIndexable
  27368             };
  27369             try sema.checkKnownAllocPtr(indexable, elem_ptr);
  27370             return elem_ptr;
  27371         },
  27372     }
  27373 }
  27374 
  27375 fn elemVal(
  27376     sema: *Sema,
  27377     block: *Block,
  27378     src: LazySrcLoc,
  27379     indexable: Air.Inst.Ref,
  27380     elem_index_uncasted: Air.Inst.Ref,
  27381     elem_index_src: LazySrcLoc,
  27382     oob_safety: bool,
  27383 ) CompileError!Air.Inst.Ref {
  27384     const indexable_src = src; // TODO better source location
  27385     const indexable_ty = sema.typeOf(indexable);
  27386     const mod = sema.mod;
  27387 
  27388     try checkIndexable(sema, block, src, indexable_ty);
  27389 
  27390     // TODO in case of a vector of pointers, we need to detect whether the element
  27391     // index is a scalar or vector instead of unconditionally casting to usize.
  27392     const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src);
  27393 
  27394     switch (indexable_ty.zigTypeTag(mod)) {
  27395         .Pointer => switch (indexable_ty.ptrSize(mod)) {
  27396             .Slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  27397             .Many, .C => {
  27398                 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
  27399                 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  27400 
  27401                 const runtime_src = rs: {
  27402                     const indexable_val = maybe_indexable_val orelse break :rs indexable_src;
  27403                     const index_val = maybe_index_val orelse break :rs elem_index_src;
  27404                     const index: usize = @intCast(index_val.toUnsignedInt(mod));
  27405                     const elem_ty = indexable_ty.elemType2(mod);
  27406                     const many_ptr_ty = try mod.manyConstPtrType(elem_ty);
  27407                     const many_ptr_val = try mod.getCoerced(indexable_val, many_ptr_ty);
  27408                     const elem_ptr_ty = try mod.singleConstPtrType(elem_ty);
  27409                     const elem_ptr_val = try many_ptr_val.elemPtr(elem_ptr_ty, index, mod);
  27410                     if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty)) |elem_val| {
  27411                         return Air.internedToRef((try mod.getCoerced(elem_val, elem_ty)).toIntern());
  27412                     }
  27413                     break :rs indexable_src;
  27414                 };
  27415 
  27416                 try sema.requireRuntimeBlock(block, src, runtime_src);
  27417                 return block.addBinOp(.ptr_elem_val, indexable, elem_index);
  27418             },
  27419             .One => {
  27420                 arr_sent: {
  27421                     const inner_ty = indexable_ty.childType(mod);
  27422                     if (inner_ty.zigTypeTag(mod) != .Array) break :arr_sent;
  27423                     const sentinel = inner_ty.sentinel(mod) orelse break :arr_sent;
  27424                     const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent;
  27425                     const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(mod));
  27426                     if (index != inner_ty.arrayLen(mod)) break :arr_sent;
  27427                     return Air.internedToRef(sentinel.toIntern());
  27428                 }
  27429                 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);
  27430                 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
  27431             },
  27432         },
  27433         .Array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  27434         .Vector => {
  27435             // TODO: If the index is a vector, the result should be a vector.
  27436             return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety);
  27437         },
  27438         .Struct => {
  27439             // Tuple field access.
  27440             const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{
  27441                 .needed_comptime_reason = "tuple field access index must be comptime-known",
  27442             });
  27443             const index: u32 = @intCast(index_val.toUnsignedInt(mod));
  27444             return sema.tupleField(block, indexable_src, indexable, elem_index_src, index);
  27445         },
  27446         else => unreachable,
  27447     }
  27448 }
  27449 
  27450 fn validateRuntimeElemAccess(
  27451     sema: *Sema,
  27452     block: *Block,
  27453     elem_index_src: LazySrcLoc,
  27454     elem_ty: Type,
  27455     parent_ty: Type,
  27456     parent_src: LazySrcLoc,
  27457 ) CompileError!void {
  27458     const mod = sema.mod;
  27459     if (try sema.typeRequiresComptime(elem_ty)) {
  27460         const msg = msg: {
  27461             const msg = try sema.errMsg(
  27462                 block,
  27463                 elem_index_src,
  27464                 "values of type '{}' must be comptime-known, but index value is runtime-known",
  27465                 .{parent_ty.fmt(mod)},
  27466             );
  27467             errdefer msg.destroy(sema.gpa);
  27468 
  27469             const src_decl = mod.declPtr(block.src_decl);
  27470             try sema.explainWhyTypeIsComptime(msg, parent_src.toSrcLoc(src_decl, mod), parent_ty);
  27471 
  27472             break :msg msg;
  27473         };
  27474         return sema.failWithOwnedErrorMsg(block, msg);
  27475     }
  27476 }
  27477 
  27478 fn tupleFieldPtr(
  27479     sema: *Sema,
  27480     block: *Block,
  27481     tuple_ptr_src: LazySrcLoc,
  27482     tuple_ptr: Air.Inst.Ref,
  27483     field_index_src: LazySrcLoc,
  27484     field_index: u32,
  27485     init: bool,
  27486 ) CompileError!Air.Inst.Ref {
  27487     const mod = sema.mod;
  27488     const tuple_ptr_ty = sema.typeOf(tuple_ptr);
  27489     const tuple_ty = tuple_ptr_ty.childType(mod);
  27490     try sema.resolveTypeFields(tuple_ty);
  27491     const field_count = tuple_ty.structFieldCount(mod);
  27492 
  27493     if (field_count == 0) {
  27494         return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});
  27495     }
  27496 
  27497     if (field_index >= field_count) {
  27498         return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
  27499             field_index, field_count,
  27500         });
  27501     }
  27502 
  27503     const field_ty = tuple_ty.structFieldType(field_index, mod);
  27504     const ptr_field_ty = try sema.ptrType(.{
  27505         .child = field_ty.toIntern(),
  27506         .flags = .{
  27507             .is_const = !tuple_ptr_ty.ptrIsMutable(mod),
  27508             .is_volatile = tuple_ptr_ty.isVolatilePtr(mod),
  27509             .address_space = tuple_ptr_ty.ptrAddressSpace(mod),
  27510         },
  27511     });
  27512 
  27513     if (tuple_ty.structFieldIsComptime(field_index, mod))
  27514         try sema.resolveStructFieldInits(tuple_ty);
  27515     if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| {
  27516         return Air.internedToRef((try mod.intern(.{ .ptr = .{
  27517             .ty = ptr_field_ty.toIntern(),
  27518             .addr = .{ .comptime_field = default_val.toIntern() },
  27519         } })));
  27520     }
  27521 
  27522     if (try sema.resolveValue(tuple_ptr)) |tuple_ptr_val| {
  27523         return Air.internedToRef((try mod.intern(.{ .ptr = .{
  27524             .ty = ptr_field_ty.toIntern(),
  27525             .addr = .{ .field = .{
  27526                 .base = tuple_ptr_val.toIntern(),
  27527                 .index = field_index,
  27528             } },
  27529         } })));
  27530     }
  27531 
  27532     if (!init) {
  27533         try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src);
  27534     }
  27535 
  27536     try sema.requireRuntimeBlock(block, tuple_ptr_src, null);
  27537     return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);
  27538 }
  27539 
  27540 fn tupleField(
  27541     sema: *Sema,
  27542     block: *Block,
  27543     tuple_src: LazySrcLoc,
  27544     tuple: Air.Inst.Ref,
  27545     field_index_src: LazySrcLoc,
  27546     field_index: u32,
  27547 ) CompileError!Air.Inst.Ref {
  27548     const mod = sema.mod;
  27549     const tuple_ty = sema.typeOf(tuple);
  27550     try sema.resolveTypeFields(tuple_ty);
  27551     const field_count = tuple_ty.structFieldCount(mod);
  27552 
  27553     if (field_count == 0) {
  27554         return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{});
  27555     }
  27556 
  27557     if (field_index >= field_count) {
  27558         return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
  27559             field_index, field_count,
  27560         });
  27561     }
  27562 
  27563     const field_ty = tuple_ty.structFieldType(field_index, mod);
  27564 
  27565     if (tuple_ty.structFieldIsComptime(field_index, mod))
  27566         try sema.resolveStructFieldInits(tuple_ty);
  27567     if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| {
  27568         return Air.internedToRef(default_value.toIntern()); // comptime field
  27569     }
  27570 
  27571     if (try sema.resolveValue(tuple)) |tuple_val| {
  27572         if (tuple_val.isUndef(mod)) return mod.undefRef(field_ty);
  27573         return Air.internedToRef((try tuple_val.fieldValue(mod, field_index)).toIntern());
  27574     }
  27575 
  27576     try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src);
  27577 
  27578     try sema.requireRuntimeBlock(block, tuple_src, null);
  27579     try sema.resolveTypeLayout(field_ty);
  27580     return block.addStructFieldVal(tuple, field_index, field_ty);
  27581 }
  27582 
  27583 fn elemValArray(
  27584     sema: *Sema,
  27585     block: *Block,
  27586     src: LazySrcLoc,
  27587     array_src: LazySrcLoc,
  27588     array: Air.Inst.Ref,
  27589     elem_index_src: LazySrcLoc,
  27590     elem_index: Air.Inst.Ref,
  27591     oob_safety: bool,
  27592 ) CompileError!Air.Inst.Ref {
  27593     const mod = sema.mod;
  27594     const array_ty = sema.typeOf(array);
  27595     const array_sent = array_ty.sentinel(mod);
  27596     const array_len = array_ty.arrayLen(mod);
  27597     const array_len_s = array_len + @intFromBool(array_sent != null);
  27598     const elem_ty = array_ty.childType(mod);
  27599 
  27600     if (array_len_s == 0) {
  27601         return sema.fail(block, array_src, "indexing into empty array is not allowed", .{});
  27602     }
  27603 
  27604     const maybe_undef_array_val = try sema.resolveValue(array);
  27605     // index must be defined since it can access out of bounds
  27606     const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  27607 
  27608     if (maybe_index_val) |index_val| {
  27609         const index: usize = @intCast(index_val.toUnsignedInt(mod));
  27610         if (array_sent) |s| {
  27611             if (index == array_len) {
  27612                 return Air.internedToRef(s.toIntern());
  27613             }
  27614         }
  27615         if (index >= array_len_s) {
  27616             const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else "";
  27617             return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
  27618         }
  27619     }
  27620     if (maybe_undef_array_val) |array_val| {
  27621         if (array_val.isUndef(mod)) {
  27622             return mod.undefRef(elem_ty);
  27623         }
  27624         if (maybe_index_val) |index_val| {
  27625             const index: usize = @intCast(index_val.toUnsignedInt(mod));
  27626             const elem_val = try array_val.elemValue(mod, index);
  27627             return Air.internedToRef(elem_val.toIntern());
  27628         }
  27629     }
  27630 
  27631     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src);
  27632 
  27633     const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src;
  27634     try sema.requireRuntimeBlock(block, src, runtime_src);
  27635     try sema.queueFullTypeResolution(array_ty);
  27636     if (oob_safety and block.wantSafety()) {
  27637         // Runtime check is only needed if unable to comptime check
  27638         if (maybe_index_val == null) {
  27639             const len_inst = try mod.intRef(Type.usize, array_len);
  27640             const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
  27641             try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op);
  27642         }
  27643     }
  27644     return block.addBinOp(.array_elem_val, array, elem_index);
  27645 }
  27646 
  27647 fn elemPtrArray(
  27648     sema: *Sema,
  27649     block: *Block,
  27650     src: LazySrcLoc,
  27651     array_ptr_src: LazySrcLoc,
  27652     array_ptr: Air.Inst.Ref,
  27653     elem_index_src: LazySrcLoc,
  27654     elem_index: Air.Inst.Ref,
  27655     init: bool,
  27656     oob_safety: bool,
  27657 ) CompileError!Air.Inst.Ref {
  27658     const mod = sema.mod;
  27659     const array_ptr_ty = sema.typeOf(array_ptr);
  27660     const array_ty = array_ptr_ty.childType(mod);
  27661     const array_sent = array_ty.sentinel(mod) != null;
  27662     const array_len = array_ty.arrayLen(mod);
  27663     const array_len_s = array_len + @intFromBool(array_sent);
  27664 
  27665     if (array_len_s == 0) {
  27666         return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{});
  27667     }
  27668 
  27669     const maybe_undef_array_ptr_val = try sema.resolveValue(array_ptr);
  27670     // The index must not be undefined since it can be out of bounds.
  27671     const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
  27672         const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod));
  27673         if (index >= array_len_s) {
  27674             const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
  27675             return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
  27676         }
  27677         break :o index;
  27678     } else null;
  27679 
  27680     const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset);
  27681 
  27682     if (maybe_undef_array_ptr_val) |array_ptr_val| {
  27683         if (array_ptr_val.isUndef(mod)) {
  27684             return mod.undefRef(elem_ptr_ty);
  27685         }
  27686         if (offset) |index| {
  27687             const elem_ptr = try array_ptr_val.elemPtr(elem_ptr_ty, index, mod);
  27688             return Air.internedToRef(elem_ptr.toIntern());
  27689         }
  27690     }
  27691 
  27692     if (!init) {
  27693         try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(mod), array_ty, array_ptr_src);
  27694     }
  27695 
  27696     const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src;
  27697     try sema.requireRuntimeBlock(block, src, runtime_src);
  27698 
  27699     // Runtime check is only needed if unable to comptime check.
  27700     if (oob_safety and block.wantSafety() and offset == null) {
  27701         const len_inst = try mod.intRef(Type.usize, array_len);
  27702         const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
  27703         try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op);
  27704     }
  27705 
  27706     return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
  27707 }
  27708 
  27709 fn elemValSlice(
  27710     sema: *Sema,
  27711     block: *Block,
  27712     src: LazySrcLoc,
  27713     slice_src: LazySrcLoc,
  27714     slice: Air.Inst.Ref,
  27715     elem_index_src: LazySrcLoc,
  27716     elem_index: Air.Inst.Ref,
  27717     oob_safety: bool,
  27718 ) CompileError!Air.Inst.Ref {
  27719     const mod = sema.mod;
  27720     const slice_ty = sema.typeOf(slice);
  27721     const slice_sent = slice_ty.sentinel(mod) != null;
  27722     const elem_ty = slice_ty.elemType2(mod);
  27723     var runtime_src = slice_src;
  27724 
  27725     // slice must be defined since it can dereferenced as null
  27726     const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice);
  27727     // index must be defined since it can index out of bounds
  27728     const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  27729 
  27730     if (maybe_slice_val) |slice_val| {
  27731         runtime_src = elem_index_src;
  27732         const slice_len = slice_val.sliceLen(mod);
  27733         const slice_len_s = slice_len + @intFromBool(slice_sent);
  27734         if (slice_len_s == 0) {
  27735             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
  27736         }
  27737         if (maybe_index_val) |index_val| {
  27738             const index: usize = @intCast(index_val.toUnsignedInt(mod));
  27739             if (index >= slice_len_s) {
  27740                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
  27741                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
  27742             }
  27743             const elem_ptr_ty = try sema.elemPtrType(slice_ty, index);
  27744             const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod);
  27745             if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| {
  27746                 return Air.internedToRef(elem_val.toIntern());
  27747             }
  27748             runtime_src = slice_src;
  27749         }
  27750     }
  27751 
  27752     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src);
  27753 
  27754     try sema.requireRuntimeBlock(block, src, runtime_src);
  27755     if (oob_safety and block.wantSafety()) {
  27756         const len_inst = if (maybe_slice_val) |slice_val|
  27757             try mod.intRef(Type.usize, slice_val.sliceLen(mod))
  27758         else
  27759             try block.addTyOp(.slice_len, Type.usize, slice);
  27760         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
  27761         try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op);
  27762     }
  27763     try sema.queueFullTypeResolution(sema.typeOf(slice));
  27764     return block.addBinOp(.slice_elem_val, slice, elem_index);
  27765 }
  27766 
  27767 fn elemPtrSlice(
  27768     sema: *Sema,
  27769     block: *Block,
  27770     src: LazySrcLoc,
  27771     slice_src: LazySrcLoc,
  27772     slice: Air.Inst.Ref,
  27773     elem_index_src: LazySrcLoc,
  27774     elem_index: Air.Inst.Ref,
  27775     oob_safety: bool,
  27776 ) CompileError!Air.Inst.Ref {
  27777     const mod = sema.mod;
  27778     const slice_ty = sema.typeOf(slice);
  27779     const slice_sent = slice_ty.sentinel(mod) != null;
  27780 
  27781     const maybe_undef_slice_val = try sema.resolveValue(slice);
  27782     // The index must not be undefined since it can be out of bounds.
  27783     const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
  27784         const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod));
  27785         break :o index;
  27786     } else null;
  27787 
  27788     const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset);
  27789 
  27790     if (maybe_undef_slice_val) |slice_val| {
  27791         if (slice_val.isUndef(mod)) {
  27792             return mod.undefRef(elem_ptr_ty);
  27793         }
  27794         const slice_len = slice_val.sliceLen(mod);
  27795         const slice_len_s = slice_len + @intFromBool(slice_sent);
  27796         if (slice_len_s == 0) {
  27797             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
  27798         }
  27799         if (offset) |index| {
  27800             if (index >= slice_len_s) {
  27801                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
  27802                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
  27803             }
  27804             const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod);
  27805             return Air.internedToRef(elem_ptr_val.toIntern());
  27806         }
  27807     }
  27808 
  27809     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src);
  27810 
  27811     const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src;
  27812     try sema.requireRuntimeBlock(block, src, runtime_src);
  27813     if (oob_safety and block.wantSafety()) {
  27814         const len_inst = len: {
  27815             if (maybe_undef_slice_val) |slice_val|
  27816                 if (!slice_val.isUndef(mod))
  27817                     break :len try mod.intRef(Type.usize, slice_val.sliceLen(mod));
  27818             break :len try block.addTyOp(.slice_len, Type.usize, slice);
  27819         };
  27820         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
  27821         try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op);
  27822     }
  27823     return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
  27824 }
  27825 
  27826 fn coerce(
  27827     sema: *Sema,
  27828     block: *Block,
  27829     dest_ty_unresolved: Type,
  27830     inst: Air.Inst.Ref,
  27831     inst_src: LazySrcLoc,
  27832 ) CompileError!Air.Inst.Ref {
  27833     return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) {
  27834         error.NotCoercible => unreachable,
  27835         else => |e| return e,
  27836     };
  27837 }
  27838 
  27839 const CoersionError = CompileError || error{
  27840     /// When coerce is called recursively, this error should be returned instead of using `fail`
  27841     /// to ensure correct types in compile errors.
  27842     NotCoercible,
  27843 };
  27844 
  27845 const CoerceOpts = struct {
  27846     /// Should coerceExtra emit error messages.
  27847     report_err: bool = true,
  27848     /// Ignored if `report_err == false`.
  27849     is_ret: bool = false,
  27850     /// Should coercion to comptime_int ermit an error message.
  27851     no_cast_to_comptime_int: bool = false,
  27852 
  27853     param_src: struct {
  27854         func_inst: Air.Inst.Ref = .none,
  27855         param_i: u32 = undefined,
  27856 
  27857         fn get(info: @This(), sema: *Sema) !?Module.SrcLoc {
  27858             if (info.func_inst == .none) return null;
  27859             const mod = sema.mod;
  27860             const fn_decl = (try sema.funcDeclSrc(info.func_inst)) orelse return null;
  27861             const param_src = Module.paramSrc(0, mod, fn_decl, info.param_i);
  27862             if (param_src == .node_offset_param) {
  27863                 return Module.SrcLoc{
  27864                     .file_scope = fn_decl.getFileScope(mod),
  27865                     .parent_decl_node = fn_decl.src_node,
  27866                     .lazy = LazySrcLoc.nodeOffset(param_src.node_offset_param),
  27867                 };
  27868             }
  27869             return param_src.toSrcLoc(fn_decl, mod);
  27870         }
  27871     } = .{},
  27872 };
  27873 
  27874 fn coerceExtra(
  27875     sema: *Sema,
  27876     block: *Block,
  27877     dest_ty: Type,
  27878     inst: Air.Inst.Ref,
  27879     inst_src: LazySrcLoc,
  27880     opts: CoerceOpts,
  27881 ) CoersionError!Air.Inst.Ref {
  27882     if (dest_ty.isGenericPoison()) return inst;
  27883     const mod = sema.mod;
  27884     const dest_ty_src = inst_src; // TODO better source location
  27885     try sema.resolveTypeFields(dest_ty);
  27886     const inst_ty = sema.typeOf(inst);
  27887     try sema.resolveTypeFields(inst_ty);
  27888     const target = mod.getTarget();
  27889     // If the types are the same, we can return the operand.
  27890     if (dest_ty.eql(inst_ty, mod))
  27891         return inst;
  27892 
  27893     const maybe_inst_val = try sema.resolveValue(inst);
  27894 
  27895     var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
  27896     if (in_memory_result == .ok) {
  27897         if (maybe_inst_val) |val| {
  27898             return sema.coerceInMemory(val, dest_ty);
  27899         }
  27900         try sema.requireRuntimeBlock(block, inst_src, null);
  27901         try sema.queueFullTypeResolution(dest_ty);
  27902         const new_val = try block.addBitCast(dest_ty, inst);
  27903         try sema.checkKnownAllocPtr(inst, new_val);
  27904         return new_val;
  27905     }
  27906 
  27907     switch (dest_ty.zigTypeTag(mod)) {
  27908         .Optional => optional: {
  27909             if (maybe_inst_val) |val| {
  27910                 // undefined sets the optional bit also to undefined.
  27911                 if (val.toIntern() == .undef) {
  27912                     return mod.undefRef(dest_ty);
  27913                 }
  27914 
  27915                 // null to ?T
  27916                 if (val.toIntern() == .null_value) {
  27917                     return Air.internedToRef((try mod.intern(.{ .opt = .{
  27918                         .ty = dest_ty.toIntern(),
  27919                         .val = .none,
  27920                     } })));
  27921                 }
  27922             }
  27923 
  27924             // cast from ?*T and ?[*]T to ?*anyopaque
  27925             // but don't do it if the source type is a double pointer
  27926             if (dest_ty.isPtrLikeOptional(mod) and
  27927                 dest_ty.elemType2(mod).toIntern() == .anyopaque_type and
  27928                 inst_ty.isPtrAtRuntime(mod))
  27929             anyopaque_check: {
  27930                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional;
  27931                 const elem_ty = inst_ty.elemType2(mod);
  27932                 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) {
  27933                     in_memory_result = .{ .double_ptr_to_anyopaque = .{
  27934                         .actual = inst_ty,
  27935                         .wanted = dest_ty,
  27936                     } };
  27937                     break :optional;
  27938                 }
  27939                 // Let the logic below handle wrapping the optional now that
  27940                 // it has been checked to correctly coerce.
  27941                 if (!inst_ty.isPtrLikeOptional(mod)) break :anyopaque_check;
  27942                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27943             }
  27944 
  27945             // T to ?T
  27946             const child_type = dest_ty.optionalChild(mod);
  27947             const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  27948                 error.NotCoercible => {
  27949                     if (in_memory_result == .no_match) {
  27950                         // Try to give more useful notes
  27951                         in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src);
  27952                     }
  27953                     break :optional;
  27954                 },
  27955                 else => |e| return e,
  27956             };
  27957             return try sema.wrapOptional(block, dest_ty, intermediate, inst_src);
  27958         },
  27959         .Pointer => pointer: {
  27960             const dest_info = dest_ty.ptrInfo(mod);
  27961 
  27962             // Function body to function pointer.
  27963             if (inst_ty.zigTypeTag(mod) == .Fn) {
  27964                 const fn_val = try sema.resolveConstDefinedValue(block, .unneeded, inst, undefined);
  27965                 const fn_decl = fn_val.pointerDecl(mod).?;
  27966                 const inst_as_ptr = try sema.analyzeDeclRef(fn_decl);
  27967                 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);
  27968             }
  27969 
  27970             // *T to *[1]T
  27971             single_item: {
  27972                 if (dest_info.flags.size != .One) break :single_item;
  27973                 if (!inst_ty.isSinglePointer(mod)) break :single_item;
  27974                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  27975                 const ptr_elem_ty = inst_ty.childType(mod);
  27976                 const array_ty = Type.fromInterned(dest_info.child);
  27977                 if (array_ty.zigTypeTag(mod) != .Array) break :single_item;
  27978                 const array_elem_ty = array_ty.childType(mod);
  27979                 if (array_ty.arrayLen(mod) != 1) break :single_item;
  27980                 const dest_is_mut = !dest_info.flags.is_const;
  27981                 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
  27982                     .ok => {},
  27983                     else => break :single_item,
  27984                 }
  27985                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27986             }
  27987 
  27988             // Coercions where the source is a single pointer to an array.
  27989             src_array_ptr: {
  27990                 if (!inst_ty.isSinglePointer(mod)) break :src_array_ptr;
  27991                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  27992                 const array_ty = inst_ty.childType(mod);
  27993                 if (array_ty.zigTypeTag(mod) != .Array) break :src_array_ptr;
  27994                 const array_elem_type = array_ty.childType(mod);
  27995                 const dest_is_mut = !dest_info.flags.is_const;
  27996 
  27997                 const dst_elem_type = Type.fromInterned(dest_info.child);
  27998                 const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src);
  27999                 switch (elem_res) {
  28000                     .ok => {},
  28001                     else => {
  28002                         in_memory_result = .{ .ptr_child = .{
  28003                             .child = try elem_res.dupe(sema.arena),
  28004                             .actual = array_elem_type,
  28005                             .wanted = dst_elem_type,
  28006                         } };
  28007                         break :src_array_ptr;
  28008                     },
  28009                 }
  28010 
  28011                 if (dest_info.sentinel != .none) {
  28012                     if (array_ty.sentinel(mod)) |inst_sent| {
  28013                         if (Air.internedToRef(dest_info.sentinel) !=
  28014                             try sema.coerceInMemory(inst_sent, dst_elem_type))
  28015                         {
  28016                             in_memory_result = .{ .ptr_sentinel = .{
  28017                                 .actual = inst_sent,
  28018                                 .wanted = Value.fromInterned(dest_info.sentinel),
  28019                                 .ty = dst_elem_type,
  28020                             } };
  28021                             break :src_array_ptr;
  28022                         }
  28023                     } else {
  28024                         in_memory_result = .{ .ptr_sentinel = .{
  28025                             .actual = Value.@"unreachable",
  28026                             .wanted = Value.fromInterned(dest_info.sentinel),
  28027                             .ty = dst_elem_type,
  28028                         } };
  28029                         break :src_array_ptr;
  28030                     }
  28031                 }
  28032 
  28033                 switch (dest_info.flags.size) {
  28034                     .Slice => {
  28035                         // *[N]T to []T
  28036                         return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);
  28037                     },
  28038                     .C => {
  28039                         // *[N]T to [*c]T
  28040                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28041                     },
  28042                     .Many => {
  28043                         // *[N]T to [*]T
  28044                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28045                     },
  28046                     .One => {},
  28047                 }
  28048             }
  28049 
  28050             // coercion from C pointer
  28051             if (inst_ty.isCPtr(mod)) src_c_ptr: {
  28052                 if (dest_info.flags.size == .Slice) break :src_c_ptr;
  28053                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr;
  28054                 // In this case we must add a safety check because the C pointer
  28055                 // could be null.
  28056                 const src_elem_ty = inst_ty.childType(mod);
  28057                 const dest_is_mut = !dest_info.flags.is_const;
  28058                 const dst_elem_type = Type.fromInterned(dest_info.child);
  28059                 switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
  28060                     .ok => {},
  28061                     else => break :src_c_ptr,
  28062                 }
  28063                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28064             }
  28065 
  28066             // cast from *T and [*]T to *anyopaque
  28067             // but don't do it if the source type is a double pointer
  28068             if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(mod) == .Pointer) to_anyopaque: {
  28069                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  28070                 const elem_ty = inst_ty.elemType2(mod);
  28071                 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) {
  28072                     in_memory_result = .{ .double_ptr_to_anyopaque = .{
  28073                         .actual = inst_ty,
  28074                         .wanted = dest_ty,
  28075                     } };
  28076                     break :pointer;
  28077                 }
  28078                 if (dest_ty.isSlice(mod)) break :to_anyopaque;
  28079                 if (inst_ty.isSlice(mod)) {
  28080                     in_memory_result = .{ .slice_to_anyopaque = .{
  28081                         .actual = inst_ty,
  28082                         .wanted = dest_ty,
  28083                     } };
  28084                     break :pointer;
  28085                 }
  28086                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28087             }
  28088 
  28089             switch (dest_info.flags.size) {
  28090                 // coercion to C pointer
  28091                 .C => switch (inst_ty.zigTypeTag(mod)) {
  28092                     .Null => return Air.internedToRef(try mod.intern(.{ .ptr = .{
  28093                         .ty = dest_ty.toIntern(),
  28094                         .addr = .{ .int = .zero_usize },
  28095                     } })),
  28096                     .ComptimeInt => {
  28097                         const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  28098                             error.NotCoercible => break :pointer,
  28099                             else => |e| return e,
  28100                         };
  28101                         return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
  28102                     },
  28103                     .Int => {
  28104                         const ptr_size_ty = switch (inst_ty.intInfo(mod).signedness) {
  28105                             .signed => Type.isize,
  28106                             .unsigned => Type.usize,
  28107                         };
  28108                         const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  28109                             error.NotCoercible => {
  28110                                 // Try to give more useful notes
  28111                                 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src);
  28112                                 break :pointer;
  28113                             },
  28114                             else => |e| return e,
  28115                         };
  28116                         return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
  28117                     },
  28118                     .Pointer => p: {
  28119                         if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
  28120                         const inst_info = inst_ty.ptrInfo(mod);
  28121                         switch (try sema.coerceInMemoryAllowed(
  28122                             block,
  28123                             Type.fromInterned(dest_info.child),
  28124                             Type.fromInterned(inst_info.child),
  28125                             !dest_info.flags.is_const,
  28126                             target,
  28127                             dest_ty_src,
  28128                             inst_src,
  28129                         )) {
  28130                             .ok => {},
  28131                             else => break :p,
  28132                         }
  28133                         if (inst_info.flags.size == .Slice) {
  28134                             assert(dest_info.sentinel == .none);
  28135                             if (inst_info.sentinel == .none or
  28136                                 inst_info.sentinel != (try mod.intValue(Type.fromInterned(inst_info.child), 0)).toIntern())
  28137                                 break :p;
  28138 
  28139                             const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
  28140                             return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
  28141                         }
  28142                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28143                     },
  28144                     else => {},
  28145                 },
  28146                 .One => switch (Type.fromInterned(dest_info.child).zigTypeTag(mod)) {
  28147                     .Union => {
  28148                         // pointer to anonymous struct to pointer to union
  28149                         if (inst_ty.isSinglePointer(mod) and
  28150                             inst_ty.childType(mod).isAnonStruct(mod) and
  28151                             sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))
  28152                         {
  28153                             return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
  28154                         }
  28155                     },
  28156                     .Struct => {
  28157                         // pointer to anonymous struct to pointer to struct
  28158                         if (inst_ty.isSinglePointer(mod) and
  28159                             inst_ty.childType(mod).isAnonStruct(mod) and
  28160                             sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))
  28161                         {
  28162                             return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src) catch |err| switch (err) {
  28163                                 error.NotCoercible => break :pointer,
  28164                                 else => |e| return e,
  28165                             };
  28166                         }
  28167                     },
  28168                     .Array => {
  28169                         // pointer to tuple to pointer to array
  28170                         if (inst_ty.isSinglePointer(mod) and
  28171                             inst_ty.childType(mod).isTuple(mod) and
  28172                             sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))
  28173                         {
  28174                             return sema.coerceTupleToArrayPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
  28175                         }
  28176                     },
  28177                     else => {},
  28178                 },
  28179                 .Slice => to_slice: {
  28180                     if (inst_ty.zigTypeTag(mod) == .Array) {
  28181                         return sema.fail(
  28182                             block,
  28183                             inst_src,
  28184                             "array literal requires address-of operator (&) to coerce to slice type '{}'",
  28185                             .{dest_ty.fmt(mod)},
  28186                         );
  28187                     }
  28188 
  28189                     if (!inst_ty.isSinglePointer(mod)) break :to_slice;
  28190                     const inst_child_ty = inst_ty.childType(mod);
  28191                     if (!inst_child_ty.isTuple(mod)) break :to_slice;
  28192 
  28193                     // empty tuple to zero-length slice
  28194                     // note that this allows coercing to a mutable slice.
  28195                     if (inst_child_ty.structFieldCount(mod) == 0) {
  28196                         // Optional slice is represented with a null pointer so
  28197                         // we use a dummy pointer value with the required alignment.
  28198                         return Air.internedToRef((try mod.intern(.{ .ptr = .{
  28199                             .ty = dest_ty.toIntern(),
  28200                             .addr = .{ .int = if (dest_info.flags.alignment != .none)
  28201                                 (try mod.intValue(
  28202                                     Type.usize,
  28203                                     dest_info.flags.alignment.toByteUnitsOptional().?,
  28204                                 )).toIntern()
  28205                             else
  28206                                 try mod.intern_pool.getCoercedInts(
  28207                                     mod.gpa,
  28208                                     mod.intern_pool.indexToKey(
  28209                                         (try Type.fromInterned(dest_info.child).lazyAbiAlignment(mod)).toIntern(),
  28210                                     ).int,
  28211                                     .usize_type,
  28212                                 ) },
  28213                             .len = (try mod.intValue(Type.usize, 0)).toIntern(),
  28214                         } })));
  28215                     }
  28216 
  28217                     // pointer to tuple to slice
  28218                     if (!dest_info.flags.is_const) {
  28219                         const err_msg = err_msg: {
  28220                             const err_msg = try sema.errMsg(block, inst_src, "cannot cast pointer to tuple to '{}'", .{dest_ty.fmt(mod)});
  28221                             errdefer err_msg.deinit(sema.gpa);
  28222                             try sema.errNote(block, dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{});
  28223                             break :err_msg err_msg;
  28224                         };
  28225                         return sema.failWithOwnedErrorMsg(block, err_msg);
  28226                     }
  28227                     return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);
  28228                 },
  28229                 .Many => p: {
  28230                     if (!inst_ty.isSlice(mod)) break :p;
  28231                     if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
  28232                     const inst_info = inst_ty.ptrInfo(mod);
  28233 
  28234                     switch (try sema.coerceInMemoryAllowed(
  28235                         block,
  28236                         Type.fromInterned(dest_info.child),
  28237                         Type.fromInterned(inst_info.child),
  28238                         !dest_info.flags.is_const,
  28239                         target,
  28240                         dest_ty_src,
  28241                         inst_src,
  28242                     )) {
  28243                         .ok => {},
  28244                         else => break :p,
  28245                     }
  28246 
  28247                     if (dest_info.sentinel == .none or inst_info.sentinel == .none or
  28248                         Air.internedToRef(dest_info.sentinel) !=
  28249                         try sema.coerceInMemory(Value.fromInterned(inst_info.sentinel), Type.fromInterned(dest_info.child)))
  28250                         break :p;
  28251 
  28252                     const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
  28253                     return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
  28254                 },
  28255             }
  28256         },
  28257         .Int, .ComptimeInt => switch (inst_ty.zigTypeTag(mod)) {
  28258             .Float, .ComptimeFloat => float: {
  28259                 const val = maybe_inst_val orelse {
  28260                     if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
  28261                         if (!opts.report_err) return error.NotCoercible;
  28262                         return sema.failWithNeededComptime(block, inst_src, .{
  28263                             .needed_comptime_reason = "value being casted to 'comptime_int' must be comptime-known",
  28264                         });
  28265                     }
  28266                     break :float;
  28267                 };
  28268                 const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty, .exact);
  28269                 return Air.internedToRef(result_val.toIntern());
  28270             },
  28271             .Int, .ComptimeInt => {
  28272                 if (maybe_inst_val) |val| {
  28273                     // comptime-known integer to other number
  28274                     if (!(try sema.intFitsInType(val, dest_ty, null))) {
  28275                         if (!opts.report_err) return error.NotCoercible;
  28276                         return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) });
  28277                     }
  28278                     return switch (mod.intern_pool.indexToKey(val.toIntern())) {
  28279                         .undef => try mod.undefRef(dest_ty),
  28280                         .int => |int| Air.internedToRef(
  28281                             try mod.intern_pool.getCoercedInts(mod.gpa, int, dest_ty.toIntern()),
  28282                         ),
  28283                         else => unreachable,
  28284                     };
  28285                 }
  28286                 if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
  28287                     if (!opts.report_err) return error.NotCoercible;
  28288                     if (opts.no_cast_to_comptime_int) return inst;
  28289                     return sema.failWithNeededComptime(block, inst_src, .{
  28290                         .needed_comptime_reason = "value being casted to 'comptime_int' must be comptime-known",
  28291                     });
  28292                 }
  28293 
  28294                 // integer widening
  28295                 const dst_info = dest_ty.intInfo(mod);
  28296                 const src_info = inst_ty.intInfo(mod);
  28297                 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
  28298                     // small enough unsigned ints can get casted to large enough signed ints
  28299                     (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
  28300                 {
  28301                     try sema.requireRuntimeBlock(block, inst_src, null);
  28302                     return block.addTyOp(.intcast, dest_ty, inst);
  28303                 }
  28304             },
  28305             else => {},
  28306         },
  28307         .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) {
  28308             .ComptimeFloat => {
  28309                 const val = try sema.resolveConstDefinedValue(block, .unneeded, inst, undefined);
  28310                 const result_val = try val.floatCast(dest_ty, mod);
  28311                 return Air.internedToRef(result_val.toIntern());
  28312             },
  28313             .Float => {
  28314                 if (maybe_inst_val) |val| {
  28315                     const result_val = try val.floatCast(dest_ty, mod);
  28316                     if (!val.eql(try result_val.floatCast(inst_ty, mod), inst_ty, mod)) {
  28317                         return sema.fail(
  28318                             block,
  28319                             inst_src,
  28320                             "type '{}' cannot represent float value '{}'",
  28321                             .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) },
  28322                         );
  28323                     }
  28324                     return Air.internedToRef(result_val.toIntern());
  28325                 } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) {
  28326                     if (!opts.report_err) return error.NotCoercible;
  28327                     return sema.failWithNeededComptime(block, inst_src, .{
  28328                         .needed_comptime_reason = "value being casted to 'comptime_float' must be comptime-known",
  28329                     });
  28330                 }
  28331 
  28332                 // float widening
  28333                 const src_bits = inst_ty.floatBits(target);
  28334                 const dst_bits = dest_ty.floatBits(target);
  28335                 if (dst_bits >= src_bits) {
  28336                     try sema.requireRuntimeBlock(block, inst_src, null);
  28337                     return block.addTyOp(.fpext, dest_ty, inst);
  28338                 }
  28339             },
  28340             .Int, .ComptimeInt => int: {
  28341                 const val = maybe_inst_val orelse {
  28342                     if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) {
  28343                         if (!opts.report_err) return error.NotCoercible;
  28344                         return sema.failWithNeededComptime(block, inst_src, .{
  28345                             .needed_comptime_reason = "value being casted to 'comptime_float' must be comptime-known",
  28346                         });
  28347                     }
  28348                     break :int;
  28349                 };
  28350                 const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, mod, sema);
  28351                 // TODO implement this compile error
  28352                 //const int_again_val = try result_val.intFromFloat(sema.arena, inst_ty);
  28353                 //if (!int_again_val.eql(val, inst_ty, mod)) {
  28354                 //    return sema.fail(
  28355                 //        block,
  28356                 //        inst_src,
  28357                 //        "type '{}' cannot represent integer value '{}'",
  28358                 //        .{ dest_ty.fmt(mod), val },
  28359                 //    );
  28360                 //}
  28361                 return Air.internedToRef(result_val.toIntern());
  28362             },
  28363             else => {},
  28364         },
  28365         .Enum => switch (inst_ty.zigTypeTag(mod)) {
  28366             .EnumLiteral => {
  28367                 // enum literal to enum
  28368                 const val = try sema.resolveConstDefinedValue(block, .unneeded, inst, undefined);
  28369                 const string = mod.intern_pool.indexToKey(val.toIntern()).enum_literal;
  28370                 const field_index = dest_ty.enumFieldIndex(string, mod) orelse {
  28371                     const msg = msg: {
  28372                         const msg = try sema.errMsg(
  28373                             block,
  28374                             inst_src,
  28375                             "no field named '{}' in enum '{}'",
  28376                             .{ string.fmt(&mod.intern_pool), dest_ty.fmt(mod) },
  28377                         );
  28378                         errdefer msg.destroy(sema.gpa);
  28379                         try sema.addDeclaredHereNote(msg, dest_ty);
  28380                         break :msg msg;
  28381                     };
  28382                     return sema.failWithOwnedErrorMsg(block, msg);
  28383                 };
  28384                 return Air.internedToRef((try mod.enumValueFieldIndex(dest_ty, @intCast(field_index))).toIntern());
  28385             },
  28386             .Union => blk: {
  28387                 // union to its own tag type
  28388                 const union_tag_ty = inst_ty.unionTagType(mod) orelse break :blk;
  28389                 if (union_tag_ty.eql(dest_ty, mod)) {
  28390                     return sema.unionToTag(block, dest_ty, inst, inst_src);
  28391                 }
  28392             },
  28393             else => {},
  28394         },
  28395         .ErrorUnion => switch (inst_ty.zigTypeTag(mod)) {
  28396             .ErrorUnion => eu: {
  28397                 if (maybe_inst_val) |inst_val| {
  28398                     switch (inst_val.toIntern()) {
  28399                         .undef => return mod.undefRef(dest_ty),
  28400                         else => switch (mod.intern_pool.indexToKey(inst_val.toIntern())) {
  28401                             .error_union => |error_union| switch (error_union.val) {
  28402                                 .err_name => |err_name| {
  28403                                     const error_set_ty = inst_ty.errorUnionSet(mod);
  28404                                     const error_set_val = Air.internedToRef((try mod.intern(.{ .err = .{
  28405                                         .ty = error_set_ty.toIntern(),
  28406                                         .name = err_name,
  28407                                     } })));
  28408                                     return sema.wrapErrorUnionSet(block, dest_ty, error_set_val, inst_src);
  28409                                 },
  28410                                 .payload => |payload| {
  28411                                     const payload_val = Air.internedToRef(payload);
  28412                                     return sema.wrapErrorUnionPayload(block, dest_ty, payload_val, inst_src) catch |err| switch (err) {
  28413                                         error.NotCoercible => break :eu,
  28414                                         else => |e| return e,
  28415                                     };
  28416                                 },
  28417                             },
  28418                             else => unreachable,
  28419                         },
  28420                     }
  28421                 }
  28422             },
  28423             .ErrorSet => {
  28424                 // E to E!T
  28425                 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);
  28426             },
  28427             else => eu: {
  28428                 // T to E!T
  28429                 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) {
  28430                     error.NotCoercible => break :eu,
  28431                     else => |e| return e,
  28432                 };
  28433             },
  28434         },
  28435         .Union => switch (inst_ty.zigTypeTag(mod)) {
  28436             .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
  28437             .Struct => {
  28438                 if (inst_ty.isAnonStruct(mod)) {
  28439                     return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src);
  28440                 }
  28441             },
  28442             else => {},
  28443         },
  28444         .Array => switch (inst_ty.zigTypeTag(mod)) {
  28445             .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
  28446             .Struct => {
  28447                 if (inst == .empty_struct) {
  28448                     return sema.arrayInitEmpty(block, inst_src, dest_ty);
  28449                 }
  28450                 if (inst_ty.isTuple(mod)) {
  28451                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
  28452                 }
  28453             },
  28454             else => {},
  28455         },
  28456         .Vector => switch (inst_ty.zigTypeTag(mod)) {
  28457             .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
  28458             .Struct => {
  28459                 if (inst_ty.isTuple(mod)) {
  28460                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
  28461                 }
  28462             },
  28463             else => {},
  28464         },
  28465         .Struct => blk: {
  28466             if (inst == .empty_struct) {
  28467                 return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src);
  28468             }
  28469             if (inst_ty.isTupleOrAnonStruct(mod)) {
  28470                 return sema.coerceTupleToStruct(block, dest_ty, inst, inst_src) catch |err| switch (err) {
  28471                     error.NotCoercible => break :blk,
  28472                     else => |e| return e,
  28473                 };
  28474             }
  28475         },
  28476         else => {},
  28477     }
  28478 
  28479     // undefined to anything. We do this after the big switch above so that
  28480     // special logic has a chance to run first, such as `*[N]T` to `[]T` which
  28481     // should initialize the length field of the slice.
  28482     if (maybe_inst_val) |val| if (val.toIntern() == .undef) return mod.undefRef(dest_ty);
  28483 
  28484     if (!opts.report_err) return error.NotCoercible;
  28485 
  28486     if (opts.is_ret and dest_ty.zigTypeTag(mod) == .NoReturn) {
  28487         const msg = msg: {
  28488             const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{});
  28489             errdefer msg.destroy(sema.gpa);
  28490 
  28491             const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
  28492             const src_decl = mod.funcOwnerDeclPtr(sema.func_index);
  28493             try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "'noreturn' declared here", .{});
  28494             break :msg msg;
  28495         };
  28496         return sema.failWithOwnedErrorMsg(block, msg);
  28497     }
  28498 
  28499     const msg = msg: {
  28500         const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), inst_ty.fmt(mod) });
  28501         errdefer msg.destroy(sema.gpa);
  28502 
  28503         // E!T to T
  28504         if (inst_ty.zigTypeTag(mod) == .ErrorUnion and
  28505             (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
  28506         {
  28507             try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{});
  28508             try sema.errNote(block, inst_src, msg, "consider using 'try', 'catch', or 'if'", .{});
  28509         }
  28510 
  28511         // ?T to T
  28512         if (inst_ty.zigTypeTag(mod) == .Optional and
  28513             (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
  28514         {
  28515             try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{});
  28516             try sema.errNote(block, inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{});
  28517         }
  28518 
  28519         try in_memory_result.report(sema, block, inst_src, msg);
  28520 
  28521         // Add notes about function return type
  28522         if (opts.is_ret and
  28523             mod.test_functions.get(mod.funcOwnerDeclIndex(sema.func_index)) == null)
  28524         {
  28525             const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
  28526             const src_decl = mod.funcOwnerDeclPtr(sema.func_index);
  28527             if (inst_ty.isError(mod) and !dest_ty.isError(mod)) {
  28528                 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function cannot return an error", .{});
  28529             } else {
  28530                 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function return type declared here", .{});
  28531             }
  28532         }
  28533 
  28534         if (try opts.param_src.get(sema)) |param_src| {
  28535             try mod.errNoteNonLazy(param_src, msg, "parameter type declared here", .{});
  28536         }
  28537 
  28538         // TODO maybe add "cannot store an error in type '{}'" note
  28539 
  28540         break :msg msg;
  28541     };
  28542     return sema.failWithOwnedErrorMsg(block, msg);
  28543 }
  28544 
  28545 fn coerceInMemory(
  28546     sema: *Sema,
  28547     val: Value,
  28548     dst_ty: Type,
  28549 ) CompileError!Air.Inst.Ref {
  28550     return Air.internedToRef((try sema.mod.getCoerced(val, dst_ty)).toIntern());
  28551 }
  28552 
  28553 const InMemoryCoercionResult = union(enum) {
  28554     ok,
  28555     no_match: Pair,
  28556     int_not_coercible: Int,
  28557     error_union_payload: PairAndChild,
  28558     array_len: IntPair,
  28559     array_sentinel: Sentinel,
  28560     array_elem: PairAndChild,
  28561     vector_len: IntPair,
  28562     vector_elem: PairAndChild,
  28563     optional_shape: Pair,
  28564     optional_child: PairAndChild,
  28565     from_anyerror,
  28566     missing_error: []const InternPool.NullTerminatedString,
  28567     /// true if wanted is var args
  28568     fn_var_args: bool,
  28569     /// true if wanted is generic
  28570     fn_generic: bool,
  28571     fn_param_count: IntPair,
  28572     fn_param_noalias: IntPair,
  28573     fn_param_comptime: ComptimeParam,
  28574     fn_param: Param,
  28575     fn_cc: CC,
  28576     fn_return_type: PairAndChild,
  28577     ptr_child: PairAndChild,
  28578     ptr_addrspace: AddressSpace,
  28579     ptr_sentinel: Sentinel,
  28580     ptr_size: Size,
  28581     ptr_qualifiers: Qualifiers,
  28582     ptr_allowzero: Pair,
  28583     ptr_bit_range: BitRange,
  28584     ptr_alignment: AlignPair,
  28585     double_ptr_to_anyopaque: Pair,
  28586     slice_to_anyopaque: Pair,
  28587 
  28588     const Pair = struct {
  28589         actual: Type,
  28590         wanted: Type,
  28591     };
  28592 
  28593     const PairAndChild = struct {
  28594         child: *InMemoryCoercionResult,
  28595         actual: Type,
  28596         wanted: Type,
  28597     };
  28598 
  28599     const Param = struct {
  28600         child: *InMemoryCoercionResult,
  28601         actual: Type,
  28602         wanted: Type,
  28603         index: u64,
  28604     };
  28605 
  28606     const ComptimeParam = struct {
  28607         index: u64,
  28608         wanted: bool,
  28609     };
  28610 
  28611     const Sentinel = struct {
  28612         // unreachable_value indicates no sentinel
  28613         actual: Value,
  28614         wanted: Value,
  28615         ty: Type,
  28616     };
  28617 
  28618     const Int = struct {
  28619         actual_signedness: std.builtin.Signedness,
  28620         wanted_signedness: std.builtin.Signedness,
  28621         actual_bits: u16,
  28622         wanted_bits: u16,
  28623     };
  28624 
  28625     const IntPair = struct {
  28626         actual: u64,
  28627         wanted: u64,
  28628     };
  28629 
  28630     const AlignPair = struct {
  28631         actual: Alignment,
  28632         wanted: Alignment,
  28633     };
  28634 
  28635     const Size = struct {
  28636         actual: std.builtin.Type.Pointer.Size,
  28637         wanted: std.builtin.Type.Pointer.Size,
  28638     };
  28639 
  28640     const Qualifiers = struct {
  28641         actual_const: bool,
  28642         wanted_const: bool,
  28643         actual_volatile: bool,
  28644         wanted_volatile: bool,
  28645     };
  28646 
  28647     const AddressSpace = struct {
  28648         actual: std.builtin.AddressSpace,
  28649         wanted: std.builtin.AddressSpace,
  28650     };
  28651 
  28652     const CC = struct {
  28653         actual: std.builtin.CallingConvention,
  28654         wanted: std.builtin.CallingConvention,
  28655     };
  28656 
  28657     const BitRange = struct {
  28658         actual_host: u16,
  28659         wanted_host: u16,
  28660         actual_offset: u16,
  28661         wanted_offset: u16,
  28662     };
  28663 
  28664     fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult {
  28665         const res = try arena.create(InMemoryCoercionResult);
  28666         res.* = child.*;
  28667         return res;
  28668     }
  28669 
  28670     fn report(res: *const InMemoryCoercionResult, sema: *Sema, block: *Block, src: LazySrcLoc, msg: *Module.ErrorMsg) !void {
  28671         const mod = sema.mod;
  28672         var cur = res;
  28673         while (true) switch (cur.*) {
  28674             .ok => unreachable,
  28675             .no_match => |types| {
  28676                 try sema.addDeclaredHereNote(msg, types.wanted);
  28677                 try sema.addDeclaredHereNote(msg, types.actual);
  28678                 break;
  28679             },
  28680             .int_not_coercible => |int| {
  28681                 try sema.errNote(block, src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{
  28682                     @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits,
  28683                 });
  28684                 break;
  28685             },
  28686             .error_union_payload => |pair| {
  28687                 try sema.errNote(block, src, msg, "error union payload '{}' cannot cast into error union payload '{}'", .{
  28688                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  28689                 });
  28690                 cur = pair.child;
  28691             },
  28692             .array_len => |lens| {
  28693                 try sema.errNote(block, src, msg, "array of length {d} cannot cast into an array of length {d}", .{
  28694                     lens.actual, lens.wanted,
  28695                 });
  28696                 break;
  28697             },
  28698             .array_sentinel => |sentinel| {
  28699                 if (sentinel.actual.toIntern() != .unreachable_value) {
  28700                     try sema.errNote(block, src, msg, "array sentinel '{}' cannot cast into array sentinel '{}'", .{
  28701                         sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod),
  28702                     });
  28703                 } else {
  28704                     try sema.errNote(block, src, msg, "destination array requires '{}' sentinel", .{
  28705                         sentinel.wanted.fmtValue(sentinel.ty, mod),
  28706                     });
  28707                 }
  28708                 break;
  28709             },
  28710             .array_elem => |pair| {
  28711                 try sema.errNote(block, src, msg, "array element type '{}' cannot cast into array element type '{}'", .{
  28712                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  28713                 });
  28714                 cur = pair.child;
  28715             },
  28716             .vector_len => |lens| {
  28717                 try sema.errNote(block, src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{
  28718                     lens.actual, lens.wanted,
  28719                 });
  28720                 break;
  28721             },
  28722             .vector_elem => |pair| {
  28723                 try sema.errNote(block, src, msg, "vector element type '{}' cannot cast into vector element type '{}'", .{
  28724                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  28725                 });
  28726                 cur = pair.child;
  28727             },
  28728             .optional_shape => |pair| {
  28729                 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{
  28730                     pair.actual.optionalChild(mod).fmt(mod), pair.wanted.optionalChild(mod).fmt(mod),
  28731                 });
  28732                 break;
  28733             },
  28734             .optional_child => |pair| {
  28735                 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{
  28736                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  28737                 });
  28738                 cur = pair.child;
  28739             },
  28740             .from_anyerror => {
  28741                 try sema.errNote(block, src, msg, "global error set cannot cast into a smaller set", .{});
  28742                 break;
  28743             },
  28744             .missing_error => |missing_errors| {
  28745                 for (missing_errors) |err| {
  28746                     try sema.errNote(block, src, msg, "'error.{}' not a member of destination error set", .{err.fmt(&mod.intern_pool)});
  28747                 }
  28748                 break;
  28749             },
  28750             .fn_var_args => |wanted_var_args| {
  28751                 if (wanted_var_args) {
  28752                     try sema.errNote(block, src, msg, "non-variadic function cannot cast into a variadic function", .{});
  28753                 } else {
  28754                     try sema.errNote(block, src, msg, "variadic function cannot cast into a non-variadic function", .{});
  28755                 }
  28756                 break;
  28757             },
  28758             .fn_generic => |wanted_generic| {
  28759                 if (wanted_generic) {
  28760                     try sema.errNote(block, src, msg, "non-generic function cannot cast into a generic function", .{});
  28761                 } else {
  28762                     try sema.errNote(block, src, msg, "generic function cannot cast into a non-generic function", .{});
  28763                 }
  28764                 break;
  28765             },
  28766             .fn_param_count => |lens| {
  28767                 try sema.errNote(block, src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{
  28768                     lens.actual, lens.wanted,
  28769                 });
  28770                 break;
  28771             },
  28772             .fn_param_noalias => |param| {
  28773                 var index: u6 = 0;
  28774                 var actual_noalias = false;
  28775                 while (true) : (index += 1) {
  28776                     const actual: u1 = @truncate(param.actual >> index);
  28777                     const wanted: u1 = @truncate(param.wanted >> index);
  28778                     if (actual != wanted) {
  28779                         actual_noalias = actual == 1;
  28780                         break;
  28781                     }
  28782                 }
  28783                 if (!actual_noalias) {
  28784                     try sema.errNote(block, src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index});
  28785                 } else {
  28786                     try sema.errNote(block, src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index});
  28787                 }
  28788                 break;
  28789             },
  28790             .fn_param_comptime => |param| {
  28791                 if (param.wanted) {
  28792                     try sema.errNote(block, src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index});
  28793                 } else {
  28794                     try sema.errNote(block, src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index});
  28795                 }
  28796                 break;
  28797             },
  28798             .fn_param => |param| {
  28799                 try sema.errNote(block, src, msg, "parameter {d} '{}' cannot cast into '{}'", .{
  28800                     param.index, param.actual.fmt(mod), param.wanted.fmt(mod),
  28801                 });
  28802                 cur = param.child;
  28803             },
  28804             .fn_cc => |cc| {
  28805                 try sema.errNote(block, src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) });
  28806                 break;
  28807             },
  28808             .fn_return_type => |pair| {
  28809                 try sema.errNote(block, src, msg, "return type '{}' cannot cast into return type '{}'", .{
  28810                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  28811                 });
  28812                 cur = pair.child;
  28813             },
  28814             .ptr_child => |pair| {
  28815                 try sema.errNote(block, src, msg, "pointer type child '{}' cannot cast into pointer type child '{}'", .{
  28816                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  28817                 });
  28818                 cur = pair.child;
  28819             },
  28820             .ptr_addrspace => |@"addrspace"| {
  28821                 try sema.errNote(block, src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) });
  28822                 break;
  28823             },
  28824             .ptr_sentinel => |sentinel| {
  28825                 if (sentinel.actual.toIntern() != .unreachable_value) {
  28826                     try sema.errNote(block, src, msg, "pointer sentinel '{}' cannot cast into pointer sentinel '{}'", .{
  28827                         sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod),
  28828                     });
  28829                 } else {
  28830                     try sema.errNote(block, src, msg, "destination pointer requires '{}' sentinel", .{
  28831                         sentinel.wanted.fmtValue(sentinel.ty, mod),
  28832                     });
  28833                 }
  28834                 break;
  28835             },
  28836             .ptr_size => |size| {
  28837                 try sema.errNote(block, src, msg, "a {s} pointer cannot cast into a {s} pointer", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) });
  28838                 break;
  28839             },
  28840             .ptr_qualifiers => |qualifiers| {
  28841                 const ok_const = !qualifiers.actual_const or qualifiers.wanted_const;
  28842                 const ok_volatile = !qualifiers.actual_volatile or qualifiers.wanted_volatile;
  28843                 if (!ok_const) {
  28844                     try sema.errNote(block, src, msg, "cast discards const qualifier", .{});
  28845                 } else if (!ok_volatile) {
  28846                     try sema.errNote(block, src, msg, "cast discards volatile qualifier", .{});
  28847                 }
  28848                 break;
  28849             },
  28850             .ptr_allowzero => |pair| {
  28851                 const wanted_allow_zero = pair.wanted.ptrAllowsZero(mod);
  28852                 const actual_allow_zero = pair.actual.ptrAllowsZero(mod);
  28853                 if (actual_allow_zero and !wanted_allow_zero) {
  28854                     try sema.errNote(block, src, msg, "'{}' could have null values which are illegal in type '{}'", .{
  28855                         pair.actual.fmt(mod), pair.wanted.fmt(mod),
  28856                     });
  28857                 } else {
  28858                     try sema.errNote(block, src, msg, "mutable '{}' allows illegal null values stored to type '{}'", .{
  28859                         pair.actual.fmt(mod), pair.wanted.fmt(mod),
  28860                     });
  28861                 }
  28862                 break;
  28863             },
  28864             .ptr_bit_range => |bit_range| {
  28865                 if (bit_range.actual_host != bit_range.wanted_host) {
  28866                     try sema.errNote(block, src, msg, "pointer host size '{}' cannot cast into pointer host size '{}'", .{
  28867                         bit_range.actual_host, bit_range.wanted_host,
  28868                     });
  28869                 }
  28870                 if (bit_range.actual_offset != bit_range.wanted_offset) {
  28871                     try sema.errNote(block, src, msg, "pointer bit offset '{}' cannot cast into pointer bit offset '{}'", .{
  28872                         bit_range.actual_offset, bit_range.wanted_offset,
  28873                     });
  28874                 }
  28875                 break;
  28876             },
  28877             .ptr_alignment => |pair| {
  28878                 try sema.errNote(block, src, msg, "pointer alignment '{d}' cannot cast into pointer alignment '{d}'", .{
  28879                     pair.actual.toByteUnits(0), pair.wanted.toByteUnits(0),
  28880                 });
  28881                 break;
  28882             },
  28883             .double_ptr_to_anyopaque => |pair| {
  28884                 try sema.errNote(block, src, msg, "cannot implicitly cast double pointer '{}' to anyopaque pointer '{}'", .{
  28885                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  28886                 });
  28887                 break;
  28888             },
  28889             .slice_to_anyopaque => |pair| {
  28890                 try sema.errNote(block, src, msg, "cannot implicitly cast slice '{}' to anyopaque pointer '{}'", .{
  28891                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  28892                 });
  28893                 try sema.errNote(block, src, msg, "consider using '.ptr'", .{});
  28894                 break;
  28895             },
  28896         };
  28897     }
  28898 };
  28899 
  28900 fn pointerSizeString(size: std.builtin.Type.Pointer.Size) []const u8 {
  28901     return switch (size) {
  28902         .One => "single",
  28903         .Many => "many",
  28904         .C => "C",
  28905         .Slice => unreachable,
  28906     };
  28907 }
  28908 
  28909 /// If pointers have the same representation in runtime memory, a bitcast AIR instruction
  28910 /// may be used for the coercion.
  28911 /// * `const` attribute can be gained
  28912 /// * `volatile` attribute can be gained
  28913 /// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if !dest_is_mut
  28914 /// * alignment can be decreased
  28915 /// * bit offset attributes must match exactly
  28916 /// * `*`/`[*]` must match exactly, but `[*c]` matches either one
  28917 /// * sentinel-terminated pointers can coerce into `[*]`
  28918 fn coerceInMemoryAllowed(
  28919     sema: *Sema,
  28920     block: *Block,
  28921     dest_ty: Type,
  28922     src_ty: Type,
  28923     dest_is_mut: bool,
  28924     target: std.Target,
  28925     dest_src: LazySrcLoc,
  28926     src_src: LazySrcLoc,
  28927 ) CompileError!InMemoryCoercionResult {
  28928     const mod = sema.mod;
  28929 
  28930     if (dest_ty.eql(src_ty, mod))
  28931         return .ok;
  28932 
  28933     const dest_tag = dest_ty.zigTypeTag(mod);
  28934     const src_tag = src_ty.zigTypeTag(mod);
  28935 
  28936     // Differently-named integers with the same number of bits.
  28937     if (dest_tag == .Int and src_tag == .Int) {
  28938         const dest_info = dest_ty.intInfo(mod);
  28939         const src_info = src_ty.intInfo(mod);
  28940 
  28941         if (dest_info.signedness == src_info.signedness and
  28942             dest_info.bits == src_info.bits)
  28943         {
  28944             return .ok;
  28945         }
  28946 
  28947         if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or
  28948             // small enough unsigned ints can get casted to large enough signed ints
  28949             (dest_info.signedness == .signed and (src_info.signedness == .unsigned or dest_info.bits <= src_info.bits)) or
  28950             (dest_info.signedness == .unsigned and src_info.signedness == .signed))
  28951         {
  28952             return InMemoryCoercionResult{ .int_not_coercible = .{
  28953                 .actual_signedness = src_info.signedness,
  28954                 .wanted_signedness = dest_info.signedness,
  28955                 .actual_bits = src_info.bits,
  28956                 .wanted_bits = dest_info.bits,
  28957             } };
  28958         }
  28959     }
  28960 
  28961     // Differently-named floats with the same number of bits.
  28962     if (dest_tag == .Float and src_tag == .Float) {
  28963         const dest_bits = dest_ty.floatBits(target);
  28964         const src_bits = src_ty.floatBits(target);
  28965         if (dest_bits == src_bits) {
  28966             return .ok;
  28967         }
  28968     }
  28969 
  28970     // Pointers / Pointer-like Optionals
  28971     const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty);
  28972     const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty);
  28973     if (maybe_dest_ptr_ty) |dest_ptr_ty| {
  28974         if (maybe_src_ptr_ty) |src_ptr_ty| {
  28975             return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src);
  28976         }
  28977     }
  28978 
  28979     // Slices
  28980     if (dest_ty.isSlice(mod) and src_ty.isSlice(mod)) {
  28981         return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
  28982     }
  28983 
  28984     // Functions
  28985     if (dest_tag == .Fn and src_tag == .Fn) {
  28986         return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, target, dest_src, src_src);
  28987     }
  28988 
  28989     // Error Unions
  28990     if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) {
  28991         const dest_payload = dest_ty.errorUnionPayload(mod);
  28992         const src_payload = src_ty.errorUnionPayload(mod);
  28993         const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src);
  28994         if (child != .ok) {
  28995             return InMemoryCoercionResult{ .error_union_payload = .{
  28996                 .child = try child.dupe(sema.arena),
  28997                 .actual = src_payload,
  28998                 .wanted = dest_payload,
  28999             } };
  29000         }
  29001         return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(mod), src_ty.errorUnionSet(mod), dest_is_mut, target, dest_src, src_src);
  29002     }
  29003 
  29004     // Error Sets
  29005     if (dest_tag == .ErrorSet and src_tag == .ErrorSet) {
  29006         return try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src);
  29007     }
  29008 
  29009     // Arrays
  29010     if (dest_tag == .Array and src_tag == .Array) {
  29011         const dest_info = dest_ty.arrayInfo(mod);
  29012         const src_info = src_ty.arrayInfo(mod);
  29013         if (dest_info.len != src_info.len) {
  29014             return InMemoryCoercionResult{ .array_len = .{
  29015                 .actual = src_info.len,
  29016                 .wanted = dest_info.len,
  29017             } };
  29018         }
  29019 
  29020         const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src);
  29021         if (child != .ok) {
  29022             return InMemoryCoercionResult{ .array_elem = .{
  29023                 .child = try child.dupe(sema.arena),
  29024                 .actual = src_info.elem_type,
  29025                 .wanted = dest_info.elem_type,
  29026             } };
  29027         }
  29028         const ok_sent = dest_info.sentinel == null or
  29029             (src_info.sentinel != null and
  29030             dest_info.sentinel.?.eql(
  29031             try mod.getCoerced(src_info.sentinel.?, dest_info.elem_type),
  29032             dest_info.elem_type,
  29033             mod,
  29034         ));
  29035         if (!ok_sent) {
  29036             return InMemoryCoercionResult{ .array_sentinel = .{
  29037                 .actual = src_info.sentinel orelse Value.@"unreachable",
  29038                 .wanted = dest_info.sentinel orelse Value.@"unreachable",
  29039                 .ty = dest_info.elem_type,
  29040             } };
  29041         }
  29042         return .ok;
  29043     }
  29044 
  29045     // Vectors
  29046     if (dest_tag == .Vector and src_tag == .Vector) {
  29047         const dest_len = dest_ty.vectorLen(mod);
  29048         const src_len = src_ty.vectorLen(mod);
  29049         if (dest_len != src_len) {
  29050             return InMemoryCoercionResult{ .vector_len = .{
  29051                 .actual = src_len,
  29052                 .wanted = dest_len,
  29053             } };
  29054         }
  29055 
  29056         const dest_elem_ty = dest_ty.scalarType(mod);
  29057         const src_elem_ty = src_ty.scalarType(mod);
  29058         const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src);
  29059         if (child != .ok) {
  29060             return InMemoryCoercionResult{ .vector_elem = .{
  29061                 .child = try child.dupe(sema.arena),
  29062                 .actual = src_elem_ty,
  29063                 .wanted = dest_elem_ty,
  29064             } };
  29065         }
  29066 
  29067         return .ok;
  29068     }
  29069 
  29070     // Arrays <-> Vectors
  29071     if ((dest_tag == .Vector and src_tag == .Array) or
  29072         (dest_tag == .Array and src_tag == .Vector))
  29073     {
  29074         const dest_len = dest_ty.arrayLen(mod);
  29075         const src_len = src_ty.arrayLen(mod);
  29076         if (dest_len != src_len) {
  29077             return InMemoryCoercionResult{ .array_len = .{
  29078                 .actual = src_len,
  29079                 .wanted = dest_len,
  29080             } };
  29081         }
  29082 
  29083         const dest_elem_ty = dest_ty.childType(mod);
  29084         const src_elem_ty = src_ty.childType(mod);
  29085         const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src);
  29086         if (child != .ok) {
  29087             return InMemoryCoercionResult{ .array_elem = .{
  29088                 .child = try child.dupe(sema.arena),
  29089                 .actual = src_elem_ty,
  29090                 .wanted = dest_elem_ty,
  29091             } };
  29092         }
  29093 
  29094         if (dest_tag == .Array) {
  29095             const dest_info = dest_ty.arrayInfo(mod);
  29096             if (dest_info.sentinel != null) {
  29097                 return InMemoryCoercionResult{ .array_sentinel = .{
  29098                     .actual = Value.@"unreachable",
  29099                     .wanted = dest_info.sentinel.?,
  29100                     .ty = dest_info.elem_type,
  29101                 } };
  29102             }
  29103         }
  29104 
  29105         // The memory layout of @Vector(N, iM) is the same as the integer type i(N*M),
  29106         // that is to say, the padding bits are not in the same place as the array [N]iM.
  29107         // If there's no padding, the bitcast is possible.
  29108         const elem_bit_size = dest_elem_ty.bitSize(mod);
  29109         const elem_abi_byte_size = dest_elem_ty.abiSize(mod);
  29110         if (elem_abi_byte_size * 8 == elem_bit_size)
  29111             return .ok;
  29112     }
  29113 
  29114     // Optionals
  29115     if (dest_tag == .Optional and src_tag == .Optional) {
  29116         if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
  29117             return InMemoryCoercionResult{ .optional_shape = .{
  29118                 .actual = src_ty,
  29119                 .wanted = dest_ty,
  29120             } };
  29121         }
  29122         const dest_child_type = dest_ty.optionalChild(mod);
  29123         const src_child_type = src_ty.optionalChild(mod);
  29124 
  29125         const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src);
  29126         if (child != .ok) {
  29127             return InMemoryCoercionResult{ .optional_child = .{
  29128                 .child = try child.dupe(sema.arena),
  29129                 .actual = src_child_type,
  29130                 .wanted = dest_child_type,
  29131             } };
  29132         }
  29133 
  29134         return .ok;
  29135     }
  29136 
  29137     // Tuples (with in-memory-coercible fields)
  29138     if (dest_ty.isTuple(mod) and src_ty.isTuple(mod)) tuple: {
  29139         if (dest_ty.containerLayout(mod) != src_ty.containerLayout(mod)) break :tuple;
  29140         if (dest_ty.structFieldCount(mod) != src_ty.structFieldCount(mod)) break :tuple;
  29141         const field_count = dest_ty.structFieldCount(mod);
  29142         for (0..field_count) |field_idx| {
  29143             if (dest_ty.structFieldIsComptime(field_idx, mod) != src_ty.structFieldIsComptime(field_idx, mod)) break :tuple;
  29144             if (dest_ty.structFieldAlign(field_idx, mod) != src_ty.structFieldAlign(field_idx, mod)) break :tuple;
  29145             const dest_field_ty = dest_ty.structFieldType(field_idx, mod);
  29146             const src_field_ty = src_ty.structFieldType(field_idx, mod);
  29147             const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src);
  29148             if (field != .ok) break :tuple;
  29149         }
  29150         return .ok;
  29151     }
  29152 
  29153     return InMemoryCoercionResult{ .no_match = .{
  29154         .actual = dest_ty,
  29155         .wanted = src_ty,
  29156     } };
  29157 }
  29158 
  29159 fn coerceInMemoryAllowedErrorSets(
  29160     sema: *Sema,
  29161     block: *Block,
  29162     dest_ty: Type,
  29163     src_ty: Type,
  29164     dest_src: LazySrcLoc,
  29165     src_src: LazySrcLoc,
  29166 ) !InMemoryCoercionResult {
  29167     const mod = sema.mod;
  29168     const gpa = sema.gpa;
  29169     const ip = &mod.intern_pool;
  29170 
  29171     // Coercion to `anyerror`. Note that this check can return false negatives
  29172     // in case the error sets did not get resolved.
  29173     if (dest_ty.isAnyError(mod)) {
  29174         return .ok;
  29175     }
  29176 
  29177     if (dest_ty.toIntern() == .adhoc_inferred_error_set_type) {
  29178         // We are trying to coerce an error set to the current function's
  29179         // inferred error set.
  29180         const dst_ies = sema.fn_ret_ty_ies.?;
  29181         try dst_ies.addErrorSet(src_ty, ip, sema.arena);
  29182         return .ok;
  29183     }
  29184 
  29185     if (ip.isInferredErrorSetType(dest_ty.toIntern())) {
  29186         const dst_ies_func_index = ip.iesFuncIndex(dest_ty.toIntern());
  29187         if (sema.fn_ret_ty_ies) |dst_ies| {
  29188             if (dst_ies.func == dst_ies_func_index) {
  29189                 // We are trying to coerce an error set to the current function's
  29190                 // inferred error set.
  29191                 try dst_ies.addErrorSet(src_ty, ip, sema.arena);
  29192                 return .ok;
  29193             }
  29194         }
  29195         switch (try sema.resolveInferredErrorSet(block, dest_src, dest_ty.toIntern())) {
  29196             // isAnyError might have changed from a false negative to a true
  29197             // positive after resolution.
  29198             .anyerror_type => return .ok,
  29199             else => {},
  29200         }
  29201     }
  29202 
  29203     var missing_error_buf = std.ArrayList(InternPool.NullTerminatedString).init(gpa);
  29204     defer missing_error_buf.deinit();
  29205 
  29206     switch (src_ty.toIntern()) {
  29207         .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) {
  29208             .simple_type => unreachable, // filtered out above
  29209             .error_set_type, .inferred_error_set_type => return .from_anyerror,
  29210             else => unreachable,
  29211         },
  29212 
  29213         else => switch (ip.indexToKey(src_ty.toIntern())) {
  29214             .inferred_error_set_type => {
  29215                 const resolved_src_ty = try sema.resolveInferredErrorSet(block, src_src, src_ty.toIntern());
  29216                 // src anyerror status might have changed after the resolution.
  29217                 if (resolved_src_ty == .anyerror_type) {
  29218                     // dest_ty.isAnyError(mod) == true is already checked for at this point.
  29219                     return .from_anyerror;
  29220                 }
  29221 
  29222                 for (ip.indexToKey(resolved_src_ty).error_set_type.names.get(ip)) |key| {
  29223                     if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) {
  29224                         try missing_error_buf.append(key);
  29225                     }
  29226                 }
  29227 
  29228                 if (missing_error_buf.items.len != 0) {
  29229                     return InMemoryCoercionResult{
  29230                         .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
  29231                     };
  29232                 }
  29233 
  29234                 return .ok;
  29235             },
  29236             .error_set_type => |error_set_type| {
  29237                 for (error_set_type.names.get(ip)) |name| {
  29238                     if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) {
  29239                         try missing_error_buf.append(name);
  29240                     }
  29241                 }
  29242 
  29243                 if (missing_error_buf.items.len != 0) {
  29244                     return InMemoryCoercionResult{
  29245                         .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
  29246                     };
  29247                 }
  29248 
  29249                 return .ok;
  29250             },
  29251             else => unreachable,
  29252         },
  29253     }
  29254 }
  29255 
  29256 fn coerceInMemoryAllowedFns(
  29257     sema: *Sema,
  29258     block: *Block,
  29259     dest_ty: Type,
  29260     src_ty: Type,
  29261     target: std.Target,
  29262     dest_src: LazySrcLoc,
  29263     src_src: LazySrcLoc,
  29264 ) !InMemoryCoercionResult {
  29265     const mod = sema.mod;
  29266     const ip = &mod.intern_pool;
  29267 
  29268     const dest_info = mod.typeToFunc(dest_ty).?;
  29269     const src_info = mod.typeToFunc(src_ty).?;
  29270 
  29271     {
  29272         if (dest_info.is_var_args != src_info.is_var_args) {
  29273             return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args };
  29274         }
  29275 
  29276         if (dest_info.is_generic != src_info.is_generic) {
  29277             return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic };
  29278         }
  29279 
  29280         if (dest_info.cc != src_info.cc) {
  29281             return InMemoryCoercionResult{ .fn_cc = .{
  29282                 .actual = src_info.cc,
  29283                 .wanted = dest_info.cc,
  29284             } };
  29285         }
  29286 
  29287         switch (src_info.return_type) {
  29288             .noreturn_type, .generic_poison_type => {},
  29289             else => {
  29290                 const dest_return_type = Type.fromInterned(dest_info.return_type);
  29291                 const src_return_type = Type.fromInterned(src_info.return_type);
  29292                 const rt = try sema.coerceInMemoryAllowed(block, dest_return_type, src_return_type, false, target, dest_src, src_src);
  29293                 if (rt != .ok) {
  29294                     return InMemoryCoercionResult{ .fn_return_type = .{
  29295                         .child = try rt.dupe(sema.arena),
  29296                         .actual = src_return_type,
  29297                         .wanted = dest_return_type,
  29298                     } };
  29299                 }
  29300             },
  29301         }
  29302     }
  29303 
  29304     const params_len = params_len: {
  29305         if (dest_info.param_types.len != src_info.param_types.len) {
  29306             return InMemoryCoercionResult{ .fn_param_count = .{
  29307                 .actual = src_info.param_types.len,
  29308                 .wanted = dest_info.param_types.len,
  29309             } };
  29310         }
  29311 
  29312         if (dest_info.noalias_bits != src_info.noalias_bits) {
  29313             return InMemoryCoercionResult{ .fn_param_noalias = .{
  29314                 .actual = src_info.noalias_bits,
  29315                 .wanted = dest_info.noalias_bits,
  29316             } };
  29317         }
  29318 
  29319         break :params_len dest_info.param_types.len;
  29320     };
  29321 
  29322     for (0..params_len) |param_i| {
  29323         const dest_param_ty = Type.fromInterned(dest_info.param_types.get(ip)[param_i]);
  29324         const src_param_ty = Type.fromInterned(src_info.param_types.get(ip)[param_i]);
  29325 
  29326         const param_i_small: u5 = @intCast(param_i);
  29327         if (dest_info.paramIsComptime(param_i_small) != src_info.paramIsComptime(param_i_small)) {
  29328             return InMemoryCoercionResult{ .fn_param_comptime = .{
  29329                 .index = param_i,
  29330                 .wanted = dest_info.paramIsComptime(param_i_small),
  29331             } };
  29332         }
  29333 
  29334         switch (src_param_ty.toIntern()) {
  29335             .generic_poison_type => {},
  29336             else => {
  29337                 // Note: Cast direction is reversed here.
  29338                 const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src);
  29339                 if (param != .ok) {
  29340                     return InMemoryCoercionResult{ .fn_param = .{
  29341                         .child = try param.dupe(sema.arena),
  29342                         .actual = src_param_ty,
  29343                         .wanted = dest_param_ty,
  29344                         .index = param_i,
  29345                     } };
  29346                 }
  29347             },
  29348         }
  29349     }
  29350 
  29351     return .ok;
  29352 }
  29353 
  29354 fn coerceInMemoryAllowedPtrs(
  29355     sema: *Sema,
  29356     block: *Block,
  29357     dest_ty: Type,
  29358     src_ty: Type,
  29359     dest_ptr_ty: Type,
  29360     src_ptr_ty: Type,
  29361     dest_is_mut: bool,
  29362     target: std.Target,
  29363     dest_src: LazySrcLoc,
  29364     src_src: LazySrcLoc,
  29365 ) !InMemoryCoercionResult {
  29366     const mod = sema.mod;
  29367     const dest_info = dest_ptr_ty.ptrInfo(mod);
  29368     const src_info = src_ptr_ty.ptrInfo(mod);
  29369 
  29370     const ok_ptr_size = src_info.flags.size == dest_info.flags.size or
  29371         src_info.flags.size == .C or dest_info.flags.size == .C;
  29372     if (!ok_ptr_size) {
  29373         return InMemoryCoercionResult{ .ptr_size = .{
  29374             .actual = src_info.flags.size,
  29375             .wanted = dest_info.flags.size,
  29376         } };
  29377     }
  29378 
  29379     const ok_cv_qualifiers =
  29380         (!src_info.flags.is_const or dest_info.flags.is_const) and
  29381         (!src_info.flags.is_volatile or dest_info.flags.is_volatile);
  29382 
  29383     if (!ok_cv_qualifiers) {
  29384         return InMemoryCoercionResult{ .ptr_qualifiers = .{
  29385             .actual_const = src_info.flags.is_const,
  29386             .wanted_const = dest_info.flags.is_const,
  29387             .actual_volatile = src_info.flags.is_volatile,
  29388             .wanted_volatile = dest_info.flags.is_volatile,
  29389         } };
  29390     }
  29391 
  29392     if (dest_info.flags.address_space != src_info.flags.address_space) {
  29393         return InMemoryCoercionResult{ .ptr_addrspace = .{
  29394             .actual = src_info.flags.address_space,
  29395             .wanted = dest_info.flags.address_space,
  29396         } };
  29397     }
  29398 
  29399     const child = try sema.coerceInMemoryAllowed(block, Type.fromInterned(dest_info.child), Type.fromInterned(src_info.child), !dest_info.flags.is_const, target, dest_src, src_src);
  29400     if (child != .ok) {
  29401         return InMemoryCoercionResult{ .ptr_child = .{
  29402             .child = try child.dupe(sema.arena),
  29403             .actual = Type.fromInterned(src_info.child),
  29404             .wanted = Type.fromInterned(dest_info.child),
  29405         } };
  29406     }
  29407 
  29408     const dest_allow_zero = dest_ty.ptrAllowsZero(mod);
  29409     const src_allow_zero = src_ty.ptrAllowsZero(mod);
  29410 
  29411     const ok_allows_zero = (dest_allow_zero and
  29412         (src_allow_zero or !dest_is_mut)) or
  29413         (!dest_allow_zero and !src_allow_zero);
  29414     if (!ok_allows_zero) {
  29415         return InMemoryCoercionResult{ .ptr_allowzero = .{
  29416             .actual = src_ty,
  29417             .wanted = dest_ty,
  29418         } };
  29419     }
  29420 
  29421     if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or
  29422         src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset)
  29423     {
  29424         return InMemoryCoercionResult{ .ptr_bit_range = .{
  29425             .actual_host = src_info.packed_offset.host_size,
  29426             .wanted_host = dest_info.packed_offset.host_size,
  29427             .actual_offset = src_info.packed_offset.bit_offset,
  29428             .wanted_offset = dest_info.packed_offset.bit_offset,
  29429         } };
  29430     }
  29431 
  29432     const ok_sent = dest_info.sentinel == .none or src_info.flags.size == .C or
  29433         (src_info.sentinel != .none and
  29434         dest_info.sentinel == try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child));
  29435     if (!ok_sent) {
  29436         return InMemoryCoercionResult{ .ptr_sentinel = .{
  29437             .actual = switch (src_info.sentinel) {
  29438                 .none => Value.@"unreachable",
  29439                 else => Value.fromInterned(src_info.sentinel),
  29440             },
  29441             .wanted = switch (dest_info.sentinel) {
  29442                 .none => Value.@"unreachable",
  29443                 else => Value.fromInterned(dest_info.sentinel),
  29444             },
  29445             .ty = Type.fromInterned(dest_info.child),
  29446         } };
  29447     }
  29448 
  29449     // If both pointers have alignment 0, it means they both want ABI alignment.
  29450     // In this case, if they share the same child type, no need to resolve
  29451     // pointee type alignment. Otherwise both pointee types must have their alignment
  29452     // resolved and we compare the alignment numerically.
  29453     if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or
  29454         dest_info.child != src_info.child)
  29455     {
  29456         const src_align = if (src_info.flags.alignment != .none)
  29457             src_info.flags.alignment
  29458         else
  29459             try sema.typeAbiAlignment(Type.fromInterned(src_info.child));
  29460 
  29461         const dest_align = if (dest_info.flags.alignment != .none)
  29462             dest_info.flags.alignment
  29463         else
  29464             try sema.typeAbiAlignment(Type.fromInterned(dest_info.child));
  29465 
  29466         if (dest_align.compare(.gt, src_align)) {
  29467             return InMemoryCoercionResult{ .ptr_alignment = .{
  29468                 .actual = src_align,
  29469                 .wanted = dest_align,
  29470             } };
  29471         }
  29472     }
  29473 
  29474     return .ok;
  29475 }
  29476 
  29477 fn coerceVarArgParam(
  29478     sema: *Sema,
  29479     block: *Block,
  29480     inst: Air.Inst.Ref,
  29481     inst_src: LazySrcLoc,
  29482 ) !Air.Inst.Ref {
  29483     if (block.is_typeof) return inst;
  29484 
  29485     const mod = sema.mod;
  29486     const uncasted_ty = sema.typeOf(inst);
  29487     const coerced = switch (uncasted_ty.zigTypeTag(mod)) {
  29488         // TODO consider casting to c_int/f64 if they fit
  29489         .ComptimeInt, .ComptimeFloat => return sema.fail(
  29490             block,
  29491             inst_src,
  29492             "integer and float literals passed to variadic function must be casted to a fixed-size number type",
  29493             .{},
  29494         ),
  29495         .Fn => fn_ptr: {
  29496             const fn_val = try sema.resolveConstDefinedValue(block, .unneeded, inst, undefined);
  29497             const fn_decl = fn_val.pointerDecl(mod).?;
  29498             break :fn_ptr try sema.analyzeDeclRef(fn_decl);
  29499         },
  29500         .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
  29501         .Float => float: {
  29502             const target = sema.mod.getTarget();
  29503             const double_bits = target.c_type_bit_size(.double);
  29504             const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget());
  29505             if (inst_bits >= double_bits) break :float inst;
  29506             switch (double_bits) {
  29507                 32 => break :float try sema.coerce(block, Type.f32, inst, inst_src),
  29508                 64 => break :float try sema.coerce(block, Type.f64, inst, inst_src),
  29509                 else => unreachable,
  29510             }
  29511         },
  29512         else => if (uncasted_ty.isAbiInt(mod)) int: {
  29513             if (!try sema.validateExternType(uncasted_ty, .param_ty)) break :int inst;
  29514             const target = sema.mod.getTarget();
  29515             const uncasted_info = uncasted_ty.intInfo(mod);
  29516             if (uncasted_info.bits <= target.c_type_bit_size(switch (uncasted_info.signedness) {
  29517                 .signed => .int,
  29518                 .unsigned => .uint,
  29519             })) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
  29520                 .signed => Type.c_int,
  29521                 .unsigned => Type.c_uint,
  29522             }, inst, inst_src);
  29523             if (uncasted_info.bits <= target.c_type_bit_size(switch (uncasted_info.signedness) {
  29524                 .signed => .long,
  29525                 .unsigned => .ulong,
  29526             })) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
  29527                 .signed => Type.c_long,
  29528                 .unsigned => Type.c_ulong,
  29529             }, inst, inst_src);
  29530             if (uncasted_info.bits <= target.c_type_bit_size(switch (uncasted_info.signedness) {
  29531                 .signed => .longlong,
  29532                 .unsigned => .ulonglong,
  29533             })) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
  29534                 .signed => Type.c_longlong,
  29535                 .unsigned => Type.c_ulonglong,
  29536             }, inst, inst_src);
  29537             break :int inst;
  29538         } else inst,
  29539     };
  29540 
  29541     const coerced_ty = sema.typeOf(coerced);
  29542     if (!try sema.validateExternType(coerced_ty, .param_ty)) {
  29543         const msg = msg: {
  29544             const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)});
  29545             errdefer msg.destroy(sema.gpa);
  29546 
  29547             const src_decl = sema.mod.declPtr(block.src_decl);
  29548             try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl, mod), coerced_ty, .param_ty);
  29549 
  29550             try sema.addDeclaredHereNote(msg, coerced_ty);
  29551             break :msg msg;
  29552         };
  29553         return sema.failWithOwnedErrorMsg(block, msg);
  29554     }
  29555     return coerced;
  29556 }
  29557 
  29558 // TODO migrate callsites to use storePtr2 instead.
  29559 fn storePtr(
  29560     sema: *Sema,
  29561     block: *Block,
  29562     src: LazySrcLoc,
  29563     ptr: Air.Inst.Ref,
  29564     uncasted_operand: Air.Inst.Ref,
  29565 ) CompileError!void {
  29566     const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store;
  29567     return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag);
  29568 }
  29569 
  29570 fn storePtr2(
  29571     sema: *Sema,
  29572     block: *Block,
  29573     src: LazySrcLoc,
  29574     ptr: Air.Inst.Ref,
  29575     ptr_src: LazySrcLoc,
  29576     uncasted_operand: Air.Inst.Ref,
  29577     operand_src: LazySrcLoc,
  29578     air_tag: Air.Inst.Tag,
  29579 ) CompileError!void {
  29580     const mod = sema.mod;
  29581     const ptr_ty = sema.typeOf(ptr);
  29582     if (ptr_ty.isConstPtr(mod))
  29583         return sema.fail(block, ptr_src, "cannot assign to constant", .{});
  29584 
  29585     const elem_ty = ptr_ty.childType(mod);
  29586 
  29587     // To generate better code for tuples, we detect a tuple operand here, and
  29588     // analyze field loads and stores directly. This avoids an extra allocation + memcpy
  29589     // which would occur if we used `coerce`.
  29590     // However, we avoid this mechanism if the destination element type is a tuple,
  29591     // because the regular store will be better for this case.
  29592     // If the destination type is a struct we don't want this mechanism to trigger, because
  29593     // this code does not handle tuple-to-struct coercion which requires dealing with missing
  29594     // fields.
  29595     const operand_ty = sema.typeOf(uncasted_operand);
  29596     if (operand_ty.isTuple(mod) and elem_ty.zigTypeTag(mod) == .Array) {
  29597         const field_count = operand_ty.structFieldCount(mod);
  29598         var i: u32 = 0;
  29599         while (i < field_count) : (i += 1) {
  29600             const elem_src = operand_src; // TODO better source location
  29601             const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i);
  29602             const elem_index = try mod.intRef(Type.usize, i);
  29603             const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true);
  29604             try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
  29605         }
  29606         return;
  29607     }
  29608 
  29609     // TODO do the same thing for anon structs as for tuples above.
  29610     // However, beware of the need to handle missing/extra fields.
  29611 
  29612     const is_ret = air_tag == .ret_ptr;
  29613 
  29614     // Detect if we are storing an array operand to a bitcasted vector pointer.
  29615     // If so, we instead reach through the bitcasted pointer to the vector pointer,
  29616     // bitcast the array operand to a vector, and then lower this as a store of
  29617     // a vector value to a vector pointer. This generally results in better code,
  29618     // as well as working around an LLVM bug:
  29619     // https://github.com/ziglang/zig/issues/11154
  29620     if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
  29621         const vector_ty = sema.typeOf(vector_ptr).childType(mod);
  29622         const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
  29623             error.NotCoercible => unreachable,
  29624             else => |e| return e,
  29625         };
  29626         try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store);
  29627         return;
  29628     }
  29629 
  29630     const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
  29631         error.NotCoercible => unreachable,
  29632         else => |e| return e,
  29633     };
  29634     const maybe_operand_val = try sema.resolveValue(operand);
  29635 
  29636     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  29637         const operand_val = maybe_operand_val orelse {
  29638             try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src);
  29639             break :rs operand_src;
  29640         };
  29641         if (ptr_val.isComptimeMutablePtr(mod)) {
  29642             try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
  29643             return;
  29644         } else break :rs ptr_src;
  29645     } else ptr_src;
  29646 
  29647     // We do this after the possible comptime store above, for the case of field_ptr stores
  29648     // to unions because we want the comptime tag to be set, even if the field type is void.
  29649     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
  29650         return;
  29651     }
  29652 
  29653     try sema.requireRuntimeBlock(block, src, runtime_src);
  29654     try sema.queueFullTypeResolution(elem_ty);
  29655 
  29656     if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) {
  29657         const ptr_inst = ptr.toIndex().?;
  29658         const air_tags = sema.air_instructions.items(.tag);
  29659         if (air_tags[@intFromEnum(ptr_inst)] == .ptr_elem_ptr) {
  29660             const ty_pl = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].ty_pl;
  29661             const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data;
  29662             _ = try block.addInst(.{
  29663                 .tag = .vector_store_elem,
  29664                 .data = .{ .vector_store_elem = .{
  29665                     .vector_ptr = bin_op.lhs,
  29666                     .payload = try block.sema.addExtra(Air.Bin{
  29667                         .lhs = bin_op.rhs,
  29668                         .rhs = operand,
  29669                     }),
  29670                 } },
  29671             });
  29672             return;
  29673         }
  29674         return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{
  29675             ptr_ty.fmt(sema.mod),
  29676         });
  29677     }
  29678 
  29679     const store_inst = if (is_ret)
  29680         try block.addBinOp(.store, ptr, operand)
  29681     else
  29682         try block.addBinOp(air_tag, ptr, operand);
  29683 
  29684     try sema.checkComptimeKnownStore(block, store_inst);
  29685 
  29686     return;
  29687 }
  29688 
  29689 /// Given an AIR store instruction, checks whether we are performing a
  29690 /// comptime-known store to a local alloc, and updates `maybe_comptime_allocs`
  29691 /// accordingly.
  29692 fn checkComptimeKnownStore(sema: *Sema, block: *Block, store_inst_ref: Air.Inst.Ref) !void {
  29693     const store_inst = store_inst_ref.toIndex().?;
  29694     const inst_data = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op;
  29695     const ptr = inst_data.lhs.toIndex() orelse return;
  29696     const operand = inst_data.rhs;
  29697 
  29698     const maybe_base_alloc = sema.base_allocs.get(ptr) orelse return;
  29699     const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(maybe_base_alloc) orelse return;
  29700 
  29701     ct: {
  29702         if (null == try sema.resolveValue(operand)) break :ct;
  29703         if (maybe_comptime_alloc.runtime_index != block.runtime_index) break :ct;
  29704         return maybe_comptime_alloc.stores.append(sema.arena, store_inst);
  29705     }
  29706 
  29707     // Store is runtime-known
  29708     _ = sema.maybe_comptime_allocs.remove(maybe_base_alloc);
  29709 }
  29710 
  29711 /// Given an AIR instruction transforming a pointer (struct_field_ptr,
  29712 /// ptr_elem_ptr, bitcast, etc), checks whether the base pointer refers to a
  29713 /// local alloc, and updates `base_allocs` accordingly.
  29714 fn checkKnownAllocPtr(sema: *Sema, base_ptr: Air.Inst.Ref, new_ptr: Air.Inst.Ref) !void {
  29715     const base_ptr_inst = base_ptr.toIndex() orelse return;
  29716     const new_ptr_inst = new_ptr.toIndex() orelse return;
  29717     const alloc_inst = sema.base_allocs.get(base_ptr_inst) orelse return;
  29718     try sema.base_allocs.put(sema.gpa, new_ptr_inst, alloc_inst);
  29719 
  29720     switch (sema.air_instructions.items(.tag)[@intFromEnum(new_ptr_inst)]) {
  29721         .optional_payload_ptr_set, .errunion_payload_ptr_set => {
  29722             const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(alloc_inst) orelse return;
  29723             try maybe_comptime_alloc.non_elideable_pointers.append(sema.arena, new_ptr_inst);
  29724         },
  29725         .ptr_elem_ptr => {
  29726             const tmp_air = sema.getTmpAir();
  29727             const pl_idx = tmp_air.instructions.items(.data)[@intFromEnum(new_ptr_inst)].ty_pl.payload;
  29728             const bin = tmp_air.extraData(Air.Bin, pl_idx).data;
  29729             const index_ref = bin.rhs;
  29730 
  29731             // If the index value is runtime-known, this pointer is also runtime-known, so
  29732             // we must in turn make the alloc value runtime-known.
  29733             if (null == try sema.resolveValue(index_ref)) {
  29734                 _ = sema.maybe_comptime_allocs.remove(alloc_inst);
  29735             }
  29736         },
  29737         else => {},
  29738     }
  29739 }
  29740 
  29741 /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector
  29742 /// pointer. Only if the final element type matches the vector element type, and the
  29743 /// lengths match.
  29744 fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref {
  29745     const mod = sema.mod;
  29746     const array_ty = sema.typeOf(ptr).childType(mod);
  29747     if (array_ty.zigTypeTag(mod) != .Array) return null;
  29748     var ptr_ref = ptr;
  29749     var ptr_inst = ptr_ref.toIndex() orelse return null;
  29750     const air_datas = sema.air_instructions.items(.data);
  29751     const air_tags = sema.air_instructions.items(.tag);
  29752     const vector_ty = while (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
  29753         ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
  29754         if (!sema.isKnownZigType(ptr_ref, .Pointer)) return null;
  29755         const child_ty = sema.typeOf(ptr_ref).childType(mod);
  29756         if (child_ty.zigTypeTag(mod) == .Vector) break child_ty;
  29757         ptr_inst = ptr_ref.toIndex() orelse return null;
  29758     } else return null;
  29759 
  29760     // We have a pointer-to-array and a pointer-to-vector. If the elements and
  29761     // lengths match, return the result.
  29762     if (array_ty.childType(mod).eql(vector_ty.childType(mod), sema.mod) and
  29763         array_ty.arrayLen(mod) == vector_ty.vectorLen(mod))
  29764     {
  29765         return ptr_ref;
  29766     } else {
  29767         return null;
  29768     }
  29769 }
  29770 
  29771 /// Call when you have Value objects rather than Air instructions, and you want to
  29772 /// assert the store must be done at comptime.
  29773 fn storePtrVal(
  29774     sema: *Sema,
  29775     block: *Block,
  29776     src: LazySrcLoc,
  29777     ptr_val: Value,
  29778     operand_val: Value,
  29779     operand_ty: Type,
  29780 ) !void {
  29781     const mod = sema.mod;
  29782     var mut_kit = try sema.beginComptimePtrMutation(block, src, ptr_val, operand_ty);
  29783     try sema.checkComptimeVarStore(block, src, mut_kit.mut_decl);
  29784 
  29785     switch (mut_kit.pointee) {
  29786         .opv => {},
  29787         .direct => |val_ptr| {
  29788             if (mut_kit.mut_decl.runtime_index == .comptime_field_ptr) {
  29789                 val_ptr.* = Value.fromInterned((try val_ptr.intern(operand_ty, mod)));
  29790                 if (!operand_val.eql(val_ptr.*, operand_ty, mod)) {
  29791                     // TODO use failWithInvalidComptimeFieldStore
  29792                     return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});
  29793                 }
  29794                 return;
  29795             }
  29796             val_ptr.* = Value.fromInterned((try operand_val.intern(operand_ty, mod)));
  29797         },
  29798         .reinterpret => |reinterpret| {
  29799             const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(mod));
  29800             const buffer = try sema.gpa.alloc(u8, abi_size);
  29801             defer sema.gpa.free(buffer);
  29802             reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, mod, buffer) catch |err| switch (err) {
  29803                 error.OutOfMemory => return error.OutOfMemory,
  29804                 error.ReinterpretDeclRef => unreachable,
  29805                 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
  29806                 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
  29807             };
  29808             if (reinterpret.write_packed) {
  29809                 operand_val.writeToPackedMemory(operand_ty, mod, buffer[reinterpret.byte_offset..], 0) catch |err| switch (err) {
  29810                     error.OutOfMemory => return error.OutOfMemory,
  29811                     error.ReinterpretDeclRef => unreachable,
  29812                 };
  29813             } else {
  29814                 operand_val.writeToMemory(operand_ty, mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) {
  29815                     error.OutOfMemory => return error.OutOfMemory,
  29816                     error.ReinterpretDeclRef => unreachable,
  29817                     error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
  29818                     error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{operand_ty.fmt(mod)}),
  29819                 };
  29820             }
  29821             const val = Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena) catch |err| switch (err) {
  29822                 error.OutOfMemory => return error.OutOfMemory,
  29823                 error.IllDefinedMemoryLayout => unreachable,
  29824                 error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
  29825             };
  29826             reinterpret.val_ptr.* = Value.fromInterned((try val.intern(mut_kit.ty, mod)));
  29827         },
  29828         .bad_decl_ty, .bad_ptr_ty => {
  29829             // TODO show the decl declaration site in a note and explain whether the decl
  29830             // or the pointer is the problematic type
  29831             return sema.fail(
  29832                 block,
  29833                 src,
  29834                 "comptime mutation of a reinterpreted pointer requires type '{}' to have a well-defined memory layout",
  29835                 .{mut_kit.ty.fmt(mod)},
  29836             );
  29837         },
  29838     }
  29839 }
  29840 
  29841 const ComptimePtrMutationKit = struct {
  29842     mut_decl: InternPool.Key.Ptr.Addr.MutDecl,
  29843     pointee: union(enum) {
  29844         opv,
  29845         /// The pointer type matches the actual comptime Value so a direct
  29846         /// modification is possible.
  29847         direct: *Value,
  29848         /// The largest parent Value containing pointee and having a well-defined memory layout.
  29849         /// This is used for bitcasting, if direct dereferencing failed.
  29850         reinterpret: struct {
  29851             val_ptr: *Value,
  29852             byte_offset: usize,
  29853             /// If set, write the operand to packed memory
  29854             write_packed: bool = false,
  29855         },
  29856         /// If the root decl could not be used as parent, this means `ty` is the type that
  29857         /// caused that by not having a well-defined layout.
  29858         /// This one means the Decl that owns the value trying to be modified does not
  29859         /// have a well defined memory layout.
  29860         bad_decl_ty,
  29861         /// If the root decl could not be used as parent, this means `ty` is the type that
  29862         /// caused that by not having a well-defined layout.
  29863         /// This one means the pointer type that is being stored through does not
  29864         /// have a well defined memory layout.
  29865         bad_ptr_ty,
  29866     },
  29867     ty: Type,
  29868 };
  29869 
  29870 fn beginComptimePtrMutation(
  29871     sema: *Sema,
  29872     block: *Block,
  29873     src: LazySrcLoc,
  29874     ptr_val: Value,
  29875     ptr_elem_ty: Type,
  29876 ) CompileError!ComptimePtrMutationKit {
  29877     const mod = sema.mod;
  29878     const ptr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr;
  29879     switch (ptr.addr) {
  29880         .decl, .anon_decl, .int => unreachable, // isComptimeMutablePtr has been checked already
  29881         .mut_decl => |mut_decl| {
  29882             const decl = mod.declPtr(mut_decl.decl);
  29883             return sema.beginComptimePtrMutationInner(block, src, decl.ty, &decl.val, ptr_elem_ty, mut_decl);
  29884         },
  29885         .comptime_field => |comptime_field| {
  29886             const duped = try sema.arena.create(Value);
  29887             duped.* = Value.fromInterned(comptime_field);
  29888             return sema.beginComptimePtrMutationInner(block, src, Type.fromInterned(mod.intern_pool.typeOf(comptime_field)), duped, ptr_elem_ty, .{
  29889                 .decl = undefined,
  29890                 .runtime_index = .comptime_field_ptr,
  29891             });
  29892         },
  29893         .eu_payload => |eu_ptr| {
  29894             const eu_ty = Type.fromInterned(mod.intern_pool.typeOf(eu_ptr)).childType(mod);
  29895             var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(eu_ptr), eu_ty);
  29896             switch (parent.pointee) {
  29897                 .opv => unreachable,
  29898                 .direct => |val_ptr| {
  29899                     const payload_ty = parent.ty.errorUnionPayload(mod);
  29900                     if (val_ptr.ip_index == .none and val_ptr.tag() == .eu_payload) {
  29901                         return ComptimePtrMutationKit{
  29902                             .mut_decl = parent.mut_decl,
  29903                             .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data },
  29904                             .ty = payload_ty,
  29905                         };
  29906                     } else {
  29907                         // An error union has been initialized to undefined at comptime and now we
  29908                         // are for the first time setting the payload. We must change the
  29909                         // representation of the error union from `undef` to `opt_payload`.
  29910 
  29911                         const payload = try sema.arena.create(Value.Payload.SubValue);
  29912                         payload.* = .{
  29913                             .base = .{ .tag = .eu_payload },
  29914                             .data = Value.fromInterned((try mod.intern(.{ .undef = payload_ty.toIntern() }))),
  29915                         };
  29916 
  29917                         val_ptr.* = Value.initPayload(&payload.base);
  29918 
  29919                         return ComptimePtrMutationKit{
  29920                             .mut_decl = parent.mut_decl,
  29921                             .pointee = .{ .direct = &payload.data },
  29922                             .ty = payload_ty,
  29923                         };
  29924                     }
  29925                 },
  29926                 .bad_decl_ty, .bad_ptr_ty => return parent,
  29927                 // Even though the parent value type has well-defined memory layout, our
  29928                 // pointer type does not.
  29929                 .reinterpret => return ComptimePtrMutationKit{
  29930                     .mut_decl = parent.mut_decl,
  29931                     .pointee = .bad_ptr_ty,
  29932                     .ty = eu_ty,
  29933                 },
  29934             }
  29935         },
  29936         .opt_payload => |opt_ptr| {
  29937             const opt_ty = Type.fromInterned(mod.intern_pool.typeOf(opt_ptr)).childType(mod);
  29938             var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(opt_ptr), opt_ty);
  29939             switch (parent.pointee) {
  29940                 .opv => unreachable,
  29941                 .direct => |val_ptr| {
  29942                     const payload_ty = parent.ty.optionalChild(mod);
  29943                     switch (val_ptr.ip_index) {
  29944                         .none => return ComptimePtrMutationKit{
  29945                             .mut_decl = parent.mut_decl,
  29946                             .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data },
  29947                             .ty = payload_ty,
  29948                         },
  29949                         else => {
  29950                             const payload_val = switch (mod.intern_pool.indexToKey(val_ptr.ip_index)) {
  29951                                 .undef => try mod.intern(.{ .undef = payload_ty.toIntern() }),
  29952                                 .opt => |opt| switch (opt.val) {
  29953                                     .none => try mod.intern(.{ .undef = payload_ty.toIntern() }),
  29954                                     else => |payload| payload,
  29955                                 },
  29956                                 else => unreachable,
  29957                             };
  29958 
  29959                             // An optional has been initialized to undefined at comptime and now we
  29960                             // are for the first time setting the payload. We must change the
  29961                             // representation of the optional from `undef` to `opt_payload`.
  29962 
  29963                             const payload = try sema.arena.create(Value.Payload.SubValue);
  29964                             payload.* = .{
  29965                                 .base = .{ .tag = .opt_payload },
  29966                                 .data = Value.fromInterned(payload_val),
  29967                             };
  29968 
  29969                             val_ptr.* = Value.initPayload(&payload.base);
  29970 
  29971                             return ComptimePtrMutationKit{
  29972                                 .mut_decl = parent.mut_decl,
  29973                                 .pointee = .{ .direct = &payload.data },
  29974                                 .ty = payload_ty,
  29975                             };
  29976                         },
  29977                     }
  29978                 },
  29979                 .bad_decl_ty, .bad_ptr_ty => return parent,
  29980                 // Even though the parent value type has well-defined memory layout, our
  29981                 // pointer type does not.
  29982                 .reinterpret => return ComptimePtrMutationKit{
  29983                     .mut_decl = parent.mut_decl,
  29984                     .pointee = .bad_ptr_ty,
  29985                     .ty = opt_ty,
  29986                 },
  29987             }
  29988         },
  29989         .elem => |elem_ptr| {
  29990             const base_elem_ty = Type.fromInterned(mod.intern_pool.typeOf(elem_ptr.base)).elemType2(mod);
  29991             var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(elem_ptr.base), base_elem_ty);
  29992 
  29993             switch (parent.pointee) {
  29994                 .opv => unreachable,
  29995                 .direct => |val_ptr| switch (parent.ty.zigTypeTag(mod)) {
  29996                     .Array, .Vector => {
  29997                         const elem_ty = parent.ty.childType(mod);
  29998                         const check_len = parent.ty.arrayLenIncludingSentinel(mod);
  29999                         if ((try sema.typeHasOnePossibleValue(ptr_elem_ty)) != null) {
  30000                             if (elem_ptr.index > check_len) {
  30001                                 // TODO have the parent include the decl so we can say "declared here"
  30002                                 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
  30003                                     elem_ptr.index, check_len,
  30004                                 });
  30005                             }
  30006                             return .{
  30007                                 .mut_decl = parent.mut_decl,
  30008                                 .pointee = .opv,
  30009                                 .ty = elem_ty,
  30010                             };
  30011                         }
  30012                         if (elem_ptr.index >= check_len) {
  30013                             // TODO have the parent include the decl so we can say "declared here"
  30014                             return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
  30015                                 elem_ptr.index, check_len,
  30016                             });
  30017                         }
  30018 
  30019                         // We might have a pointer to multiple elements of the array (e.g. a pointer
  30020                         // to a sub-array). In this case, we just have to reinterpret the relevant
  30021                         // bytes of the whole array rather than any single element.
  30022                         reinterp_multi_elem: {
  30023                             if (try sema.typeRequiresComptime(base_elem_ty)) break :reinterp_multi_elem;
  30024                             if (try sema.typeRequiresComptime(ptr_elem_ty)) break :reinterp_multi_elem;
  30025 
  30026                             const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty);
  30027                             if (elem_abi_size_u64 >= try sema.typeAbiSize(ptr_elem_ty)) break :reinterp_multi_elem;
  30028 
  30029                             const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
  30030                             const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
  30031                             return .{
  30032                                 .mut_decl = parent.mut_decl,
  30033                                 .pointee = .{ .reinterpret = .{
  30034                                     .val_ptr = val_ptr,
  30035                                     .byte_offset = elem_abi_size * elem_idx,
  30036                                 } },
  30037                                 .ty = parent.ty,
  30038                             };
  30039                         }
  30040 
  30041                         switch (val_ptr.ip_index) {
  30042                             .none => switch (val_ptr.tag()) {
  30043                                 .bytes => {
  30044                                     // An array is memory-optimized to store a slice of bytes, but we are about
  30045                                     // to modify an individual field and the representation has to change.
  30046                                     // If we wanted to avoid this, there would need to be special detection
  30047                                     // elsewhere to identify when writing a value to an array element that is stored
  30048                                     // using the `bytes` tag, and handle it without making a call to this function.
  30049                                     const arena = mod.tmp_hack_arena.allocator();
  30050 
  30051                                     const bytes = val_ptr.castTag(.bytes).?.data;
  30052                                     const dest_len = parent.ty.arrayLenIncludingSentinel(mod);
  30053                                     // bytes.len may be one greater than dest_len because of the case when
  30054                                     // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
  30055                                     assert(bytes.len >= dest_len);
  30056                                     const elems = try arena.alloc(Value, @intCast(dest_len));
  30057                                     for (elems, 0..) |*elem, i| {
  30058                                         elem.* = try mod.intValue(elem_ty, bytes[i]);
  30059                                     }
  30060 
  30061                                     val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  30062 
  30063                                     return beginComptimePtrMutationInner(
  30064                                         sema,
  30065                                         block,
  30066                                         src,
  30067                                         elem_ty,
  30068                                         &elems[@intCast(elem_ptr.index)],
  30069                                         ptr_elem_ty,
  30070                                         parent.mut_decl,
  30071                                     );
  30072                                 },
  30073                                 .repeated => {
  30074                                     // An array is memory-optimized to store only a single element value, and
  30075                                     // that value is understood to be the same for the entire length of the array.
  30076                                     // However, now we want to modify an individual field and so the
  30077                                     // representation has to change.  If we wanted to avoid this, there would
  30078                                     // need to be special detection elsewhere to identify when writing a value to an
  30079                                     // array element that is stored using the `repeated` tag, and handle it
  30080                                     // without making a call to this function.
  30081                                     const arena = mod.tmp_hack_arena.allocator();
  30082 
  30083                                     const repeated_val = try val_ptr.castTag(.repeated).?.data.intern(parent.ty.childType(mod), mod);
  30084                                     const array_len_including_sentinel =
  30085                                         try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
  30086                                     const elems = try arena.alloc(Value, array_len_including_sentinel);
  30087                                     @memset(elems, Value.fromInterned(repeated_val));
  30088 
  30089                                     val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  30090 
  30091                                     return beginComptimePtrMutationInner(
  30092                                         sema,
  30093                                         block,
  30094                                         src,
  30095                                         elem_ty,
  30096                                         &elems[@intCast(elem_ptr.index)],
  30097                                         ptr_elem_ty,
  30098                                         parent.mut_decl,
  30099                                     );
  30100                                 },
  30101 
  30102                                 .aggregate => return beginComptimePtrMutationInner(
  30103                                     sema,
  30104                                     block,
  30105                                     src,
  30106                                     elem_ty,
  30107                                     &val_ptr.castTag(.aggregate).?.data[@intCast(elem_ptr.index)],
  30108                                     ptr_elem_ty,
  30109                                     parent.mut_decl,
  30110                                 ),
  30111 
  30112                                 else => unreachable,
  30113                             },
  30114                             else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
  30115                                 .undef => {
  30116                                     // An array has been initialized to undefined at comptime and now we
  30117                                     // are for the first time setting an element. We must change the representation
  30118                                     // of the array from `undef` to `array`.
  30119                                     const arena = mod.tmp_hack_arena.allocator();
  30120 
  30121                                     const array_len_including_sentinel =
  30122                                         try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
  30123                                     const elems = try arena.alloc(Value, array_len_including_sentinel);
  30124                                     @memset(elems, Value.fromInterned((try mod.intern(.{ .undef = elem_ty.toIntern() }))));
  30125 
  30126                                     val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  30127 
  30128                                     return beginComptimePtrMutationInner(
  30129                                         sema,
  30130                                         block,
  30131                                         src,
  30132                                         elem_ty,
  30133                                         &elems[@intCast(elem_ptr.index)],
  30134                                         ptr_elem_ty,
  30135                                         parent.mut_decl,
  30136                                     );
  30137                                 },
  30138                                 else => unreachable,
  30139                             },
  30140                         }
  30141                     },
  30142                     else => {
  30143                         if (elem_ptr.index != 0) {
  30144                             // TODO include a "declared here" note for the decl
  30145                             return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
  30146                                 elem_ptr.index,
  30147                             });
  30148                         }
  30149                         return beginComptimePtrMutationInner(
  30150                             sema,
  30151                             block,
  30152                             src,
  30153                             parent.ty,
  30154                             val_ptr,
  30155                             ptr_elem_ty,
  30156                             parent.mut_decl,
  30157                         );
  30158                     },
  30159                 },
  30160                 .reinterpret => |reinterpret| {
  30161                     if (!base_elem_ty.hasWellDefinedLayout(mod)) {
  30162                         // Even though the parent value type has well-defined memory layout, our
  30163                         // pointer type does not.
  30164                         return ComptimePtrMutationKit{
  30165                             .mut_decl = parent.mut_decl,
  30166                             .pointee = .bad_ptr_ty,
  30167                             .ty = base_elem_ty,
  30168                         };
  30169                     }
  30170 
  30171                     const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty);
  30172                     const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
  30173                     const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
  30174                     return ComptimePtrMutationKit{
  30175                         .mut_decl = parent.mut_decl,
  30176                         .pointee = .{ .reinterpret = .{
  30177                             .val_ptr = reinterpret.val_ptr,
  30178                             .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_idx,
  30179                         } },
  30180                         .ty = parent.ty,
  30181                     };
  30182                 },
  30183                 .bad_decl_ty, .bad_ptr_ty => return parent,
  30184             }
  30185         },
  30186         .field => |field_ptr| {
  30187             const base_child_ty = Type.fromInterned(mod.intern_pool.typeOf(field_ptr.base)).childType(mod);
  30188             const field_index: u32 = @intCast(field_ptr.index);
  30189 
  30190             var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(field_ptr.base), base_child_ty);
  30191             switch (parent.pointee) {
  30192                 .opv => unreachable,
  30193                 .direct => |val_ptr| switch (val_ptr.ip_index) {
  30194                     .empty_struct => {
  30195                         const duped = try sema.arena.create(Value);
  30196                         duped.* = val_ptr.*;
  30197                         return beginComptimePtrMutationInner(
  30198                             sema,
  30199                             block,
  30200                             src,
  30201                             parent.ty.structFieldType(field_index, mod),
  30202                             duped,
  30203                             ptr_elem_ty,
  30204                             parent.mut_decl,
  30205                         );
  30206                     },
  30207                     .none => switch (val_ptr.tag()) {
  30208                         .aggregate => return beginComptimePtrMutationInner(
  30209                             sema,
  30210                             block,
  30211                             src,
  30212                             parent.ty.structFieldType(field_index, mod),
  30213                             &val_ptr.castTag(.aggregate).?.data[field_index],
  30214                             ptr_elem_ty,
  30215                             parent.mut_decl,
  30216                         ),
  30217                         .repeated => {
  30218                             const arena = mod.tmp_hack_arena.allocator();
  30219 
  30220                             const elems = try arena.alloc(Value, parent.ty.structFieldCount(mod));
  30221                             @memset(elems, val_ptr.castTag(.repeated).?.data);
  30222                             val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  30223 
  30224                             return beginComptimePtrMutationInner(
  30225                                 sema,
  30226                                 block,
  30227                                 src,
  30228                                 parent.ty.structFieldType(field_index, mod),
  30229                                 &elems[field_index],
  30230                                 ptr_elem_ty,
  30231                                 parent.mut_decl,
  30232                             );
  30233                         },
  30234                         .@"union" => {
  30235                             const payload = &val_ptr.castTag(.@"union").?.data;
  30236                             const layout = base_child_ty.containerLayout(mod);
  30237 
  30238                             const tag_type = base_child_ty.unionTagTypeHypothetical(mod);
  30239                             const hypothetical_tag = try mod.enumValueFieldIndex(tag_type, field_index);
  30240                             if (layout == .Auto or (payload.tag != null and hypothetical_tag.eql(payload.tag.?, tag_type, mod))) {
  30241                                 // We need to set the active field of the union.
  30242                                 payload.tag = hypothetical_tag;
  30243 
  30244                                 const field_ty = parent.ty.structFieldType(field_index, mod);
  30245                                 return beginComptimePtrMutationInner(
  30246                                     sema,
  30247                                     block,
  30248                                     src,
  30249                                     field_ty,
  30250                                     &payload.val,
  30251                                     ptr_elem_ty,
  30252                                     parent.mut_decl,
  30253                                 );
  30254                             } else {
  30255                                 // Writing to a different field (a different or unknown tag is active) requires reinterpreting
  30256                                 // memory of the entire union, which requires knowing its abiSize.
  30257                                 try sema.resolveTypeLayout(parent.ty);
  30258 
  30259                                 // This union value no longer has a well-defined tag type.
  30260                                 // The reinterpretation will read it back out as .none.
  30261                                 payload.val = try payload.val.unintern(sema.arena, mod);
  30262                                 return ComptimePtrMutationKit{
  30263                                     .mut_decl = parent.mut_decl,
  30264                                     .pointee = .{ .reinterpret = .{
  30265                                         .val_ptr = val_ptr,
  30266                                         .byte_offset = 0,
  30267                                         .write_packed = layout == .Packed,
  30268                                     } },
  30269                                     .ty = parent.ty,
  30270                                 };
  30271                             }
  30272                         },
  30273                         .slice => switch (field_index) {
  30274                             Value.slice_ptr_index => return beginComptimePtrMutationInner(
  30275                                 sema,
  30276                                 block,
  30277                                 src,
  30278                                 parent.ty.slicePtrFieldType(mod),
  30279                                 &val_ptr.castTag(.slice).?.data.ptr,
  30280                                 ptr_elem_ty,
  30281                                 parent.mut_decl,
  30282                             ),
  30283 
  30284                             Value.slice_len_index => return beginComptimePtrMutationInner(
  30285                                 sema,
  30286                                 block,
  30287                                 src,
  30288                                 Type.usize,
  30289                                 &val_ptr.castTag(.slice).?.data.len,
  30290                                 ptr_elem_ty,
  30291                                 parent.mut_decl,
  30292                             ),
  30293 
  30294                             else => unreachable,
  30295                         },
  30296                         else => unreachable,
  30297                     },
  30298                     else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
  30299                         .undef => {
  30300                             // A struct or union has been initialized to undefined at comptime and now we
  30301                             // are for the first time setting a field. We must change the representation
  30302                             // of the struct/union from `undef` to `struct`/`union`.
  30303                             const arena = mod.tmp_hack_arena.allocator();
  30304 
  30305                             switch (parent.ty.zigTypeTag(mod)) {
  30306                                 .Struct => {
  30307                                     const fields = try arena.alloc(Value, parent.ty.structFieldCount(mod));
  30308                                     for (fields, 0..) |*field, i| field.* = Value.fromInterned((try mod.intern(.{
  30309                                         .undef = parent.ty.structFieldType(i, mod).toIntern(),
  30310                                     })));
  30311 
  30312                                     val_ptr.* = try Value.Tag.aggregate.create(arena, fields);
  30313 
  30314                                     return beginComptimePtrMutationInner(
  30315                                         sema,
  30316                                         block,
  30317                                         src,
  30318                                         parent.ty.structFieldType(field_index, mod),
  30319                                         &fields[field_index],
  30320                                         ptr_elem_ty,
  30321                                         parent.mut_decl,
  30322                                     );
  30323                                 },
  30324                                 .Union => {
  30325                                     const payload = try arena.create(Value.Payload.Union);
  30326                                     const tag_ty = parent.ty.unionTagTypeHypothetical(mod);
  30327                                     const payload_ty = parent.ty.structFieldType(field_index, mod);
  30328                                     payload.* = .{ .data = .{
  30329                                         .tag = try mod.enumValueFieldIndex(tag_ty, field_index),
  30330                                         .val = Value.fromInterned((try mod.intern(.{ .undef = payload_ty.toIntern() }))),
  30331                                     } };
  30332 
  30333                                     val_ptr.* = Value.initPayload(&payload.base);
  30334 
  30335                                     return beginComptimePtrMutationInner(
  30336                                         sema,
  30337                                         block,
  30338                                         src,
  30339                                         payload_ty,
  30340                                         &payload.data.val,
  30341                                         ptr_elem_ty,
  30342                                         parent.mut_decl,
  30343                                     );
  30344                                 },
  30345                                 .Pointer => {
  30346                                     assert(parent.ty.isSlice(mod));
  30347                                     const ptr_ty = parent.ty.slicePtrFieldType(mod);
  30348                                     val_ptr.* = try Value.Tag.slice.create(arena, .{
  30349                                         .ptr = Value.fromInterned((try mod.intern(.{ .undef = ptr_ty.toIntern() }))),
  30350                                         .len = Value.fromInterned((try mod.intern(.{ .undef = .usize_type }))),
  30351                                     });
  30352 
  30353                                     switch (field_index) {
  30354                                         Value.slice_ptr_index => return beginComptimePtrMutationInner(
  30355                                             sema,
  30356                                             block,
  30357                                             src,
  30358                                             ptr_ty,
  30359                                             &val_ptr.castTag(.slice).?.data.ptr,
  30360                                             ptr_elem_ty,
  30361                                             parent.mut_decl,
  30362                                         ),
  30363                                         Value.slice_len_index => return beginComptimePtrMutationInner(
  30364                                             sema,
  30365                                             block,
  30366                                             src,
  30367                                             Type.usize,
  30368                                             &val_ptr.castTag(.slice).?.data.len,
  30369                                             ptr_elem_ty,
  30370                                             parent.mut_decl,
  30371                                         ),
  30372 
  30373                                         else => unreachable,
  30374                                     }
  30375                                 },
  30376                                 else => unreachable,
  30377                             }
  30378                         },
  30379                         else => unreachable,
  30380                     },
  30381                 },
  30382                 .reinterpret => |reinterpret| {
  30383                     const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod);
  30384                     const field_offset = try sema.usizeCast(block, src, field_offset_u64);
  30385                     return ComptimePtrMutationKit{
  30386                         .mut_decl = parent.mut_decl,
  30387                         .pointee = .{ .reinterpret = .{
  30388                             .val_ptr = reinterpret.val_ptr,
  30389                             .byte_offset = reinterpret.byte_offset + field_offset,
  30390                         } },
  30391                         .ty = parent.ty,
  30392                     };
  30393                 },
  30394                 .bad_decl_ty, .bad_ptr_ty => return parent,
  30395             }
  30396         },
  30397     }
  30398 }
  30399 
  30400 fn beginComptimePtrMutationInner(
  30401     sema: *Sema,
  30402     block: *Block,
  30403     src: LazySrcLoc,
  30404     decl_ty: Type,
  30405     decl_val: *Value,
  30406     ptr_elem_ty: Type,
  30407     mut_decl: InternPool.Key.Ptr.Addr.MutDecl,
  30408 ) CompileError!ComptimePtrMutationKit {
  30409     const mod = sema.mod;
  30410     const target = mod.getTarget();
  30411     const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok;
  30412 
  30413     decl_val.* = try decl_val.unintern(sema.arena, mod);
  30414 
  30415     if (coerce_ok) {
  30416         return ComptimePtrMutationKit{
  30417             .mut_decl = mut_decl,
  30418             .pointee = .{ .direct = decl_val },
  30419             .ty = decl_ty,
  30420         };
  30421     }
  30422 
  30423     // Handle the case that the decl is an array and we're actually trying to point to an element.
  30424     if (decl_ty.isArrayOrVector(mod)) {
  30425         const decl_elem_ty = decl_ty.childType(mod);
  30426         if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) {
  30427             return ComptimePtrMutationKit{
  30428                 .mut_decl = mut_decl,
  30429                 .pointee = .{ .direct = decl_val },
  30430                 .ty = decl_ty,
  30431             };
  30432         }
  30433     }
  30434 
  30435     if (!decl_ty.hasWellDefinedLayout(mod)) {
  30436         return ComptimePtrMutationKit{
  30437             .mut_decl = mut_decl,
  30438             .pointee = .bad_decl_ty,
  30439             .ty = decl_ty,
  30440         };
  30441     }
  30442     if (!ptr_elem_ty.hasWellDefinedLayout(mod)) {
  30443         return ComptimePtrMutationKit{
  30444             .mut_decl = mut_decl,
  30445             .pointee = .bad_ptr_ty,
  30446             .ty = ptr_elem_ty,
  30447         };
  30448     }
  30449     return ComptimePtrMutationKit{
  30450         .mut_decl = mut_decl,
  30451         .pointee = .{ .reinterpret = .{
  30452             .val_ptr = decl_val,
  30453             .byte_offset = 0,
  30454         } },
  30455         .ty = decl_ty,
  30456     };
  30457 }
  30458 
  30459 const TypedValueAndOffset = struct {
  30460     tv: TypedValue,
  30461     byte_offset: usize,
  30462 };
  30463 
  30464 const ComptimePtrLoadKit = struct {
  30465     /// The Value and Type corresponding to the pointee of the provided pointer.
  30466     /// If a direct dereference is not possible, this is null.
  30467     pointee: ?TypedValue,
  30468     /// The largest parent Value containing `pointee` and having a well-defined memory layout.
  30469     /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null).
  30470     parent: ?TypedValueAndOffset,
  30471     /// Whether the `pointee` could be mutated by further
  30472     /// semantic analysis and a copy must be performed.
  30473     is_mutable: bool,
  30474     /// If the root decl could not be used as `parent`, this is the type that
  30475     /// caused that by not having a well-defined layout
  30476     ty_without_well_defined_layout: ?Type,
  30477 };
  30478 
  30479 const ComptimePtrLoadError = CompileError || error{
  30480     RuntimeLoad,
  30481 };
  30482 
  30483 /// If `maybe_array_ty` is provided, it will be used to directly dereference an
  30484 /// .elem_ptr of type T to a value of [N]T, if necessary.
  30485 fn beginComptimePtrLoad(
  30486     sema: *Sema,
  30487     block: *Block,
  30488     src: LazySrcLoc,
  30489     ptr_val: Value,
  30490     maybe_array_ty: ?Type,
  30491 ) ComptimePtrLoadError!ComptimePtrLoadKit {
  30492     const mod = sema.mod;
  30493     const ip = &mod.intern_pool;
  30494     const target = mod.getTarget();
  30495 
  30496     var deref: ComptimePtrLoadKit = switch (ip.indexToKey(ptr_val.toIntern())) {
  30497         .ptr => |ptr| switch (ptr.addr) {
  30498             .decl, .mut_decl => blk: {
  30499                 const decl_index = switch (ptr.addr) {
  30500                     .decl => |decl| decl,
  30501                     .mut_decl => |mut_decl| mut_decl.decl,
  30502                     else => unreachable,
  30503                 };
  30504                 const is_mutable = ptr.addr == .mut_decl;
  30505                 const decl = mod.declPtr(decl_index);
  30506                 const decl_tv = try decl.typedValue();
  30507                 if (decl.val.getVariable(mod) != null) return error.RuntimeLoad;
  30508 
  30509                 const layout_defined = decl.ty.hasWellDefinedLayout(mod);
  30510                 break :blk ComptimePtrLoadKit{
  30511                     .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
  30512                     .pointee = decl_tv,
  30513                     .is_mutable = is_mutable,
  30514                     .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null,
  30515                 };
  30516             },
  30517             .anon_decl => |anon_decl| blk: {
  30518                 const decl_val = anon_decl.val;
  30519                 if (Value.fromInterned(decl_val).getVariable(mod) != null) return error.RuntimeLoad;
  30520                 const decl_ty = Type.fromInterned(ip.typeOf(decl_val));
  30521                 const decl_tv: TypedValue = .{ .ty = decl_ty, .val = Value.fromInterned(decl_val) };
  30522                 const layout_defined = decl_ty.hasWellDefinedLayout(mod);
  30523                 break :blk ComptimePtrLoadKit{
  30524                     .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
  30525                     .pointee = decl_tv,
  30526                     .is_mutable = false,
  30527                     .ty_without_well_defined_layout = if (!layout_defined) decl_ty else null,
  30528                 };
  30529             },
  30530             .int => return error.RuntimeLoad,
  30531             .eu_payload, .opt_payload => |container_ptr| blk: {
  30532                 const container_ty = Type.fromInterned(ip.typeOf(container_ptr)).childType(mod);
  30533                 const payload_ty = switch (ptr.addr) {
  30534                     .eu_payload => container_ty.errorUnionPayload(mod),
  30535                     .opt_payload => container_ty.optionalChild(mod),
  30536                     else => unreachable,
  30537                 };
  30538                 var deref = try sema.beginComptimePtrLoad(block, src, Value.fromInterned(container_ptr), container_ty);
  30539 
  30540                 // eu_payload and opt_payload never have a well-defined layout
  30541                 if (deref.parent != null) {
  30542                     deref.parent = null;
  30543                     deref.ty_without_well_defined_layout = container_ty;
  30544                 }
  30545 
  30546                 if (deref.pointee) |*tv| {
  30547                     const coerce_in_mem_ok =
  30548                         (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or
  30549                         (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;
  30550                     if (coerce_in_mem_ok) {
  30551                         const payload_val = switch (tv.val.ip_index) {
  30552                             .none => tv.val.cast(Value.Payload.SubValue).?.data,
  30553                             .null_value => return sema.fail(block, src, "attempt to use null value", .{}),
  30554                             else => Value.fromInterned(switch (ip.indexToKey(tv.val.toIntern())) {
  30555                                 .error_union => |error_union| switch (error_union.val) {
  30556                                     .err_name => |err_name| return sema.fail(
  30557                                         block,
  30558                                         src,
  30559                                         "attempt to unwrap error: {}",
  30560                                         .{err_name.fmt(ip)},
  30561                                     ),
  30562                                     .payload => |payload| payload,
  30563                                 },
  30564                                 .opt => |opt| switch (opt.val) {
  30565                                     .none => return sema.fail(block, src, "attempt to use null value", .{}),
  30566                                     else => |payload| payload,
  30567                                 },
  30568                                 else => unreachable,
  30569                             }),
  30570                         };
  30571                         tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
  30572                         break :blk deref;
  30573                     }
  30574                 }
  30575                 deref.pointee = null;
  30576                 break :blk deref;
  30577             },
  30578             .comptime_field => |comptime_field| blk: {
  30579                 const field_ty = Type.fromInterned(ip.typeOf(comptime_field));
  30580                 break :blk ComptimePtrLoadKit{
  30581                     .parent = null,
  30582                     .pointee = .{ .ty = field_ty, .val = Value.fromInterned(comptime_field) },
  30583                     .is_mutable = false,
  30584                     .ty_without_well_defined_layout = field_ty,
  30585                 };
  30586             },
  30587             .elem => |elem_ptr| blk: {
  30588                 const elem_ty = Type.fromInterned(ip.typeOf(elem_ptr.base)).elemType2(mod);
  30589                 var deref = try sema.beginComptimePtrLoad(block, src, Value.fromInterned(elem_ptr.base), null);
  30590 
  30591                 // This code assumes that elem_ptrs have been "flattened" in order for direct dereference
  30592                 // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that
  30593                 // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened"
  30594                 switch (ip.indexToKey(elem_ptr.base)) {
  30595                     .ptr => |base_ptr| switch (base_ptr.addr) {
  30596                         .elem => |base_elem| assert(!Type.fromInterned(ip.typeOf(base_elem.base)).elemType2(mod).eql(elem_ty, mod)),
  30597                         else => {},
  30598                     },
  30599                     else => {},
  30600                 }
  30601 
  30602                 if (elem_ptr.index != 0) {
  30603                     if (elem_ty.hasWellDefinedLayout(mod)) {
  30604                         if (deref.parent) |*parent| {
  30605                             // Update the byte offset (in-place)
  30606                             const elem_size = try sema.typeAbiSize(elem_ty);
  30607                             const offset = parent.byte_offset + elem_size * elem_ptr.index;
  30608                             parent.byte_offset = try sema.usizeCast(block, src, offset);
  30609                         }
  30610                     } else {
  30611                         deref.parent = null;
  30612                         deref.ty_without_well_defined_layout = elem_ty;
  30613                     }
  30614                 }
  30615 
  30616                 // If we're loading an elem that was derived from a different type
  30617                 // than the true type of the underlying decl, we cannot deref directly
  30618                 const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector(mod)) x: {
  30619                     const deref_elem_ty = deref.pointee.?.ty.childType(mod);
  30620                     break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or
  30621                         (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok;
  30622                 } else false;
  30623                 if (!ty_matches) {
  30624                     deref.pointee = null;
  30625                     break :blk deref;
  30626                 }
  30627 
  30628                 var array_tv = deref.pointee.?;
  30629                 const check_len = array_tv.ty.arrayLenIncludingSentinel(mod);
  30630                 if (maybe_array_ty) |load_ty| {
  30631                     // It's possible that we're loading a [N]T, in which case we'd like to slice
  30632                     // the pointee array directly from our parent array.
  30633                     if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) {
  30634                         const len = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod));
  30635                         const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
  30636                         deref.pointee = if (elem_ptr.index + len <= check_len) TypedValue{
  30637                             .ty = try mod.arrayType(.{
  30638                                 .len = len,
  30639                                 .child = elem_ty.toIntern(),
  30640                             }),
  30641                             .val = try array_tv.val.sliceArray(mod, sema.arena, elem_idx, elem_idx + len),
  30642                         } else null;
  30643                         break :blk deref;
  30644                     }
  30645                 }
  30646 
  30647                 if (elem_ptr.index >= check_len) {
  30648                     deref.pointee = null;
  30649                     break :blk deref;
  30650                 }
  30651                 if (elem_ptr.index == check_len - 1) {
  30652                     if (array_tv.ty.sentinel(mod)) |sent| {
  30653                         deref.pointee = TypedValue{
  30654                             .ty = elem_ty,
  30655                             .val = sent,
  30656                         };
  30657                         break :blk deref;
  30658                     }
  30659                 }
  30660                 deref.pointee = TypedValue{
  30661                     .ty = elem_ty,
  30662                     .val = try array_tv.val.elemValue(mod, @intCast(elem_ptr.index)),
  30663                 };
  30664                 break :blk deref;
  30665             },
  30666             .field => |field_ptr| blk: {
  30667                 const field_index: u32 = @intCast(field_ptr.index);
  30668                 const container_ty = Type.fromInterned(ip.typeOf(field_ptr.base)).childType(mod);
  30669                 var deref = try sema.beginComptimePtrLoad(block, src, Value.fromInterned(field_ptr.base), container_ty);
  30670 
  30671                 if (container_ty.hasWellDefinedLayout(mod)) {
  30672                     const struct_obj = mod.typeToStruct(container_ty);
  30673                     if (struct_obj != null and struct_obj.?.layout == .Packed) {
  30674                         // packed structs are not byte addressable
  30675                         deref.parent = null;
  30676                     } else if (deref.parent) |*parent| {
  30677                         // Update the byte offset (in-place)
  30678                         try sema.resolveTypeLayout(container_ty);
  30679                         const field_offset = container_ty.structFieldOffset(field_index, mod);
  30680                         parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset);
  30681                     }
  30682                 } else {
  30683                     deref.parent = null;
  30684                     deref.ty_without_well_defined_layout = container_ty;
  30685                 }
  30686 
  30687                 const tv = deref.pointee orelse {
  30688                     deref.pointee = null;
  30689                     break :blk deref;
  30690                 };
  30691                 const coerce_in_mem_ok =
  30692                     (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or
  30693                     (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;
  30694                 if (!coerce_in_mem_ok) {
  30695                     deref.pointee = null;
  30696                     break :blk deref;
  30697                 }
  30698 
  30699                 if (container_ty.isSlice(mod)) {
  30700                     deref.pointee = switch (field_index) {
  30701                         Value.slice_ptr_index => TypedValue{
  30702                             .ty = container_ty.slicePtrFieldType(mod),
  30703                             .val = tv.val.slicePtr(mod),
  30704                         },
  30705                         Value.slice_len_index => TypedValue{
  30706                             .ty = Type.usize,
  30707                             .val = Value.fromInterned(ip.indexToKey(try tv.val.intern(tv.ty, mod)).ptr.len),
  30708                         },
  30709                         else => unreachable,
  30710                     };
  30711                 } else {
  30712                     const field_ty = container_ty.structFieldType(field_index, mod);
  30713                     deref.pointee = TypedValue{
  30714                         .ty = field_ty,
  30715                         .val = try tv.val.fieldValue(mod, field_index),
  30716                     };
  30717                 }
  30718                 break :blk deref;
  30719             },
  30720         },
  30721         .opt => |opt| switch (opt.val) {
  30722             .none => return sema.fail(block, src, "attempt to use null value", .{}),
  30723             else => |payload| try sema.beginComptimePtrLoad(block, src, Value.fromInterned(payload), null),
  30724         },
  30725         else => unreachable,
  30726     };
  30727 
  30728     if (deref.pointee) |tv| {
  30729         if (deref.parent == null and tv.ty.hasWellDefinedLayout(mod)) {
  30730             deref.parent = .{ .tv = tv, .byte_offset = 0 };
  30731         }
  30732     }
  30733     return deref;
  30734 }
  30735 
  30736 fn bitCast(
  30737     sema: *Sema,
  30738     block: *Block,
  30739     dest_ty: Type,
  30740     inst: Air.Inst.Ref,
  30741     inst_src: LazySrcLoc,
  30742     operand_src: ?LazySrcLoc,
  30743 ) CompileError!Air.Inst.Ref {
  30744     const mod = sema.mod;
  30745     try sema.resolveTypeLayout(dest_ty);
  30746 
  30747     const old_ty = sema.typeOf(inst);
  30748     try sema.resolveTypeLayout(old_ty);
  30749 
  30750     const dest_bits = dest_ty.bitSize(mod);
  30751     const old_bits = old_ty.bitSize(mod);
  30752 
  30753     if (old_bits != dest_bits) {
  30754         return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{
  30755             dest_ty.fmt(mod),
  30756             dest_bits,
  30757             old_ty.fmt(mod),
  30758             old_bits,
  30759         });
  30760     }
  30761 
  30762     if (try sema.resolveValue(inst)) |val| {
  30763         if (val.isUndef(mod))
  30764             return mod.undefRef(dest_ty);
  30765         if (try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0)) |result_val| {
  30766             return Air.internedToRef(result_val.toIntern());
  30767         }
  30768     }
  30769     try sema.requireRuntimeBlock(block, inst_src, operand_src);
  30770     return block.addBitCast(dest_ty, inst);
  30771 }
  30772 
  30773 fn bitCastVal(
  30774     sema: *Sema,
  30775     block: *Block,
  30776     src: LazySrcLoc,
  30777     val: Value,
  30778     old_ty: Type,
  30779     new_ty: Type,
  30780     buffer_offset: usize,
  30781 ) !?Value {
  30782     const mod = sema.mod;
  30783     if (old_ty.eql(new_ty, mod)) return val;
  30784 
  30785     // For types with well-defined memory layouts, we serialize them a byte buffer,
  30786     // then deserialize to the new type.
  30787     const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(mod));
  30788 
  30789     const buffer = try sema.gpa.alloc(u8, abi_size);
  30790     defer sema.gpa.free(buffer);
  30791     val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) {
  30792         error.OutOfMemory => return error.OutOfMemory,
  30793         error.ReinterpretDeclRef => return null,
  30794         error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
  30795         error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}),
  30796     };
  30797 
  30798     return Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena) catch |err| switch (err) {
  30799         error.OutOfMemory => return error.OutOfMemory,
  30800         error.IllDefinedMemoryLayout => unreachable,
  30801         error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{new_ty.fmt(mod)}),
  30802     };
  30803 }
  30804 
  30805 fn bitCastUnionFieldVal(
  30806     sema: *Sema,
  30807     block: *Block,
  30808     src: LazySrcLoc,
  30809     val: Value,
  30810     old_ty: Type,
  30811     field_ty: Type,
  30812     layout: std.builtin.Type.ContainerLayout,
  30813 ) !?Value {
  30814     const mod = sema.mod;
  30815     if (old_ty.eql(field_ty, mod)) return val;
  30816 
  30817     const old_size = try sema.usizeCast(block, src, old_ty.abiSize(mod));
  30818     const field_size = try sema.usizeCast(block, src, field_ty.abiSize(mod));
  30819     const endian = mod.getTarget().cpu.arch.endian();
  30820 
  30821     const buffer = try sema.gpa.alloc(u8, @max(old_size, field_size));
  30822     defer sema.gpa.free(buffer);
  30823 
  30824     // Reading a larger value means we need to reinterpret from undefined bytes.
  30825     const offset = switch (layout) {
  30826         .Extern => offset: {
  30827             if (field_size > old_size) @memset(buffer[old_size..], 0xaa);
  30828             val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) {
  30829                 error.OutOfMemory => return error.OutOfMemory,
  30830                 error.ReinterpretDeclRef => return null,
  30831                 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
  30832                 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}),
  30833             };
  30834             break :offset 0;
  30835         },
  30836         .Packed => offset: {
  30837             if (field_size > old_size) {
  30838                 const min_size = @max(old_size, 1);
  30839                 switch (endian) {
  30840                     .little => @memset(buffer[min_size - 1 ..], 0xaa),
  30841                     .big => @memset(buffer[0 .. buffer.len - min_size + 1], 0xaa),
  30842                 }
  30843             }
  30844 
  30845             val.writeToPackedMemory(old_ty, mod, buffer, 0) catch |err| switch (err) {
  30846                 error.OutOfMemory => return error.OutOfMemory,
  30847                 error.ReinterpretDeclRef => return null,
  30848             };
  30849 
  30850             break :offset if (endian == .big) buffer.len - field_size else 0;
  30851         },
  30852         .Auto => unreachable,
  30853     };
  30854 
  30855     return Value.readFromMemory(field_ty, mod, buffer[offset..], sema.arena) catch |err| switch (err) {
  30856         error.OutOfMemory => return error.OutOfMemory,
  30857         error.IllDefinedMemoryLayout => unreachable,
  30858         error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{field_ty.fmt(mod)}),
  30859     };
  30860 }
  30861 
  30862 fn coerceArrayPtrToSlice(
  30863     sema: *Sema,
  30864     block: *Block,
  30865     dest_ty: Type,
  30866     inst: Air.Inst.Ref,
  30867     inst_src: LazySrcLoc,
  30868 ) CompileError!Air.Inst.Ref {
  30869     const mod = sema.mod;
  30870     if (try sema.resolveValue(inst)) |val| {
  30871         const ptr_array_ty = sema.typeOf(inst);
  30872         const array_ty = ptr_array_ty.childType(mod);
  30873         const slice_val = try mod.intern(.{ .ptr = .{
  30874             .ty = dest_ty.toIntern(),
  30875             .addr = switch (mod.intern_pool.indexToKey(val.toIntern())) {
  30876                 .undef => .{ .int = try mod.intern(.{ .undef = .usize_type }) },
  30877                 .ptr => |ptr| ptr.addr,
  30878                 else => unreachable,
  30879             },
  30880             .len = (try mod.intValue(Type.usize, array_ty.arrayLen(mod))).toIntern(),
  30881         } });
  30882         return Air.internedToRef(slice_val);
  30883     }
  30884     try sema.requireRuntimeBlock(block, inst_src, null);
  30885     return block.addTyOp(.array_to_slice, dest_ty, inst);
  30886 }
  30887 
  30888 fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool {
  30889     const mod = sema.mod;
  30890     const dest_info = dest_ty.ptrInfo(mod);
  30891     const inst_info = inst_ty.ptrInfo(mod);
  30892     const len0 = (Type.fromInterned(inst_info.child).zigTypeTag(mod) == .Array and (Type.fromInterned(inst_info.child).arrayLenIncludingSentinel(mod) == 0 or
  30893         (Type.fromInterned(inst_info.child).arrayLen(mod) == 0 and dest_info.sentinel == .none and dest_info.flags.size != .C and dest_info.flags.size != .Many))) or
  30894         (Type.fromInterned(inst_info.child).isTuple(mod) and Type.fromInterned(inst_info.child).structFieldCount(mod) == 0);
  30895 
  30896     const ok_cv_qualifiers =
  30897         ((!inst_info.flags.is_const or dest_info.flags.is_const) or len0) and
  30898         (!inst_info.flags.is_volatile or dest_info.flags.is_volatile);
  30899 
  30900     if (!ok_cv_qualifiers) {
  30901         in_memory_result.* = .{ .ptr_qualifiers = .{
  30902             .actual_const = inst_info.flags.is_const,
  30903             .wanted_const = dest_info.flags.is_const,
  30904             .actual_volatile = inst_info.flags.is_volatile,
  30905             .wanted_volatile = dest_info.flags.is_volatile,
  30906         } };
  30907         return false;
  30908     }
  30909     if (dest_info.flags.address_space != inst_info.flags.address_space) {
  30910         in_memory_result.* = .{ .ptr_addrspace = .{
  30911             .actual = inst_info.flags.address_space,
  30912             .wanted = dest_info.flags.address_space,
  30913         } };
  30914         return false;
  30915     }
  30916     if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true;
  30917     if (len0) return true;
  30918 
  30919     const inst_align = if (inst_info.flags.alignment != .none)
  30920         inst_info.flags.alignment
  30921     else
  30922         Type.fromInterned(inst_info.child).abiAlignment(mod);
  30923 
  30924     const dest_align = if (dest_info.flags.alignment != .none)
  30925         dest_info.flags.alignment
  30926     else
  30927         Type.fromInterned(dest_info.child).abiAlignment(mod);
  30928 
  30929     if (dest_align.compare(.gt, inst_align)) {
  30930         in_memory_result.* = .{ .ptr_alignment = .{
  30931             .actual = inst_align,
  30932             .wanted = dest_align,
  30933         } };
  30934         return false;
  30935     }
  30936     return true;
  30937 }
  30938 
  30939 fn coerceCompatiblePtrs(
  30940     sema: *Sema,
  30941     block: *Block,
  30942     dest_ty: Type,
  30943     inst: Air.Inst.Ref,
  30944     inst_src: LazySrcLoc,
  30945 ) !Air.Inst.Ref {
  30946     const mod = sema.mod;
  30947     const inst_ty = sema.typeOf(inst);
  30948     if (try sema.resolveValue(inst)) |val| {
  30949         if (!val.isUndef(mod) and val.isNull(mod) and !dest_ty.isAllowzeroPtr(mod)) {
  30950             return sema.fail(block, inst_src, "null pointer casted to type '{}'", .{dest_ty.fmt(sema.mod)});
  30951         }
  30952         // The comptime Value representation is compatible with both types.
  30953         return Air.internedToRef(
  30954             (try mod.getCoerced(Value.fromInterned((try val.intern(inst_ty, mod))), dest_ty)).toIntern(),
  30955         );
  30956     }
  30957     try sema.requireRuntimeBlock(block, inst_src, null);
  30958     const inst_allows_zero = inst_ty.zigTypeTag(mod) != .Pointer or inst_ty.ptrAllowsZero(mod);
  30959     if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(mod) and
  30960         (try sema.typeHasRuntimeBits(dest_ty.elemType2(mod)) or dest_ty.elemType2(mod).zigTypeTag(mod) == .Fn))
  30961     {
  30962         const actual_ptr = if (inst_ty.isSlice(mod))
  30963             try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty)
  30964         else
  30965             inst;
  30966         const ptr_int = try block.addUnOp(.int_from_ptr, actual_ptr);
  30967         const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
  30968         const ok = if (inst_ty.isSlice(mod)) ok: {
  30969             const len = try sema.analyzeSliceLen(block, inst_src, inst);
  30970             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
  30971             break :ok try block.addBinOp(.bool_or, len_zero, is_non_zero);
  30972         } else is_non_zero;
  30973         try sema.addSafetyCheck(block, inst_src, ok, .cast_to_null);
  30974     }
  30975     const new_ptr = try sema.bitCast(block, dest_ty, inst, inst_src, null);
  30976     try sema.checkKnownAllocPtr(inst, new_ptr);
  30977     return new_ptr;
  30978 }
  30979 
  30980 fn coerceEnumToUnion(
  30981     sema: *Sema,
  30982     block: *Block,
  30983     union_ty: Type,
  30984     union_ty_src: LazySrcLoc,
  30985     inst: Air.Inst.Ref,
  30986     inst_src: LazySrcLoc,
  30987 ) !Air.Inst.Ref {
  30988     const mod = sema.mod;
  30989     const ip = &mod.intern_pool;
  30990     const inst_ty = sema.typeOf(inst);
  30991 
  30992     const tag_ty = union_ty.unionTagType(mod) orelse {
  30993         const msg = msg: {
  30994             const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
  30995                 union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
  30996             });
  30997             errdefer msg.destroy(sema.gpa);
  30998             try sema.errNote(block, union_ty_src, msg, "cannot coerce enum to untagged union", .{});
  30999             try sema.addDeclaredHereNote(msg, union_ty);
  31000             break :msg msg;
  31001         };
  31002         return sema.failWithOwnedErrorMsg(block, msg);
  31003     };
  31004 
  31005     const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
  31006     if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| {
  31007         const field_index = union_ty.unionTagFieldIndex(val, sema.mod) orelse {
  31008             const msg = msg: {
  31009                 const msg = try sema.errMsg(block, inst_src, "union '{}' has no tag with value '{}'", .{
  31010                     union_ty.fmt(sema.mod), val.fmtValue(tag_ty, sema.mod),
  31011                 });
  31012                 errdefer msg.destroy(sema.gpa);
  31013                 try sema.addDeclaredHereNote(msg, union_ty);
  31014                 break :msg msg;
  31015             };
  31016             return sema.failWithOwnedErrorMsg(block, msg);
  31017         };
  31018 
  31019         const union_obj = mod.typeToUnion(union_ty).?;
  31020         const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
  31021         try sema.resolveTypeFields(field_ty);
  31022         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  31023             const msg = msg: {
  31024                 const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{});
  31025                 errdefer msg.destroy(sema.gpa);
  31026 
  31027                 const field_name = union_obj.field_names.get(ip)[field_index];
  31028                 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{
  31029                     field_name.fmt(ip),
  31030                 });
  31031                 try sema.addDeclaredHereNote(msg, union_ty);
  31032                 break :msg msg;
  31033             };
  31034             return sema.failWithOwnedErrorMsg(block, msg);
  31035         }
  31036         const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse {
  31037             const msg = msg: {
  31038                 const field_name = union_obj.field_names.get(ip)[field_index];
  31039                 const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{}'", .{
  31040                     inst_ty.fmt(sema.mod),  union_ty.fmt(sema.mod),
  31041                     field_ty.fmt(sema.mod), field_name.fmt(ip),
  31042                 });
  31043                 errdefer msg.destroy(sema.gpa);
  31044 
  31045                 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{
  31046                     field_name.fmt(ip),
  31047                 });
  31048                 try sema.addDeclaredHereNote(msg, union_ty);
  31049                 break :msg msg;
  31050             };
  31051             return sema.failWithOwnedErrorMsg(block, msg);
  31052         };
  31053 
  31054         return Air.internedToRef((try mod.unionValue(union_ty, val, opv)).toIntern());
  31055     }
  31056 
  31057     try sema.requireRuntimeBlock(block, inst_src, null);
  31058 
  31059     if (tag_ty.isNonexhaustiveEnum(mod)) {
  31060         const msg = msg: {
  31061             const msg = try sema.errMsg(block, inst_src, "runtime coercion to union '{}' from non-exhaustive enum", .{
  31062                 union_ty.fmt(sema.mod),
  31063             });
  31064             errdefer msg.destroy(sema.gpa);
  31065             try sema.addDeclaredHereNote(msg, tag_ty);
  31066             break :msg msg;
  31067         };
  31068         return sema.failWithOwnedErrorMsg(block, msg);
  31069     }
  31070 
  31071     const union_obj = mod.typeToUnion(union_ty).?;
  31072     {
  31073         var msg: ?*Module.ErrorMsg = null;
  31074         errdefer if (msg) |some| some.destroy(sema.gpa);
  31075 
  31076         for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| {
  31077             if (Type.fromInterned(field_ty).zigTypeTag(mod) == .NoReturn) {
  31078                 const err_msg = msg orelse try sema.errMsg(
  31079                     block,
  31080                     inst_src,
  31081                     "runtime coercion from enum '{}' to union '{}' which has a 'noreturn' field",
  31082                     .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) },
  31083                 );
  31084                 msg = err_msg;
  31085 
  31086                 try sema.addFieldErrNote(union_ty, field_index, err_msg, "'noreturn' field here", .{});
  31087             }
  31088         }
  31089         if (msg) |some| {
  31090             msg = null;
  31091             try sema.addDeclaredHereNote(some, union_ty);
  31092             return sema.failWithOwnedErrorMsg(block, some);
  31093         }
  31094     }
  31095 
  31096     // If the union has all fields 0 bits, the union value is just the enum value.
  31097     if (union_ty.unionHasAllZeroBitFieldTypes(mod)) {
  31098         return block.addBitCast(union_ty, enum_tag);
  31099     }
  31100 
  31101     const msg = msg: {
  31102         const msg = try sema.errMsg(
  31103             block,
  31104             inst_src,
  31105             "runtime coercion from enum '{}' to union '{}' which has non-void fields",
  31106             .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) },
  31107         );
  31108         errdefer msg.destroy(sema.gpa);
  31109 
  31110         for (0..union_obj.field_names.len) |field_index| {
  31111             const field_name = union_obj.field_names.get(ip)[field_index];
  31112             const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
  31113             if (!(try sema.typeHasRuntimeBits(field_ty))) continue;
  31114             try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' has type '{}'", .{
  31115                 field_name.fmt(ip),
  31116                 field_ty.fmt(sema.mod),
  31117             });
  31118         }
  31119         try sema.addDeclaredHereNote(msg, union_ty);
  31120         break :msg msg;
  31121     };
  31122     return sema.failWithOwnedErrorMsg(block, msg);
  31123 }
  31124 
  31125 fn coerceAnonStructToUnion(
  31126     sema: *Sema,
  31127     block: *Block,
  31128     union_ty: Type,
  31129     union_ty_src: LazySrcLoc,
  31130     inst: Air.Inst.Ref,
  31131     inst_src: LazySrcLoc,
  31132 ) !Air.Inst.Ref {
  31133     const mod = sema.mod;
  31134     const ip = &mod.intern_pool;
  31135     const inst_ty = sema.typeOf(inst);
  31136     const field_info: union(enum) {
  31137         name: InternPool.NullTerminatedString,
  31138         count: usize,
  31139     } = switch (ip.indexToKey(inst_ty.toIntern())) {
  31140         .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 1)
  31141             .{ .name = anon_struct_type.names.get(ip)[0] }
  31142         else
  31143             .{ .count = anon_struct_type.names.len },
  31144         .struct_type => |struct_type| name: {
  31145             const field_names = struct_type.field_names.get(ip);
  31146             break :name if (field_names.len == 1)
  31147                 .{ .name = field_names[0] }
  31148             else
  31149                 .{ .count = field_names.len };
  31150         },
  31151         else => unreachable,
  31152     };
  31153     switch (field_info) {
  31154         .name => |field_name| {
  31155             const init = try sema.structFieldVal(block, inst_src, inst, field_name, inst_src, inst_ty);
  31156             return sema.unionInit(block, init, inst_src, union_ty, union_ty_src, field_name, inst_src);
  31157         },
  31158         .count => |field_count| {
  31159             assert(field_count != 1);
  31160             const msg = msg: {
  31161                 const msg = if (field_count > 1) try sema.errMsg(
  31162                     block,
  31163                     inst_src,
  31164                     "cannot initialize multiple union fields at once; unions can only have one active field",
  31165                     .{},
  31166                 ) else try sema.errMsg(
  31167                     block,
  31168                     inst_src,
  31169                     "union initializer must initialize one field",
  31170                     .{},
  31171                 );
  31172                 errdefer msg.destroy(sema.gpa);
  31173 
  31174                 // TODO add notes for where the anon struct was created to point out
  31175                 // the extra fields.
  31176 
  31177                 try sema.addDeclaredHereNote(msg, union_ty);
  31178                 break :msg msg;
  31179             };
  31180             return sema.failWithOwnedErrorMsg(block, msg);
  31181         },
  31182     }
  31183 }
  31184 
  31185 fn coerceAnonStructToUnionPtrs(
  31186     sema: *Sema,
  31187     block: *Block,
  31188     ptr_union_ty: Type,
  31189     union_ty_src: LazySrcLoc,
  31190     ptr_anon_struct: Air.Inst.Ref,
  31191     anon_struct_src: LazySrcLoc,
  31192 ) !Air.Inst.Ref {
  31193     const mod = sema.mod;
  31194     const union_ty = ptr_union_ty.childType(mod);
  31195     const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src);
  31196     const union_inst = try sema.coerceAnonStructToUnion(block, union_ty, union_ty_src, anon_struct, anon_struct_src);
  31197     return sema.analyzeRef(block, union_ty_src, union_inst);
  31198 }
  31199 
  31200 fn coerceAnonStructToStructPtrs(
  31201     sema: *Sema,
  31202     block: *Block,
  31203     ptr_struct_ty: Type,
  31204     struct_ty_src: LazySrcLoc,
  31205     ptr_anon_struct: Air.Inst.Ref,
  31206     anon_struct_src: LazySrcLoc,
  31207 ) !Air.Inst.Ref {
  31208     const mod = sema.mod;
  31209     const struct_ty = ptr_struct_ty.childType(mod);
  31210     const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src);
  31211     const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, anon_struct, anon_struct_src);
  31212     return sema.analyzeRef(block, struct_ty_src, struct_inst);
  31213 }
  31214 
  31215 /// If the lengths match, coerces element-wise.
  31216 fn coerceArrayLike(
  31217     sema: *Sema,
  31218     block: *Block,
  31219     dest_ty: Type,
  31220     dest_ty_src: LazySrcLoc,
  31221     inst: Air.Inst.Ref,
  31222     inst_src: LazySrcLoc,
  31223 ) !Air.Inst.Ref {
  31224     const mod = sema.mod;
  31225     const inst_ty = sema.typeOf(inst);
  31226     const target = mod.getTarget();
  31227 
  31228     // try coercion of the whole array
  31229     const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
  31230     if (in_memory_result == .ok) {
  31231         if (try sema.resolveValue(inst)) |inst_val| {
  31232             // These types share the same comptime value representation.
  31233             return sema.coerceInMemory(inst_val, dest_ty);
  31234         }
  31235         try sema.requireRuntimeBlock(block, inst_src, null);
  31236         return block.addBitCast(dest_ty, inst);
  31237     }
  31238 
  31239     // otherwise, try element by element
  31240     const inst_len = inst_ty.arrayLen(mod);
  31241     const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod));
  31242     if (dest_len != inst_len) {
  31243         const msg = msg: {
  31244             const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
  31245                 dest_ty.fmt(mod), inst_ty.fmt(mod),
  31246             });
  31247             errdefer msg.destroy(sema.gpa);
  31248             try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
  31249             try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
  31250             break :msg msg;
  31251         };
  31252         return sema.failWithOwnedErrorMsg(block, msg);
  31253     }
  31254 
  31255     const dest_elem_ty = dest_ty.childType(mod);
  31256     const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
  31257     const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
  31258     var runtime_src: ?LazySrcLoc = null;
  31259 
  31260     for (element_vals, element_refs, 0..) |*val, *ref, i| {
  31261         const index_ref = Air.internedToRef((try mod.intValue(Type.usize, i)).toIntern());
  31262         const src = inst_src; // TODO better source location
  31263         const elem_src = inst_src; // TODO better source location
  31264         const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true);
  31265         const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
  31266         ref.* = coerced;
  31267         if (runtime_src == null) {
  31268             if (try sema.resolveValue(coerced)) |elem_val| {
  31269                 val.* = try elem_val.intern(dest_elem_ty, mod);
  31270             } else {
  31271                 runtime_src = elem_src;
  31272             }
  31273         }
  31274     }
  31275 
  31276     if (runtime_src) |rs| {
  31277         try sema.requireRuntimeBlock(block, inst_src, rs);
  31278         return block.addAggregateInit(dest_ty, element_refs);
  31279     }
  31280 
  31281     return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  31282         .ty = dest_ty.toIntern(),
  31283         .storage = .{ .elems = element_vals },
  31284     } })));
  31285 }
  31286 
  31287 /// If the lengths match, coerces element-wise.
  31288 fn coerceTupleToArray(
  31289     sema: *Sema,
  31290     block: *Block,
  31291     dest_ty: Type,
  31292     dest_ty_src: LazySrcLoc,
  31293     inst: Air.Inst.Ref,
  31294     inst_src: LazySrcLoc,
  31295 ) !Air.Inst.Ref {
  31296     const mod = sema.mod;
  31297     const inst_ty = sema.typeOf(inst);
  31298     const inst_len = inst_ty.arrayLen(mod);
  31299     const dest_len = dest_ty.arrayLen(mod);
  31300 
  31301     if (dest_len != inst_len) {
  31302         const msg = msg: {
  31303             const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
  31304                 dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
  31305             });
  31306             errdefer msg.destroy(sema.gpa);
  31307             try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
  31308             try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
  31309             break :msg msg;
  31310         };
  31311         return sema.failWithOwnedErrorMsg(block, msg);
  31312     }
  31313 
  31314     const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len);
  31315     const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems);
  31316     const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);
  31317     const dest_elem_ty = dest_ty.childType(mod);
  31318 
  31319     var runtime_src: ?LazySrcLoc = null;
  31320     for (element_vals, element_refs, 0..) |*val, *ref, i_usize| {
  31321         const i: u32 = @intCast(i_usize);
  31322         if (i_usize == inst_len) {
  31323             const sentinel_val = dest_ty.sentinel(mod).?;
  31324             val.* = sentinel_val.toIntern();
  31325             ref.* = Air.internedToRef(sentinel_val.toIntern());
  31326             break;
  31327         }
  31328         const elem_src = inst_src; // TODO better source location
  31329         const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i);
  31330         const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
  31331         ref.* = coerced;
  31332         if (runtime_src == null) {
  31333             if (try sema.resolveValue(coerced)) |elem_val| {
  31334                 val.* = try elem_val.intern(dest_elem_ty, mod);
  31335             } else {
  31336                 runtime_src = elem_src;
  31337             }
  31338         }
  31339     }
  31340 
  31341     if (runtime_src) |rs| {
  31342         try sema.requireRuntimeBlock(block, inst_src, rs);
  31343         return block.addAggregateInit(dest_ty, element_refs);
  31344     }
  31345 
  31346     return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  31347         .ty = dest_ty.toIntern(),
  31348         .storage = .{ .elems = element_vals },
  31349     } })));
  31350 }
  31351 
  31352 /// If the lengths match, coerces element-wise.
  31353 fn coerceTupleToSlicePtrs(
  31354     sema: *Sema,
  31355     block: *Block,
  31356     slice_ty: Type,
  31357     slice_ty_src: LazySrcLoc,
  31358     ptr_tuple: Air.Inst.Ref,
  31359     tuple_src: LazySrcLoc,
  31360 ) !Air.Inst.Ref {
  31361     const mod = sema.mod;
  31362     const tuple_ty = sema.typeOf(ptr_tuple).childType(mod);
  31363     const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
  31364     const slice_info = slice_ty.ptrInfo(mod);
  31365     const array_ty = try mod.arrayType(.{
  31366         .len = tuple_ty.structFieldCount(mod),
  31367         .sentinel = slice_info.sentinel,
  31368         .child = slice_info.child,
  31369     });
  31370     const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src);
  31371     if (slice_info.flags.alignment != .none) {
  31372         return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{});
  31373     }
  31374     const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst);
  31375     return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);
  31376 }
  31377 
  31378 /// If the lengths match, coerces element-wise.
  31379 fn coerceTupleToArrayPtrs(
  31380     sema: *Sema,
  31381     block: *Block,
  31382     ptr_array_ty: Type,
  31383     array_ty_src: LazySrcLoc,
  31384     ptr_tuple: Air.Inst.Ref,
  31385     tuple_src: LazySrcLoc,
  31386 ) !Air.Inst.Ref {
  31387     const mod = sema.mod;
  31388     const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
  31389     const ptr_info = ptr_array_ty.ptrInfo(mod);
  31390     const array_ty = Type.fromInterned(ptr_info.child);
  31391     const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src);
  31392     if (ptr_info.flags.alignment != .none) {
  31393         return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{});
  31394     }
  31395     const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst);
  31396     return ptr_array;
  31397 }
  31398 
  31399 /// Handles both tuples and anon struct literals. Coerces field-wise. Reports
  31400 /// errors for both extra fields and missing fields.
  31401 fn coerceTupleToStruct(
  31402     sema: *Sema,
  31403     block: *Block,
  31404     struct_ty: Type,
  31405     inst: Air.Inst.Ref,
  31406     inst_src: LazySrcLoc,
  31407 ) !Air.Inst.Ref {
  31408     const mod = sema.mod;
  31409     const ip = &mod.intern_pool;
  31410     try sema.resolveTypeFields(struct_ty);
  31411     try sema.resolveStructFieldInits(struct_ty);
  31412 
  31413     if (struct_ty.isTupleOrAnonStruct(mod)) {
  31414         return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src);
  31415     }
  31416 
  31417     const struct_type = mod.typeToStruct(struct_ty).?;
  31418     const field_vals = try sema.arena.alloc(InternPool.Index, struct_type.field_types.len);
  31419     const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
  31420     @memset(field_refs, .none);
  31421 
  31422     const inst_ty = sema.typeOf(inst);
  31423     var runtime_src: ?LazySrcLoc = null;
  31424     const field_count = switch (ip.indexToKey(inst_ty.toIntern())) {
  31425         .anon_struct_type => |anon_struct_type| anon_struct_type.types.len,
  31426         .struct_type => |s| s.field_types.len,
  31427         else => unreachable,
  31428     };
  31429     for (0..field_count) |field_index_usize| {
  31430         const field_i: u32 = @intCast(field_index_usize);
  31431         const field_src = inst_src; // TODO better source location
  31432         // https://github.com/ziglang/zig/issues/15709
  31433         const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) {
  31434             .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0)
  31435                 anon_struct_type.names.get(ip)[field_i]
  31436             else
  31437                 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}),
  31438             .struct_type => |s| s.field_names.get(ip)[field_i],
  31439             else => unreachable,
  31440         };
  31441         const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);
  31442         const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]);
  31443         const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
  31444         const coerced = try sema.coerce(block, field_ty, elem_ref, field_src);
  31445         field_refs[field_index] = coerced;
  31446         if (struct_type.fieldIsComptime(ip, field_index)) {
  31447             const init_val = (try sema.resolveValue(coerced)) orelse {
  31448                 return sema.failWithNeededComptime(block, field_src, .{
  31449                     .needed_comptime_reason = "value stored in comptime field must be comptime-known",
  31450                 });
  31451             };
  31452 
  31453             const field_init = Value.fromInterned(struct_type.field_inits.get(ip)[field_index]);
  31454             if (!init_val.eql(field_init, field_ty, sema.mod)) {
  31455                 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i);
  31456             }
  31457         }
  31458         if (runtime_src == null) {
  31459             if (try sema.resolveValue(coerced)) |field_val| {
  31460                 field_vals[field_index] = field_val.toIntern();
  31461             } else {
  31462                 runtime_src = field_src;
  31463             }
  31464         }
  31465     }
  31466 
  31467     // Populate default field values and report errors for missing fields.
  31468     var root_msg: ?*Module.ErrorMsg = null;
  31469     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  31470 
  31471     for (field_refs, 0..) |*field_ref, i| {
  31472         if (field_ref.* != .none) continue;
  31473 
  31474         const field_name = struct_type.field_names.get(ip)[i];
  31475         const field_default_val = struct_type.fieldInit(ip, i);
  31476         const field_src = inst_src; // TODO better source location
  31477         if (field_default_val == .none) {
  31478             const template = "missing struct field: {}";
  31479             const args = .{field_name.fmt(ip)};
  31480             if (root_msg) |msg| {
  31481                 try sema.errNote(block, field_src, msg, template, args);
  31482             } else {
  31483                 root_msg = try sema.errMsg(block, field_src, template, args);
  31484             }
  31485             continue;
  31486         }
  31487         if (runtime_src == null) {
  31488             field_vals[i] = field_default_val;
  31489         } else {
  31490             field_ref.* = Air.internedToRef(field_default_val);
  31491         }
  31492     }
  31493 
  31494     if (root_msg) |msg| {
  31495         try sema.addDeclaredHereNote(msg, struct_ty);
  31496         root_msg = null;
  31497         return sema.failWithOwnedErrorMsg(block, msg);
  31498     }
  31499 
  31500     if (runtime_src) |rs| {
  31501         try sema.requireRuntimeBlock(block, inst_src, rs);
  31502         return block.addAggregateInit(struct_ty, field_refs);
  31503     }
  31504 
  31505     const struct_val = try mod.intern(.{ .aggregate = .{
  31506         .ty = struct_ty.toIntern(),
  31507         .storage = .{ .elems = field_vals },
  31508     } });
  31509     // TODO: figure out InternPool removals for incremental compilation
  31510     //errdefer ip.remove(struct_val);
  31511 
  31512     return Air.internedToRef(struct_val);
  31513 }
  31514 
  31515 fn coerceTupleToTuple(
  31516     sema: *Sema,
  31517     block: *Block,
  31518     tuple_ty: Type,
  31519     inst: Air.Inst.Ref,
  31520     inst_src: LazySrcLoc,
  31521 ) !Air.Inst.Ref {
  31522     const mod = sema.mod;
  31523     const ip = &mod.intern_pool;
  31524     const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) {
  31525         .anon_struct_type => |anon_struct_type| anon_struct_type.types.len,
  31526         .struct_type => |struct_type| struct_type.field_types.len,
  31527         else => unreachable,
  31528     };
  31529     const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count);
  31530     const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
  31531     @memset(field_refs, .none);
  31532 
  31533     const inst_ty = sema.typeOf(inst);
  31534     const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) {
  31535         .anon_struct_type => |anon_struct_type| anon_struct_type.types.len,
  31536         .struct_type => |struct_type| struct_type.field_types.len,
  31537         else => unreachable,
  31538     };
  31539     if (src_field_count > dest_field_count) return error.NotCoercible;
  31540 
  31541     var runtime_src: ?LazySrcLoc = null;
  31542     for (0..dest_field_count) |field_index_usize| {
  31543         const field_i: u32 = @intCast(field_index_usize);
  31544         const field_src = inst_src; // TODO better source location
  31545         // https://github.com/ziglang/zig/issues/15709
  31546         const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) {
  31547             .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0)
  31548                 anon_struct_type.names.get(ip)[field_i]
  31549             else
  31550                 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}),
  31551             .struct_type => |struct_type| if (struct_type.field_names.len > 0)
  31552                 struct_type.field_names.get(ip)[field_i]
  31553             else
  31554                 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}),
  31555             else => unreachable,
  31556         };
  31557 
  31558         if (ip.stringEqlSlice(field_name, "len"))
  31559             return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{});
  31560 
  31561         const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) {
  31562             .anon_struct_type => |anon_struct_type| anon_struct_type.types.get(ip)[field_index_usize],
  31563             .struct_type => |struct_type| struct_type.field_types.get(ip)[field_index_usize],
  31564             else => unreachable,
  31565         };
  31566         const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
  31567             .anon_struct_type => |anon_struct_type| anon_struct_type.values.get(ip)[field_index_usize],
  31568             .struct_type => |struct_type| struct_type.fieldInit(ip, field_index_usize),
  31569             else => unreachable,
  31570         };
  31571 
  31572         const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src);
  31573 
  31574         const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
  31575         const coerced = try sema.coerce(block, Type.fromInterned(field_ty), elem_ref, field_src);
  31576         field_refs[field_index] = coerced;
  31577         if (default_val != .none) {
  31578             const init_val = (try sema.resolveValue(coerced)) orelse {
  31579                 return sema.failWithNeededComptime(block, field_src, .{
  31580                     .needed_comptime_reason = "value stored in comptime field must be comptime-known",
  31581                 });
  31582             };
  31583 
  31584             if (!init_val.eql(Value.fromInterned(default_val), Type.fromInterned(field_ty), sema.mod)) {
  31585                 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i);
  31586             }
  31587         }
  31588         if (runtime_src == null) {
  31589             if (try sema.resolveValue(coerced)) |field_val| {
  31590                 field_vals[field_index] = field_val.toIntern();
  31591             } else {
  31592                 runtime_src = field_src;
  31593             }
  31594         }
  31595     }
  31596 
  31597     // Populate default field values and report errors for missing fields.
  31598     var root_msg: ?*Module.ErrorMsg = null;
  31599     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  31600 
  31601     for (field_refs, 0..) |*field_ref, i_usize| {
  31602         const i: u32 = @intCast(i_usize);
  31603         if (field_ref.* != .none) continue;
  31604 
  31605         const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
  31606             .anon_struct_type => |anon_struct_type| anon_struct_type.values.get(ip)[i],
  31607             .struct_type => |struct_type| struct_type.fieldInit(ip, i),
  31608             else => unreachable,
  31609         };
  31610 
  31611         const field_src = inst_src; // TODO better source location
  31612         if (default_val == .none) {
  31613             const field_name = tuple_ty.structFieldName(i, mod).unwrap() orelse {
  31614                 const template = "missing tuple field: {d}";
  31615                 if (root_msg) |msg| {
  31616                     try sema.errNote(block, field_src, msg, template, .{i});
  31617                 } else {
  31618                     root_msg = try sema.errMsg(block, field_src, template, .{i});
  31619                 }
  31620                 continue;
  31621             };
  31622             const template = "missing struct field: {}";
  31623             const args = .{field_name.fmt(ip)};
  31624             if (root_msg) |msg| {
  31625                 try sema.errNote(block, field_src, msg, template, args);
  31626             } else {
  31627                 root_msg = try sema.errMsg(block, field_src, template, args);
  31628             }
  31629             continue;
  31630         }
  31631         if (runtime_src == null) {
  31632             field_vals[i] = default_val;
  31633         } else {
  31634             field_ref.* = Air.internedToRef(default_val);
  31635         }
  31636     }
  31637 
  31638     if (root_msg) |msg| {
  31639         try sema.addDeclaredHereNote(msg, tuple_ty);
  31640         root_msg = null;
  31641         return sema.failWithOwnedErrorMsg(block, msg);
  31642     }
  31643 
  31644     if (runtime_src) |rs| {
  31645         try sema.requireRuntimeBlock(block, inst_src, rs);
  31646         return block.addAggregateInit(tuple_ty, field_refs);
  31647     }
  31648 
  31649     return Air.internedToRef((try mod.intern(.{ .aggregate = .{
  31650         .ty = tuple_ty.toIntern(),
  31651         .storage = .{ .elems = field_vals },
  31652     } })));
  31653 }
  31654 
  31655 fn analyzeDeclVal(
  31656     sema: *Sema,
  31657     block: *Block,
  31658     src: LazySrcLoc,
  31659     decl_index: InternPool.DeclIndex,
  31660 ) CompileError!Air.Inst.Ref {
  31661     try sema.addReferencedBy(block, src, decl_index);
  31662     if (sema.decl_val_table.get(decl_index)) |result| {
  31663         return result;
  31664     }
  31665     const decl_ref = try sema.analyzeDeclRefInner(decl_index, false);
  31666     const result = try sema.analyzeLoad(block, src, decl_ref, src);
  31667     if (result.toInterned() != null) {
  31668         if (!block.is_typeof) {
  31669             try sema.decl_val_table.put(sema.gpa, decl_index, result);
  31670         }
  31671     }
  31672     return result;
  31673 }
  31674 
  31675 fn addReferencedBy(
  31676     sema: *Sema,
  31677     block: *Block,
  31678     src: LazySrcLoc,
  31679     decl_index: InternPool.DeclIndex,
  31680 ) !void {
  31681     if (sema.mod.comp.reference_trace == 0) return;
  31682     if (src == .unneeded) {
  31683         // We can't use NeededSourceLocation, since sites handling that assume it means a compile
  31684         // error. Our long-term strategy here is to gradually transition from NeededSourceLocation
  31685         // into having more LazySrcLoc tags. In the meantime, let release compilers just ignore this
  31686         // reference (a slightly-incomplete error is better than a crash!), but trigger a panic in
  31687         // debug so we can fix this case.
  31688         if (std.debug.runtime_safety) unreachable else return;
  31689     }
  31690     try sema.mod.reference_table.put(sema.gpa, decl_index, .{
  31691         .referencer = block.src_decl,
  31692         .src = src,
  31693     });
  31694 }
  31695 
  31696 fn ensureDeclAnalyzed(sema: *Sema, decl_index: InternPool.DeclIndex) CompileError!void {
  31697     const mod = sema.mod;
  31698     const ip = &mod.intern_pool;
  31699     const decl = mod.declPtr(decl_index);
  31700     if (decl.analysis == .in_progress) {
  31701         const msg = try Module.ErrorMsg.create(sema.gpa, decl.srcLoc(mod), "dependency loop detected", .{});
  31702         return sema.failWithOwnedErrorMsg(null, msg);
  31703     }
  31704 
  31705     mod.ensureDeclAnalyzed(decl_index) catch |err| {
  31706         if (sema.owner_func_index != .none) {
  31707             ip.funcAnalysis(sema.owner_func_index).state = .dependency_failure;
  31708         } else {
  31709             sema.owner_decl.analysis = .dependency_failure;
  31710         }
  31711         return err;
  31712     };
  31713 }
  31714 
  31715 fn ensureFuncBodyAnalyzed(sema: *Sema, func: InternPool.Index) CompileError!void {
  31716     const mod = sema.mod;
  31717     const ip = &mod.intern_pool;
  31718     mod.ensureFuncBodyAnalyzed(func) catch |err| {
  31719         if (sema.owner_func_index != .none) {
  31720             ip.funcAnalysis(sema.owner_func_index).state = .dependency_failure;
  31721         } else {
  31722             sema.owner_decl.analysis = .dependency_failure;
  31723         }
  31724         return err;
  31725     };
  31726 }
  31727 
  31728 fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {
  31729     const mod = sema.mod;
  31730     const ptr_anyopaque_ty = try mod.singleConstPtrType(Type.anyopaque);
  31731     return Value.fromInterned((try mod.intern(.{ .opt = .{
  31732         .ty = (try mod.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(),
  31733         .val = if (opt_val) |val| (try mod.getCoerced(
  31734             Value.fromInterned((try sema.refValue(val.toIntern()))),
  31735             ptr_anyopaque_ty,
  31736         )).toIntern() else .none,
  31737     } })));
  31738 }
  31739 
  31740 fn analyzeDeclRef(sema: *Sema, decl_index: InternPool.DeclIndex) CompileError!Air.Inst.Ref {
  31741     return sema.analyzeDeclRefInner(decl_index, true);
  31742 }
  31743 
  31744 /// Analyze a reference to the decl at the given index. Ensures the underlying decl is analyzed, but
  31745 /// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a
  31746 /// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeDeclRef` wraps
  31747 /// this function with `analyze_fn_body` set to true.
  31748 fn analyzeDeclRefInner(sema: *Sema, decl_index: InternPool.DeclIndex, analyze_fn_body: bool) CompileError!Air.Inst.Ref {
  31749     const mod = sema.mod;
  31750     try sema.ensureDeclAnalyzed(decl_index);
  31751 
  31752     const decl = mod.declPtr(decl_index);
  31753     const decl_tv = try decl.typedValue();
  31754     const ptr_ty = try sema.ptrType(.{
  31755         .child = decl_tv.ty.toIntern(),
  31756         .flags = .{
  31757             .alignment = decl.alignment,
  31758             .is_const = if (decl.val.getVariable(mod)) |variable| variable.is_const else true,
  31759             .address_space = decl.@"addrspace",
  31760         },
  31761     });
  31762     if (analyze_fn_body) {
  31763         try sema.maybeQueueFuncBodyAnalysis(decl_index);
  31764     }
  31765     return Air.internedToRef((try mod.intern(.{ .ptr = .{
  31766         .ty = ptr_ty.toIntern(),
  31767         .addr = .{ .decl = decl_index },
  31768     } })));
  31769 }
  31770 
  31771 fn maybeQueueFuncBodyAnalysis(sema: *Sema, decl_index: InternPool.DeclIndex) !void {
  31772     const mod = sema.mod;
  31773     const decl = mod.declPtr(decl_index);
  31774     const tv = try decl.typedValue();
  31775     if (tv.ty.zigTypeTag(mod) != .Fn) return;
  31776     if (!try sema.fnHasRuntimeBits(tv.ty)) return;
  31777     const func_index = tv.val.toIntern();
  31778     if (!mod.intern_pool.isFuncBody(func_index)) return; // undef or extern function
  31779     try mod.ensureFuncBodyAnalysisQueued(func_index);
  31780 }
  31781 
  31782 fn analyzeRef(
  31783     sema: *Sema,
  31784     block: *Block,
  31785     src: LazySrcLoc,
  31786     operand: Air.Inst.Ref,
  31787 ) CompileError!Air.Inst.Ref {
  31788     const mod = sema.mod;
  31789     const operand_ty = sema.typeOf(operand);
  31790 
  31791     if (try sema.resolveValue(operand)) |val| {
  31792         switch (mod.intern_pool.indexToKey(val.toIntern())) {
  31793             .extern_func => |extern_func| return sema.analyzeDeclRef(extern_func.decl),
  31794             .func => |func| return sema.analyzeDeclRef(func.owner_decl),
  31795             else => return anonDeclRef(sema, val.toIntern()),
  31796         }
  31797     }
  31798 
  31799     try sema.requireRuntimeBlock(block, src, null);
  31800     const address_space = target_util.defaultAddressSpace(mod.getTarget(), .local);
  31801     const ptr_type = try sema.ptrType(.{
  31802         .child = operand_ty.toIntern(),
  31803         .flags = .{
  31804             .is_const = true,
  31805             .address_space = address_space,
  31806         },
  31807     });
  31808     const mut_ptr_type = try sema.ptrType(.{
  31809         .child = operand_ty.toIntern(),
  31810         .flags = .{ .address_space = address_space },
  31811     });
  31812     const alloc = try block.addTy(.alloc, mut_ptr_type);
  31813     try sema.storePtr(block, src, alloc, operand);
  31814 
  31815     // TODO: Replace with sema.coerce when that supports adding pointer constness.
  31816     return sema.bitCast(block, ptr_type, alloc, src, null);
  31817 }
  31818 
  31819 fn analyzeLoad(
  31820     sema: *Sema,
  31821     block: *Block,
  31822     src: LazySrcLoc,
  31823     ptr: Air.Inst.Ref,
  31824     ptr_src: LazySrcLoc,
  31825 ) CompileError!Air.Inst.Ref {
  31826     const mod = sema.mod;
  31827     const ptr_ty = sema.typeOf(ptr);
  31828     const elem_ty = switch (ptr_ty.zigTypeTag(mod)) {
  31829         .Pointer => ptr_ty.childType(mod),
  31830         else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}),
  31831     };
  31832     if (elem_ty.zigTypeTag(mod) == .Opaque) {
  31833         return sema.fail(block, ptr_src, "cannot load opaque type '{}'", .{elem_ty.fmt(mod)});
  31834     }
  31835 
  31836     if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| {
  31837         return Air.internedToRef(opv.toIntern());
  31838     }
  31839 
  31840     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
  31841         if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| {
  31842             return Air.internedToRef(elem_val.toIntern());
  31843         }
  31844     }
  31845 
  31846     if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) {
  31847         const ptr_inst = ptr.toIndex().?;
  31848         const air_tags = sema.air_instructions.items(.tag);
  31849         if (air_tags[@intFromEnum(ptr_inst)] == .ptr_elem_ptr) {
  31850             const ty_pl = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].ty_pl;
  31851             const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data;
  31852             return block.addBinOp(.ptr_elem_val, bin_op.lhs, bin_op.rhs);
  31853         }
  31854         return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{
  31855             ptr_ty.fmt(sema.mod),
  31856         });
  31857     }
  31858 
  31859     return block.addTyOp(.load, elem_ty, ptr);
  31860 }
  31861 
  31862 fn analyzeSlicePtr(
  31863     sema: *Sema,
  31864     block: *Block,
  31865     slice_src: LazySrcLoc,
  31866     slice: Air.Inst.Ref,
  31867     slice_ty: Type,
  31868 ) CompileError!Air.Inst.Ref {
  31869     const mod = sema.mod;
  31870     const result_ty = slice_ty.slicePtrFieldType(mod);
  31871     if (try sema.resolveValue(slice)) |val| {
  31872         if (val.isUndef(mod)) return mod.undefRef(result_ty);
  31873         return Air.internedToRef(val.slicePtr(mod).toIntern());
  31874     }
  31875     try sema.requireRuntimeBlock(block, slice_src, null);
  31876     return block.addTyOp(.slice_ptr, result_ty, slice);
  31877 }
  31878 
  31879 fn analyzeSliceLen(
  31880     sema: *Sema,
  31881     block: *Block,
  31882     src: LazySrcLoc,
  31883     slice_inst: Air.Inst.Ref,
  31884 ) CompileError!Air.Inst.Ref {
  31885     const mod = sema.mod;
  31886     if (try sema.resolveValue(slice_inst)) |slice_val| {
  31887         if (slice_val.isUndef(mod)) {
  31888             return mod.undefRef(Type.usize);
  31889         }
  31890         return mod.intRef(Type.usize, slice_val.sliceLen(sema.mod));
  31891     }
  31892     try sema.requireRuntimeBlock(block, src, null);
  31893     return block.addTyOp(.slice_len, Type.usize, slice_inst);
  31894 }
  31895 
  31896 fn analyzeIsNull(
  31897     sema: *Sema,
  31898     block: *Block,
  31899     src: LazySrcLoc,
  31900     operand: Air.Inst.Ref,
  31901     invert_logic: bool,
  31902 ) CompileError!Air.Inst.Ref {
  31903     const mod = sema.mod;
  31904     const result_ty = Type.bool;
  31905     if (try sema.resolveValue(operand)) |opt_val| {
  31906         if (opt_val.isUndef(mod)) {
  31907             return mod.undefRef(result_ty);
  31908         }
  31909         const is_null = opt_val.isNull(mod);
  31910         const bool_value = if (invert_logic) !is_null else is_null;
  31911         return if (bool_value) .bool_true else .bool_false;
  31912     }
  31913 
  31914     const inverted_non_null_res: Air.Inst.Ref = if (invert_logic) .bool_true else .bool_false;
  31915     const operand_ty = sema.typeOf(operand);
  31916     if (operand_ty.zigTypeTag(mod) == .Optional and operand_ty.optionalChild(mod).zigTypeTag(mod) == .NoReturn) {
  31917         return inverted_non_null_res;
  31918     }
  31919     if (operand_ty.zigTypeTag(mod) != .Optional and !operand_ty.isPtrLikeOptional(mod)) {
  31920         return inverted_non_null_res;
  31921     }
  31922     try sema.requireRuntimeBlock(block, src, null);
  31923     const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
  31924     return block.addUnOp(air_tag, operand);
  31925 }
  31926 
  31927 fn analyzePtrIsNonErrComptimeOnly(
  31928     sema: *Sema,
  31929     block: *Block,
  31930     src: LazySrcLoc,
  31931     operand: Air.Inst.Ref,
  31932 ) CompileError!Air.Inst.Ref {
  31933     const mod = sema.mod;
  31934     const ptr_ty = sema.typeOf(operand);
  31935     assert(ptr_ty.zigTypeTag(mod) == .Pointer);
  31936     const child_ty = ptr_ty.childType(mod);
  31937 
  31938     const child_tag = child_ty.zigTypeTag(mod);
  31939     if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return .bool_true;
  31940     if (child_tag == .ErrorSet) return .bool_false;
  31941     assert(child_tag == .ErrorUnion);
  31942 
  31943     _ = block;
  31944     _ = src;
  31945 
  31946     return .none;
  31947 }
  31948 
  31949 fn analyzeIsNonErrComptimeOnly(
  31950     sema: *Sema,
  31951     block: *Block,
  31952     src: LazySrcLoc,
  31953     operand: Air.Inst.Ref,
  31954 ) CompileError!Air.Inst.Ref {
  31955     const mod = sema.mod;
  31956     const ip = &mod.intern_pool;
  31957     const operand_ty = sema.typeOf(operand);
  31958     const ot = operand_ty.zigTypeTag(mod);
  31959     if (ot != .ErrorSet and ot != .ErrorUnion) return .bool_true;
  31960     if (ot == .ErrorSet) return .bool_false;
  31961     assert(ot == .ErrorUnion);
  31962 
  31963     const payload_ty = operand_ty.errorUnionPayload(mod);
  31964     if (payload_ty.zigTypeTag(mod) == .NoReturn) {
  31965         return .bool_false;
  31966     }
  31967 
  31968     if (operand.toIndex()) |operand_inst| {
  31969         switch (sema.air_instructions.items(.tag)[@intFromEnum(operand_inst)]) {
  31970             .wrap_errunion_payload => return .bool_true,
  31971             .wrap_errunion_err => return .bool_false,
  31972             else => {},
  31973         }
  31974     } else if (operand == .undef) {
  31975         return mod.undefRef(Type.bool);
  31976     } else if (@intFromEnum(operand) < InternPool.static_len) {
  31977         // None of the ref tags can be errors.
  31978         return .bool_true;
  31979     }
  31980 
  31981     const maybe_operand_val = try sema.resolveValue(operand);
  31982 
  31983     // exception if the error union error set is known to be empty,
  31984     // we allow the comparison but always make it comptime-known.
  31985     const set_ty = ip.errorUnionSet(operand_ty.toIntern());
  31986     switch (set_ty) {
  31987         .anyerror_type => {},
  31988         .adhoc_inferred_error_set_type => if (sema.fn_ret_ty_ies) |ies| blk: {
  31989             // If the error set is empty, we must return a comptime true or false.
  31990             // However we want to avoid unnecessarily resolving an inferred error set
  31991             // in case it is already non-empty.
  31992             switch (ies.resolved) {
  31993                 .anyerror_type => break :blk,
  31994                 .none => {},
  31995                 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk,
  31996             }
  31997 
  31998             if (maybe_operand_val != null) break :blk;
  31999 
  32000             // Try to avoid resolving inferred error set if possible.
  32001             if (ies.errors.count() != 0) return .none;
  32002             switch (ies.resolved) {
  32003                 .anyerror_type => return .none,
  32004                 .none => {},
  32005                 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
  32006                     0 => return .bool_true,
  32007                     else => return .none,
  32008                 },
  32009             }
  32010             // We do not have a comptime answer because this inferred error
  32011             // set is not resolved, and an instruction later in this function
  32012             // body may or may not cause an error to be added to this set.
  32013             return .none;
  32014         },
  32015         else => switch (ip.indexToKey(set_ty)) {
  32016             .error_set_type => |error_set_type| {
  32017                 if (error_set_type.names.len == 0) return .bool_true;
  32018             },
  32019             .inferred_error_set_type => |func_index| blk: {
  32020                 // If the error set is empty, we must return a comptime true or false.
  32021                 // However we want to avoid unnecessarily resolving an inferred error set
  32022                 // in case it is already non-empty.
  32023                 switch (ip.funcIesResolved(func_index).*) {
  32024                     .anyerror_type => break :blk,
  32025                     .none => {},
  32026                     else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk,
  32027                 }
  32028                 if (maybe_operand_val != null) break :blk;
  32029                 if (sema.fn_ret_ty_ies) |ies| {
  32030                     if (ies.func == func_index) {
  32031                         // Try to avoid resolving inferred error set if possible.
  32032                         if (ies.errors.count() != 0) return .none;
  32033                         switch (ies.resolved) {
  32034                             .anyerror_type => return .none,
  32035                             .none => {},
  32036                             else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
  32037                                 0 => return .bool_true,
  32038                                 else => return .none,
  32039                             },
  32040                         }
  32041                         // We do not have a comptime answer because this inferred error
  32042                         // set is not resolved, and an instruction later in this function
  32043                         // body may or may not cause an error to be added to this set.
  32044                         return .none;
  32045                     }
  32046                 }
  32047                 const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty);
  32048                 if (resolved_ty == .anyerror_type)
  32049                     break :blk;
  32050                 if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0)
  32051                     return .bool_true;
  32052             },
  32053             else => unreachable,
  32054         },
  32055     }
  32056 
  32057     if (maybe_operand_val) |err_union| {
  32058         if (err_union.isUndef(mod)) {
  32059             return mod.undefRef(Type.bool);
  32060         }
  32061         if (err_union.getErrorName(mod) == .none) {
  32062             return .bool_true;
  32063         } else {
  32064             return .bool_false;
  32065         }
  32066     }
  32067     return .none;
  32068 }
  32069 
  32070 fn analyzeIsNonErr(
  32071     sema: *Sema,
  32072     block: *Block,
  32073     src: LazySrcLoc,
  32074     operand: Air.Inst.Ref,
  32075 ) CompileError!Air.Inst.Ref {
  32076     const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
  32077     if (result == .none) {
  32078         try sema.requireRuntimeBlock(block, src, null);
  32079         return block.addUnOp(.is_non_err, operand);
  32080     } else {
  32081         return result;
  32082     }
  32083 }
  32084 
  32085 fn analyzePtrIsNonErr(
  32086     sema: *Sema,
  32087     block: *Block,
  32088     src: LazySrcLoc,
  32089     operand: Air.Inst.Ref,
  32090 ) CompileError!Air.Inst.Ref {
  32091     const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand);
  32092     if (result == .none) {
  32093         try sema.requireRuntimeBlock(block, src, null);
  32094         return block.addUnOp(.is_non_err_ptr, operand);
  32095     } else {
  32096         return result;
  32097     }
  32098 }
  32099 
  32100 fn analyzeSlice(
  32101     sema: *Sema,
  32102     block: *Block,
  32103     src: LazySrcLoc,
  32104     ptr_ptr: Air.Inst.Ref,
  32105     uncasted_start: Air.Inst.Ref,
  32106     uncasted_end_opt: Air.Inst.Ref,
  32107     sentinel_opt: Air.Inst.Ref,
  32108     sentinel_src: LazySrcLoc,
  32109     ptr_src: LazySrcLoc,
  32110     start_src: LazySrcLoc,
  32111     end_src: LazySrcLoc,
  32112     by_length: bool,
  32113 ) CompileError!Air.Inst.Ref {
  32114     const mod = sema.mod;
  32115     // Slice expressions can operate on a variable whose type is an array. This requires
  32116     // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
  32117     const ptr_ptr_ty = sema.typeOf(ptr_ptr);
  32118     const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(mod)) {
  32119         .Pointer => ptr_ptr_ty.childType(mod),
  32120         else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(mod)}),
  32121     };
  32122 
  32123     var array_ty = ptr_ptr_child_ty;
  32124     var slice_ty = ptr_ptr_ty;
  32125     var ptr_or_slice = ptr_ptr;
  32126     var elem_ty: Type = undefined;
  32127     var ptr_sentinel: ?Value = null;
  32128     switch (ptr_ptr_child_ty.zigTypeTag(mod)) {
  32129         .Array => {
  32130             ptr_sentinel = ptr_ptr_child_ty.sentinel(mod);
  32131             elem_ty = ptr_ptr_child_ty.childType(mod);
  32132         },
  32133         .Pointer => switch (ptr_ptr_child_ty.ptrSize(mod)) {
  32134             .One => {
  32135                 const double_child_ty = ptr_ptr_child_ty.childType(mod);
  32136                 if (double_child_ty.zigTypeTag(mod) == .Array) {
  32137                     ptr_sentinel = double_child_ty.sentinel(mod);
  32138                     ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  32139                     slice_ty = ptr_ptr_child_ty;
  32140                     array_ty = double_child_ty;
  32141                     elem_ty = double_child_ty.childType(mod);
  32142                 } else {
  32143                     return sema.fail(block, src, "slice of single-item pointer", .{});
  32144                 }
  32145             },
  32146             .Many, .C => {
  32147                 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod);
  32148                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  32149                 slice_ty = ptr_ptr_child_ty;
  32150                 array_ty = ptr_ptr_child_ty;
  32151                 elem_ty = ptr_ptr_child_ty.childType(mod);
  32152 
  32153                 if (ptr_ptr_child_ty.ptrSize(mod) == .C) {
  32154                     if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| {
  32155                         if (ptr_val.isNull(mod)) {
  32156                             return sema.fail(block, src, "slice of null pointer", .{});
  32157                         }
  32158                     }
  32159                 }
  32160             },
  32161             .Slice => {
  32162                 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod);
  32163                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  32164                 slice_ty = ptr_ptr_child_ty;
  32165                 array_ty = ptr_ptr_child_ty;
  32166                 elem_ty = ptr_ptr_child_ty.childType(mod);
  32167             },
  32168         },
  32169         else => return sema.fail(block, src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(mod)}),
  32170     }
  32171 
  32172     const ptr = if (slice_ty.isSlice(mod))
  32173         try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty)
  32174     else if (array_ty.zigTypeTag(mod) == .Array) ptr: {
  32175         var manyptr_ty_key = mod.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type;
  32176         assert(manyptr_ty_key.child == array_ty.toIntern());
  32177         assert(manyptr_ty_key.flags.size == .One);
  32178         manyptr_ty_key.child = elem_ty.toIntern();
  32179         manyptr_ty_key.flags.size = .Many;
  32180         break :ptr try sema.coerceCompatiblePtrs(block, try sema.ptrType(manyptr_ty_key), ptr_or_slice, ptr_src);
  32181     } else ptr_or_slice;
  32182 
  32183     const start = try sema.coerce(block, Type.usize, uncasted_start, start_src);
  32184     const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
  32185     const new_ptr_ty = sema.typeOf(new_ptr);
  32186 
  32187     // true if and only if the end index of the slice, implicitly or explicitly, equals
  32188     // the length of the underlying object being sliced. we might learn the length of the
  32189     // underlying object because it is an array (which has the length in the type), or
  32190     // we might learn of the length because it is a comptime-known slice value.
  32191     var end_is_len = uncasted_end_opt == .none;
  32192     const end = e: {
  32193         if (array_ty.zigTypeTag(mod) == .Array) {
  32194             const len_val = try mod.intValue(Type.usize, array_ty.arrayLen(mod));
  32195 
  32196             if (!end_is_len) {
  32197                 const end = if (by_length) end: {
  32198                     const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  32199                     if (try sema.resolveValue(len)) |slice_len_val| {
  32200                         const len_s_val = try mod.intValue(
  32201                             Type.usize,
  32202                             array_ty.arrayLenIncludingSentinel(mod),
  32203                         );
  32204                         if (!(try sema.compareScalar(slice_len_val, .lte, len_s_val, Type.usize))) {
  32205                             const sentinel_label: []const u8 = if (array_ty.sentinel(mod) != null)
  32206                                 " +1 (sentinel)"
  32207                             else
  32208                                 "";
  32209 
  32210                             return sema.fail(
  32211                                 block,
  32212                                 end_src,
  32213                                 "length {} out of bounds for array of length {}{s}",
  32214                                 .{
  32215                                     slice_len_val.fmtValue(Type.usize, mod),
  32216                                     len_val.fmtValue(Type.usize, mod),
  32217                                     sentinel_label,
  32218                                 },
  32219                             );
  32220                         }
  32221                     }
  32222                     // check len is less than array size if comptime known
  32223                     const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  32224                     break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
  32225                 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  32226                 if (try sema.resolveValue(end)) |end_val| {
  32227                     const len_s_val = try mod.intValue(
  32228                         Type.usize,
  32229                         array_ty.arrayLenIncludingSentinel(mod),
  32230                     );
  32231                     if (!(try sema.compareAll(end_val, .lte, len_s_val, Type.usize))) {
  32232                         const sentinel_label: []const u8 = if (array_ty.sentinel(mod) != null)
  32233                             " +1 (sentinel)"
  32234                         else
  32235                             "";
  32236 
  32237                         return sema.fail(
  32238                             block,
  32239                             end_src,
  32240                             "end index {} out of bounds for array of length {}{s}",
  32241                             .{
  32242                                 end_val.fmtValue(Type.usize, mod),
  32243                                 len_val.fmtValue(Type.usize, mod),
  32244                                 sentinel_label,
  32245                             },
  32246                         );
  32247                     }
  32248 
  32249                     // end_is_len is only true if we are NOT using the sentinel
  32250                     // length. For sentinel-length, we don't want the type to
  32251                     // contain the sentinel.
  32252                     if (end_val.eql(len_val, Type.usize, mod)) {
  32253                         end_is_len = true;
  32254                     }
  32255                 }
  32256                 break :e end;
  32257             }
  32258 
  32259             break :e Air.internedToRef(len_val.toIntern());
  32260         } else if (slice_ty.isSlice(mod)) {
  32261             if (!end_is_len) {
  32262                 const end = if (by_length) end: {
  32263                     const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  32264                     const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  32265                     break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
  32266                 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  32267                 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  32268                     if (try sema.resolveValue(ptr_or_slice)) |slice_val| {
  32269                         if (slice_val.isUndef(mod)) {
  32270                             return sema.fail(block, src, "slice of undefined", .{});
  32271                         }
  32272                         const has_sentinel = slice_ty.sentinel(mod) != null;
  32273                         const slice_len = slice_val.sliceLen(mod);
  32274                         const len_plus_sent = slice_len + @intFromBool(has_sentinel);
  32275                         const slice_len_val_with_sentinel = try mod.intValue(Type.usize, len_plus_sent);
  32276                         if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, Type.usize))) {
  32277                             const sentinel_label: []const u8 = if (has_sentinel)
  32278                                 " +1 (sentinel)"
  32279                             else
  32280                                 "";
  32281 
  32282                             return sema.fail(
  32283                                 block,
  32284                                 end_src,
  32285                                 "end index {} out of bounds for slice of length {d}{s}",
  32286                                 .{
  32287                                     end_val.fmtValue(Type.usize, mod),
  32288                                     slice_val.sliceLen(mod),
  32289                                     sentinel_label,
  32290                                 },
  32291                             );
  32292                         }
  32293 
  32294                         // If the slice has a sentinel, we consider end_is_len
  32295                         // is only true if it equals the length WITHOUT the
  32296                         // sentinel, so we don't add a sentinel type.
  32297                         const slice_len_val = try mod.intValue(Type.usize, slice_len);
  32298                         if (end_val.eql(slice_len_val, Type.usize, mod)) {
  32299                             end_is_len = true;
  32300                         }
  32301                     }
  32302                 }
  32303                 break :e end;
  32304             }
  32305             break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
  32306         }
  32307         if (!end_is_len) {
  32308             if (by_length) {
  32309                 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  32310                 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  32311                 break :e try sema.coerce(block, Type.usize, uncasted_end, end_src);
  32312             } else break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  32313         }
  32314         return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
  32315     };
  32316 
  32317     const sentinel = s: {
  32318         if (sentinel_opt != .none) {
  32319             const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
  32320             break :s try sema.resolveConstDefinedValue(block, sentinel_src, casted, .{
  32321                 .needed_comptime_reason = "slice sentinel must be comptime-known",
  32322             });
  32323         }
  32324         // If we are slicing to the end of something that is sentinel-terminated
  32325         // then the resulting slice type is also sentinel-terminated.
  32326         if (end_is_len) {
  32327             if (ptr_sentinel) |sent| {
  32328                 break :s sent;
  32329             }
  32330         }
  32331         break :s null;
  32332     };
  32333     const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
  32334 
  32335     var checked_start_lte_end = by_length;
  32336     var runtime_src: ?LazySrcLoc = null;
  32337 
  32338     // requirement: start <= end
  32339     if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  32340         if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
  32341             if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
  32342                 return sema.fail(
  32343                     block,
  32344                     start_src,
  32345                     "start index {} is larger than end index {}",
  32346                     .{
  32347                         start_val.fmtValue(Type.usize, mod),
  32348                         end_val.fmtValue(Type.usize, mod),
  32349                     },
  32350                 );
  32351             }
  32352             checked_start_lte_end = true;
  32353             if (try sema.resolveValue(new_ptr)) |ptr_val| sentinel_check: {
  32354                 const expected_sentinel = sentinel orelse break :sentinel_check;
  32355                 const start_int = start_val.getUnsignedInt(mod).?;
  32356                 const end_int = end_val.getUnsignedInt(mod).?;
  32357                 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
  32358 
  32359                 const many_ptr_ty = try mod.manyConstPtrType(elem_ty);
  32360                 const many_ptr_val = try mod.getCoerced(ptr_val, many_ptr_ty);
  32361                 const elem_ptr_ty = try mod.singleConstPtrType(elem_ty);
  32362                 const elem_ptr = try many_ptr_val.elemPtr(elem_ptr_ty, sentinel_index, mod);
  32363                 const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty);
  32364                 const actual_sentinel = switch (res) {
  32365                     .runtime_load => break :sentinel_check,
  32366                     .val => |v| v,
  32367                     .needed_well_defined => |ty| return sema.fail(
  32368                         block,
  32369                         src,
  32370                         "comptime dereference requires '{}' to have a well-defined layout, but it does not.",
  32371                         .{ty.fmt(mod)},
  32372                     ),
  32373                     .out_of_bounds => |ty| return sema.fail(
  32374                         block,
  32375                         end_src,
  32376                         "slice end index {d} exceeds bounds of containing decl of type '{}'",
  32377                         .{ end_int, ty.fmt(mod) },
  32378                     ),
  32379                 };
  32380 
  32381                 if (!actual_sentinel.eql(expected_sentinel, elem_ty, mod)) {
  32382                     const msg = msg: {
  32383                         const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{});
  32384                         errdefer msg.destroy(sema.gpa);
  32385                         try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{
  32386                             expected_sentinel.fmtValue(elem_ty, mod),
  32387                             actual_sentinel.fmtValue(elem_ty, mod),
  32388                         });
  32389 
  32390                         break :msg msg;
  32391                     };
  32392                     return sema.failWithOwnedErrorMsg(block, msg);
  32393                 }
  32394             } else {
  32395                 runtime_src = ptr_src;
  32396             }
  32397         } else {
  32398             runtime_src = start_src;
  32399         }
  32400     } else {
  32401         runtime_src = end_src;
  32402     }
  32403 
  32404     if (!checked_start_lte_end and block.wantSafety() and !block.is_comptime) {
  32405         // requirement: start <= end
  32406         assert(!block.is_comptime);
  32407         try sema.requireRuntimeBlock(block, src, runtime_src.?);
  32408         const ok = try block.addBinOp(.cmp_lte, start, end);
  32409         if (!sema.mod.comp.formatted_panics) {
  32410             try sema.addSafetyCheck(block, src, ok, .start_index_greater_than_end);
  32411         } else {
  32412             try sema.safetyCheckFormatted(block, src, ok, "panicStartGreaterThanEnd", &.{ start, end });
  32413         }
  32414     }
  32415     const new_len = if (by_length)
  32416         try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
  32417     else
  32418         try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
  32419     const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
  32420 
  32421     const new_ptr_ty_info = new_ptr_ty.ptrInfo(mod);
  32422     const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(mod) != .C;
  32423 
  32424     if (opt_new_len_val) |new_len_val| {
  32425         const new_len_int = new_len_val.toUnsignedInt(mod);
  32426 
  32427         const return_ty = try sema.ptrType(.{
  32428             .child = (try mod.arrayType(.{
  32429                 .len = new_len_int,
  32430                 .sentinel = if (sentinel) |s| s.toIntern() else .none,
  32431                 .child = elem_ty.toIntern(),
  32432             })).toIntern(),
  32433             .flags = .{
  32434                 .alignment = new_ptr_ty_info.flags.alignment,
  32435                 .is_const = new_ptr_ty_info.flags.is_const,
  32436                 .is_allowzero = new_allowzero,
  32437                 .is_volatile = new_ptr_ty_info.flags.is_volatile,
  32438                 .address_space = new_ptr_ty_info.flags.address_space,
  32439             },
  32440         });
  32441 
  32442         const opt_new_ptr_val = try sema.resolveValue(new_ptr);
  32443         const new_ptr_val = opt_new_ptr_val orelse {
  32444             const result = try block.addBitCast(return_ty, new_ptr);
  32445             if (block.wantSafety()) {
  32446                 // requirement: slicing C ptr is non-null
  32447                 if (ptr_ptr_child_ty.isCPtr(mod)) {
  32448                     const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
  32449                     try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
  32450                 }
  32451 
  32452                 if (slice_ty.isSlice(mod)) {
  32453                     const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);
  32454                     const actual_len = if (slice_ty.sentinel(mod) == null)
  32455                         slice_len_inst
  32456                     else
  32457                         try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
  32458 
  32459                     const actual_end = if (slice_sentinel != null)
  32460                         try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
  32461                     else
  32462                         end;
  32463 
  32464                     try sema.panicIndexOutOfBounds(block, src, actual_end, actual_len, .cmp_lte);
  32465                 }
  32466 
  32467                 // requirement: result[new_len] == slice_sentinel
  32468                 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
  32469             }
  32470             return result;
  32471         };
  32472 
  32473         if (!new_ptr_val.isUndef(mod)) {
  32474             return Air.internedToRef((try mod.getCoerced(
  32475                 Value.fromInterned((try new_ptr_val.intern(new_ptr_ty, mod))),
  32476                 return_ty,
  32477             )).toIntern());
  32478         }
  32479 
  32480         // Special case: @as([]i32, undefined)[x..x]
  32481         if (new_len_int == 0) {
  32482             return mod.undefRef(return_ty);
  32483         }
  32484 
  32485         return sema.fail(block, src, "non-zero length slice of undefined pointer", .{});
  32486     }
  32487 
  32488     const return_ty = try sema.ptrType(.{
  32489         .child = elem_ty.toIntern(),
  32490         .sentinel = if (sentinel) |s| s.toIntern() else .none,
  32491         .flags = .{
  32492             .size = .Slice,
  32493             .alignment = new_ptr_ty_info.flags.alignment,
  32494             .is_const = new_ptr_ty_info.flags.is_const,
  32495             .is_volatile = new_ptr_ty_info.flags.is_volatile,
  32496             .is_allowzero = new_allowzero,
  32497             .address_space = new_ptr_ty_info.flags.address_space,
  32498         },
  32499     });
  32500 
  32501     try sema.requireRuntimeBlock(block, src, runtime_src.?);
  32502     if (block.wantSafety()) {
  32503         // requirement: slicing C ptr is non-null
  32504         if (ptr_ptr_child_ty.isCPtr(mod)) {
  32505             const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
  32506             try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
  32507         }
  32508 
  32509         // requirement: end <= len
  32510         const opt_len_inst = if (array_ty.zigTypeTag(mod) == .Array)
  32511             try mod.intRef(Type.usize, array_ty.arrayLenIncludingSentinel(mod))
  32512         else if (slice_ty.isSlice(mod)) blk: {
  32513             if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
  32514                 // we don't need to add one for sentinels because the
  32515                 // underlying value data includes the sentinel
  32516                 break :blk try mod.intRef(Type.usize, slice_val.sliceLen(mod));
  32517             }
  32518 
  32519             const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);
  32520             if (slice_ty.sentinel(mod) == null) break :blk slice_len_inst;
  32521 
  32522             // we have to add one because slice lengths don't include the sentinel
  32523             break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
  32524         } else null;
  32525         if (opt_len_inst) |len_inst| {
  32526             const actual_end = if (slice_sentinel != null)
  32527                 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
  32528             else
  32529                 end;
  32530             try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte);
  32531         }
  32532 
  32533         // requirement: start <= end
  32534         try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
  32535     }
  32536     const result = try block.addInst(.{
  32537         .tag = .slice,
  32538         .data = .{ .ty_pl = .{
  32539             .ty = Air.internedToRef(return_ty.toIntern()),
  32540             .payload = try sema.addExtra(Air.Bin{
  32541                 .lhs = new_ptr,
  32542                 .rhs = new_len,
  32543             }),
  32544         } },
  32545     });
  32546     if (block.wantSafety()) {
  32547         // requirement: result[new_len] == slice_sentinel
  32548         try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
  32549     }
  32550     return result;
  32551 }
  32552 
  32553 /// Asserts that lhs and rhs types are both numeric.
  32554 fn cmpNumeric(
  32555     sema: *Sema,
  32556     block: *Block,
  32557     src: LazySrcLoc,
  32558     uncasted_lhs: Air.Inst.Ref,
  32559     uncasted_rhs: Air.Inst.Ref,
  32560     op: std.math.CompareOperator,
  32561     lhs_src: LazySrcLoc,
  32562     rhs_src: LazySrcLoc,
  32563 ) CompileError!Air.Inst.Ref {
  32564     const mod = sema.mod;
  32565     const lhs_ty = sema.typeOf(uncasted_lhs);
  32566     const rhs_ty = sema.typeOf(uncasted_rhs);
  32567 
  32568     assert(lhs_ty.isNumeric(mod));
  32569     assert(rhs_ty.isNumeric(mod));
  32570 
  32571     const lhs_ty_tag = lhs_ty.zigTypeTag(mod);
  32572     const rhs_ty_tag = rhs_ty.zigTypeTag(mod);
  32573     const target = mod.getTarget();
  32574 
  32575     // One exception to heterogeneous comparison: comptime_float needs to
  32576     // coerce to fixed-width float.
  32577 
  32578     const lhs = if (lhs_ty_tag == .ComptimeFloat and rhs_ty_tag == .Float)
  32579         try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src)
  32580     else
  32581         uncasted_lhs;
  32582 
  32583     const rhs = if (lhs_ty_tag == .Float and rhs_ty_tag == .ComptimeFloat)
  32584         try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src)
  32585     else
  32586         uncasted_rhs;
  32587 
  32588     const runtime_src: LazySrcLoc = src: {
  32589         if (try sema.resolveValue(lhs)) |lhs_val| {
  32590             if (try sema.resolveValue(rhs)) |rhs_val| {
  32591                 // Compare ints: const vs. undefined (or vice versa)
  32592                 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)) {
  32593                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
  32594                         return if (res) .bool_true else .bool_false;
  32595                     }
  32596                 } 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)) {
  32597                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
  32598                         return if (res) .bool_true else .bool_false;
  32599                     }
  32600                 }
  32601 
  32602                 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  32603                     return mod.undefRef(Type.bool);
  32604                 }
  32605                 if (lhs_val.isNan(mod) or rhs_val.isNan(mod)) {
  32606                     return if (op == std.math.CompareOperator.neq) .bool_true else .bool_false;
  32607                 }
  32608                 return if (try Value.compareHeteroAdvanced(lhs_val, op, rhs_val, mod, sema))
  32609                     .bool_true
  32610                 else
  32611                     .bool_false;
  32612             } else {
  32613                 if (!lhs_val.isUndef(mod) and (lhs_ty.isInt(mod) or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt(mod)) {
  32614                     // Compare ints: const vs. var
  32615                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
  32616                         return if (res) .bool_true else .bool_false;
  32617                     }
  32618                 }
  32619                 break :src rhs_src;
  32620             }
  32621         } else {
  32622             if (try sema.resolveValueResolveLazy(rhs)) |rhs_val| {
  32623                 if (!rhs_val.isUndef(mod) and (rhs_ty.isInt(mod) or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt(mod)) {
  32624                     // Compare ints: var vs. const
  32625                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
  32626                         return if (res) .bool_true else .bool_false;
  32627                     }
  32628                 }
  32629             }
  32630             break :src lhs_src;
  32631         }
  32632     };
  32633 
  32634     // TODO handle comparisons against lazy zero values
  32635     // Some values can be compared against zero without being runtime-known or without forcing
  32636     // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
  32637     // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
  32638     // of this function if we don't need to.
  32639     try sema.requireRuntimeBlock(block, src, runtime_src);
  32640 
  32641     // For floats, emit a float comparison instruction.
  32642     const lhs_is_float = switch (lhs_ty_tag) {
  32643         .Float, .ComptimeFloat => true,
  32644         else => false,
  32645     };
  32646     const rhs_is_float = switch (rhs_ty_tag) {
  32647         .Float, .ComptimeFloat => true,
  32648         else => false,
  32649     };
  32650 
  32651     if (lhs_is_float and rhs_is_float) {
  32652         // Smaller fixed-width floats coerce to larger fixed-width floats.
  32653         // comptime_float coerces to fixed-width float.
  32654         const dest_ty = x: {
  32655             if (lhs_ty_tag == .ComptimeFloat) {
  32656                 break :x rhs_ty;
  32657             } else if (rhs_ty_tag == .ComptimeFloat) {
  32658                 break :x lhs_ty;
  32659             }
  32660             if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) {
  32661                 break :x lhs_ty;
  32662             } else {
  32663                 break :x rhs_ty;
  32664             }
  32665         };
  32666         const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
  32667         const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
  32668         return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs);
  32669     }
  32670     // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
  32671     // For mixed signed and unsigned integers, implicit cast both operands to a signed
  32672     // integer with + 1 bit.
  32673     // For mixed floats and integers, extract the integer part from the float, cast that to
  32674     // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
  32675     // add/subtract 1.
  32676     const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|
  32677         !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema))
  32678     else
  32679         (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(mod));
  32680     const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
  32681         !(try rhs_val.compareAllWithZeroAdvanced(.gte, sema))
  32682     else
  32683         (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(mod));
  32684     const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
  32685 
  32686     var dest_float_type: ?Type = null;
  32687 
  32688     var lhs_bits: usize = undefined;
  32689     if (try sema.resolveValueResolveLazy(lhs)) |lhs_val| {
  32690         if (lhs_val.isUndef(mod))
  32691             return mod.undefRef(Type.bool);
  32692         if (lhs_val.isNan(mod)) switch (op) {
  32693             .neq => return .bool_true,
  32694             else => return .bool_false,
  32695         };
  32696         if (lhs_val.isInf(mod)) switch (op) {
  32697             .neq => return .bool_true,
  32698             .eq => return .bool_false,
  32699             .gt, .gte => return if (lhs_val.isNegativeInf(mod)) .bool_false else .bool_true,
  32700             .lt, .lte => return if (lhs_val.isNegativeInf(mod)) .bool_true else .bool_false,
  32701         };
  32702         if (!rhs_is_signed) {
  32703             switch (lhs_val.orderAgainstZero(mod)) {
  32704                 .gt => {},
  32705                 .eq => switch (op) { // LHS = 0, RHS is unsigned
  32706                     .lte => return .bool_true,
  32707                     .gt => return .bool_false,
  32708                     else => {},
  32709                 },
  32710                 .lt => switch (op) { // LHS < 0, RHS is unsigned
  32711                     .neq, .lt, .lte => return .bool_true,
  32712                     .eq, .gt, .gte => return .bool_false,
  32713                 },
  32714             }
  32715         }
  32716         if (lhs_is_float) {
  32717             if (lhs_val.floatHasFraction(mod)) {
  32718                 switch (op) {
  32719                     .eq => return .bool_false,
  32720                     .neq => return .bool_true,
  32721                     else => {},
  32722                 }
  32723             }
  32724 
  32725             var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128, mod));
  32726             defer bigint.deinit();
  32727             if (lhs_val.floatHasFraction(mod)) {
  32728                 if (lhs_is_signed) {
  32729                     try bigint.addScalar(&bigint, -1);
  32730                 } else {
  32731                     try bigint.addScalar(&bigint, 1);
  32732                 }
  32733             }
  32734             lhs_bits = bigint.toConst().bitCountTwosComp();
  32735         } else {
  32736             lhs_bits = lhs_val.intBitCountTwosComp(mod);
  32737         }
  32738         lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed);
  32739     } else if (lhs_is_float) {
  32740         dest_float_type = lhs_ty;
  32741     } else {
  32742         const int_info = lhs_ty.intInfo(mod);
  32743         lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
  32744     }
  32745 
  32746     var rhs_bits: usize = undefined;
  32747     if (try sema.resolveValueResolveLazy(rhs)) |rhs_val| {
  32748         if (rhs_val.isUndef(mod))
  32749             return mod.undefRef(Type.bool);
  32750         if (rhs_val.isNan(mod)) switch (op) {
  32751             .neq => return .bool_true,
  32752             else => return .bool_false,
  32753         };
  32754         if (rhs_val.isInf(mod)) switch (op) {
  32755             .neq => return .bool_true,
  32756             .eq => return .bool_false,
  32757             .gt, .gte => return if (rhs_val.isNegativeInf(mod)) .bool_true else .bool_false,
  32758             .lt, .lte => return if (rhs_val.isNegativeInf(mod)) .bool_false else .bool_true,
  32759         };
  32760         if (!lhs_is_signed) {
  32761             switch (rhs_val.orderAgainstZero(mod)) {
  32762                 .gt => {},
  32763                 .eq => switch (op) { // RHS = 0, LHS is unsigned
  32764                     .gte => return .bool_true,
  32765                     .lt => return .bool_false,
  32766                     else => {},
  32767                 },
  32768                 .lt => switch (op) { // RHS < 0, LHS is unsigned
  32769                     .neq, .gt, .gte => return .bool_true,
  32770                     .eq, .lt, .lte => return .bool_false,
  32771                 },
  32772             }
  32773         }
  32774         if (rhs_is_float) {
  32775             if (rhs_val.floatHasFraction(mod)) {
  32776                 switch (op) {
  32777                     .eq => return .bool_false,
  32778                     .neq => return .bool_true,
  32779                     else => {},
  32780                 }
  32781             }
  32782 
  32783             var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128, mod));
  32784             defer bigint.deinit();
  32785             if (rhs_val.floatHasFraction(mod)) {
  32786                 if (rhs_is_signed) {
  32787                     try bigint.addScalar(&bigint, -1);
  32788                 } else {
  32789                     try bigint.addScalar(&bigint, 1);
  32790                 }
  32791             }
  32792             rhs_bits = bigint.toConst().bitCountTwosComp();
  32793         } else {
  32794             rhs_bits = rhs_val.intBitCountTwosComp(mod);
  32795         }
  32796         rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed);
  32797     } else if (rhs_is_float) {
  32798         dest_float_type = rhs_ty;
  32799     } else {
  32800         const int_info = rhs_ty.intInfo(mod);
  32801         rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
  32802     }
  32803 
  32804     const dest_ty = if (dest_float_type) |ft| ft else blk: {
  32805         const max_bits = @max(lhs_bits, rhs_bits);
  32806         const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits});
  32807         const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
  32808         break :blk try mod.intType(signedness, casted_bits);
  32809     };
  32810     const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
  32811     const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
  32812 
  32813     return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs);
  32814 }
  32815 
  32816 /// Asserts that LHS value is an int or comptime int and not undefined, and
  32817 /// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to
  32818 /// determine whether `op` has a guaranteed result.
  32819 /// If it cannot be determined, returns null.
  32820 /// Otherwise returns a bool for the guaranteed comparison operation.
  32821 fn compareIntsOnlyPossibleResult(
  32822     sema: *Sema,
  32823     lhs_val: Value,
  32824     op: std.math.CompareOperator,
  32825     rhs_ty: Type,
  32826 ) Allocator.Error!?bool {
  32827     const mod = sema.mod;
  32828     const rhs_info = rhs_ty.intInfo(mod);
  32829     const vs_zero = lhs_val.orderAgainstZeroAdvanced(mod, sema) catch unreachable;
  32830     const is_zero = vs_zero == .eq;
  32831     const is_negative = vs_zero == .lt;
  32832     const is_positive = vs_zero == .gt;
  32833 
  32834     // Anything vs. zero-sized type has guaranteed outcome.
  32835     if (rhs_info.bits == 0) return switch (op) {
  32836         .eq, .lte, .gte => is_zero,
  32837         .neq, .lt, .gt => !is_zero,
  32838     };
  32839 
  32840     // Special case for i1, which can only be 0 or -1.
  32841     // Zero and positive ints have guaranteed outcome.
  32842     if (rhs_info.bits == 1 and rhs_info.signedness == .signed) {
  32843         if (is_positive) return switch (op) {
  32844             .gt, .gte, .neq => true,
  32845             .lt, .lte, .eq => false,
  32846         };
  32847         if (is_zero) return switch (op) {
  32848             .gte => true,
  32849             .lt => false,
  32850             .gt, .lte, .eq, .neq => null,
  32851         };
  32852     }
  32853 
  32854     // Negative vs. unsigned has guaranteed outcome.
  32855     if (rhs_info.signedness == .unsigned and is_negative) return switch (op) {
  32856         .eq, .gt, .gte => false,
  32857         .neq, .lt, .lte => true,
  32858     };
  32859 
  32860     const sign_adj = @intFromBool(!is_negative and rhs_info.signedness == .signed);
  32861     const req_bits = lhs_val.intBitCountTwosComp(mod) + sign_adj;
  32862 
  32863     // No sized type can have more than 65535 bits.
  32864     // The RHS type operand is either a runtime value or sized (but undefined) constant.
  32865     if (req_bits > 65535) return switch (op) {
  32866         .lt, .lte => is_negative,
  32867         .gt, .gte => is_positive,
  32868         .eq => false,
  32869         .neq => true,
  32870     };
  32871     const fits = req_bits <= rhs_info.bits;
  32872 
  32873     // Oversized int has guaranteed outcome.
  32874     switch (op) {
  32875         .eq => return if (!fits) false else null,
  32876         .neq => return if (!fits) true else null,
  32877         .lt, .lte => if (!fits) return is_negative,
  32878         .gt, .gte => if (!fits) return !is_negative,
  32879     }
  32880 
  32881     // For any other comparison, we need to know if the LHS value is
  32882     // equal to the maximum or minimum possible value of the RHS type.
  32883     const is_min, const is_max = edge: {
  32884         if (is_zero and rhs_info.signedness == .unsigned) break :edge .{ true, false };
  32885 
  32886         if (req_bits != rhs_info.bits) break :edge .{ false, false };
  32887 
  32888         const ty = try mod.intType(
  32889             if (is_negative) .signed else .unsigned,
  32890             @intCast(req_bits),
  32891         );
  32892         const pop_count = lhs_val.popCount(ty, mod);
  32893 
  32894         if (is_negative) {
  32895             break :edge .{ pop_count == 1, false };
  32896         } else {
  32897             break :edge .{ false, pop_count == req_bits - sign_adj };
  32898         }
  32899     };
  32900 
  32901     assert(fits);
  32902     return switch (op) {
  32903         .lt => if (is_max) false else null,
  32904         .lte => if (is_min) true else null,
  32905         .gt => if (is_min) false else null,
  32906         .gte => if (is_max) true else null,
  32907         .eq, .neq => unreachable,
  32908     };
  32909 }
  32910 
  32911 /// Asserts that lhs and rhs types are both vectors.
  32912 fn cmpVector(
  32913     sema: *Sema,
  32914     block: *Block,
  32915     src: LazySrcLoc,
  32916     lhs: Air.Inst.Ref,
  32917     rhs: Air.Inst.Ref,
  32918     op: std.math.CompareOperator,
  32919     lhs_src: LazySrcLoc,
  32920     rhs_src: LazySrcLoc,
  32921 ) CompileError!Air.Inst.Ref {
  32922     const mod = sema.mod;
  32923     const lhs_ty = sema.typeOf(lhs);
  32924     const rhs_ty = sema.typeOf(rhs);
  32925     assert(lhs_ty.zigTypeTag(mod) == .Vector);
  32926     assert(rhs_ty.zigTypeTag(mod) == .Vector);
  32927     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  32928 
  32929     const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } });
  32930     const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src);
  32931     const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src);
  32932 
  32933     const result_ty = try mod.vectorType(.{
  32934         .len = lhs_ty.vectorLen(mod),
  32935         .child = .bool_type,
  32936     });
  32937 
  32938     const runtime_src: LazySrcLoc = src: {
  32939         if (try sema.resolveValue(casted_lhs)) |lhs_val| {
  32940             if (try sema.resolveValue(casted_rhs)) |rhs_val| {
  32941                 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  32942                     return mod.undefRef(result_ty);
  32943                 }
  32944                 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
  32945                 return Air.internedToRef(cmp_val.toIntern());
  32946             } else {
  32947                 break :src rhs_src;
  32948             }
  32949         } else {
  32950             break :src lhs_src;
  32951         }
  32952     };
  32953 
  32954     try sema.requireRuntimeBlock(block, src, runtime_src);
  32955     return block.addCmpVector(casted_lhs, casted_rhs, op);
  32956 }
  32957 
  32958 fn wrapOptional(
  32959     sema: *Sema,
  32960     block: *Block,
  32961     dest_ty: Type,
  32962     inst: Air.Inst.Ref,
  32963     inst_src: LazySrcLoc,
  32964 ) !Air.Inst.Ref {
  32965     if (try sema.resolveValue(inst)) |val| {
  32966         return Air.internedToRef((try sema.mod.intern(.{ .opt = .{
  32967             .ty = dest_ty.toIntern(),
  32968             .val = val.toIntern(),
  32969         } })));
  32970     }
  32971 
  32972     try sema.requireRuntimeBlock(block, inst_src, null);
  32973     return block.addTyOp(.wrap_optional, dest_ty, inst);
  32974 }
  32975 
  32976 fn wrapErrorUnionPayload(
  32977     sema: *Sema,
  32978     block: *Block,
  32979     dest_ty: Type,
  32980     inst: Air.Inst.Ref,
  32981     inst_src: LazySrcLoc,
  32982 ) !Air.Inst.Ref {
  32983     const mod = sema.mod;
  32984     const dest_payload_ty = dest_ty.errorUnionPayload(mod);
  32985     const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false });
  32986     if (try sema.resolveValue(coerced)) |val| {
  32987         return Air.internedToRef((try mod.intern(.{ .error_union = .{
  32988             .ty = dest_ty.toIntern(),
  32989             .val = .{ .payload = try val.intern(dest_payload_ty, mod) },
  32990         } })));
  32991     }
  32992     try sema.requireRuntimeBlock(block, inst_src, null);
  32993     try sema.queueFullTypeResolution(dest_payload_ty);
  32994     return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
  32995 }
  32996 
  32997 fn wrapErrorUnionSet(
  32998     sema: *Sema,
  32999     block: *Block,
  33000     dest_ty: Type,
  33001     inst: Air.Inst.Ref,
  33002     inst_src: LazySrcLoc,
  33003 ) !Air.Inst.Ref {
  33004     const mod = sema.mod;
  33005     const ip = &mod.intern_pool;
  33006     const inst_ty = sema.typeOf(inst);
  33007     const dest_err_set_ty = dest_ty.errorUnionSet(mod);
  33008     if (try sema.resolveValue(inst)) |val| {
  33009         const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name;
  33010         switch (dest_err_set_ty.toIntern()) {
  33011             .anyerror_type => {},
  33012             .adhoc_inferred_error_set_type => ok: {
  33013                 const ies = sema.fn_ret_ty_ies.?;
  33014                 switch (ies.resolved) {
  33015                     .anyerror_type => break :ok,
  33016                     .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) {
  33017                         break :ok;
  33018                     },
  33019                     else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) {
  33020                         break :ok;
  33021                     },
  33022                 }
  33023                 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  33024             },
  33025             else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) {
  33026                 .error_set_type => |error_set_type| ok: {
  33027                     if (error_set_type.nameIndex(ip, expected_name) != null) break :ok;
  33028                     return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  33029                 },
  33030                 .inferred_error_set_type => |func_index| ok: {
  33031                     // We carefully do this in an order that avoids unnecessarily
  33032                     // resolving the destination error set type.
  33033                     switch (ip.funcIesResolved(func_index).*) {
  33034                         .anyerror_type => break :ok,
  33035                         .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) {
  33036                             break :ok;
  33037                         },
  33038                         else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) {
  33039                             break :ok;
  33040                         },
  33041                     }
  33042 
  33043                     return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  33044                 },
  33045                 else => unreachable,
  33046             },
  33047         }
  33048         return Air.internedToRef((try mod.intern(.{ .error_union = .{
  33049             .ty = dest_ty.toIntern(),
  33050             .val = .{ .err_name = expected_name },
  33051         } })));
  33052     }
  33053 
  33054     try sema.requireRuntimeBlock(block, inst_src, null);
  33055     const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src);
  33056     return block.addTyOp(.wrap_errunion_err, dest_ty, coerced);
  33057 }
  33058 
  33059 fn unionToTag(
  33060     sema: *Sema,
  33061     block: *Block,
  33062     enum_ty: Type,
  33063     un: Air.Inst.Ref,
  33064     un_src: LazySrcLoc,
  33065 ) !Air.Inst.Ref {
  33066     const mod = sema.mod;
  33067     if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| {
  33068         return Air.internedToRef(opv.toIntern());
  33069     }
  33070     if (try sema.resolveValue(un)) |un_val| {
  33071         return Air.internedToRef(un_val.unionTag(mod).?.toIntern());
  33072     }
  33073     try sema.requireRuntimeBlock(block, un_src, null);
  33074     return block.addTyOp(.get_union_tag, enum_ty, un);
  33075 }
  33076 
  33077 const PeerResolveStrategy = enum {
  33078     /// The type is not known.
  33079     /// If refined no further, this is equivalent to `exact`.
  33080     unknown,
  33081     /// The type may be an error set or error union.
  33082     /// If refined no further, it is an error set.
  33083     error_set,
  33084     /// The type must be some error union.
  33085     error_union,
  33086     /// The type may be @TypeOf(null), an optional or a C pointer.
  33087     /// If refined no further, it is @TypeOf(null).
  33088     nullable,
  33089     /// The type must be some optional or a C pointer.
  33090     /// If refined no further, it is an optional.
  33091     optional,
  33092     /// The type must be either an array or a vector.
  33093     /// If refined no further, it is an array.
  33094     array,
  33095     /// The type must be a vector.
  33096     vector,
  33097     /// The type must be a C pointer.
  33098     c_ptr,
  33099     /// The type must be a pointer (C or not).
  33100     /// If refined no further, it is a non-C pointer.
  33101     ptr,
  33102     /// The type must be a function or a pointer to a function.
  33103     /// If refined no further, it is a function.
  33104     func,
  33105     /// The type must be an enum literal, or some specific enum or union. Which one is decided
  33106     /// afterwards based on the types in question.
  33107     enum_or_union,
  33108     /// The type must be some integer or float type.
  33109     /// If refined no further, it is `comptime_int`.
  33110     comptime_int,
  33111     /// The type must be some float type.
  33112     /// If refined no further, it is `comptime_float`.
  33113     comptime_float,
  33114     /// The type must be some float or fixed-width integer type.
  33115     /// If refined no further, it is some fixed-width integer type.
  33116     fixed_int,
  33117     /// The type must be some fixed-width float type.
  33118     fixed_float,
  33119     /// The type must be a struct literal or tuple type.
  33120     coercible_struct,
  33121     /// The peers must all be of the same type.
  33122     exact,
  33123 
  33124     /// Given two strategies, find a strategy that satisfies both, if one exists. If no such
  33125     /// strategy exists, any strategy may be returned; an error will be emitted when the caller
  33126     /// attempts to use the strategy to resolve the type.
  33127     /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at
  33128     /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy.
  33129     fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy {
  33130         // Our merging should be order-independent. Thus, even though the union order is arbitrary,
  33131         // by sorting the tags and switching first on the smaller, we have half as many cases to
  33132         // worry about (since we avoid the duplicates).
  33133         const s0_is_a = @intFromEnum(a) <= @intFromEnum(b);
  33134         const s0 = if (s0_is_a) a else b;
  33135         const s1 = if (s0_is_a) b else a;
  33136 
  33137         const ReasonMethod = enum {
  33138             all_s0,
  33139             all_s1,
  33140             either,
  33141         };
  33142 
  33143         const reason_method: ReasonMethod, const strat: PeerResolveStrategy = switch (s0) {
  33144             .unknown => .{ .all_s1, s1 },
  33145             .error_set => switch (s1) {
  33146                 .error_set => .{ .either, .error_set },
  33147                 else => .{ .all_s0, .error_union },
  33148             },
  33149             .error_union => switch (s1) {
  33150                 .error_union => .{ .either, .error_union },
  33151                 else => .{ .all_s0, .error_union },
  33152             },
  33153             .nullable => switch (s1) {
  33154                 .nullable => .{ .either, .nullable },
  33155                 .c_ptr => .{ .all_s1, .c_ptr },
  33156                 else => .{ .all_s0, .optional },
  33157             },
  33158             .optional => switch (s1) {
  33159                 .optional => .{ .either, .optional },
  33160                 .c_ptr => .{ .all_s1, .c_ptr },
  33161                 else => .{ .all_s0, .optional },
  33162             },
  33163             .array => switch (s1) {
  33164                 .array => .{ .either, .array },
  33165                 .vector => .{ .all_s1, .vector },
  33166                 else => .{ .all_s0, .array },
  33167             },
  33168             .vector => switch (s1) {
  33169                 .vector => .{ .either, .vector },
  33170                 else => .{ .all_s0, .vector },
  33171             },
  33172             .c_ptr => switch (s1) {
  33173                 .c_ptr => .{ .either, .c_ptr },
  33174                 else => .{ .all_s0, .c_ptr },
  33175             },
  33176             .ptr => switch (s1) {
  33177                 .ptr => .{ .either, .ptr },
  33178                 else => .{ .all_s0, .ptr },
  33179             },
  33180             .func => switch (s1) {
  33181                 .func => .{ .either, .func },
  33182                 else => .{ .all_s1, s1 }, // doesn't override anything later
  33183             },
  33184             .enum_or_union => switch (s1) {
  33185                 .enum_or_union => .{ .either, .enum_or_union },
  33186                 else => .{ .all_s0, .enum_or_union },
  33187             },
  33188             .comptime_int => switch (s1) {
  33189                 .comptime_int => .{ .either, .comptime_int },
  33190                 else => .{ .all_s1, s1 }, // doesn't override anything later
  33191             },
  33192             .comptime_float => switch (s1) {
  33193                 .comptime_float => .{ .either, .comptime_float },
  33194                 else => .{ .all_s1, s1 }, // doesn't override anything later
  33195             },
  33196             .fixed_int => switch (s1) {
  33197                 .fixed_int => .{ .either, .fixed_int },
  33198                 else => .{ .all_s1, s1 }, // doesn't override anything later
  33199             },
  33200             .fixed_float => switch (s1) {
  33201                 .fixed_float => .{ .either, .fixed_float },
  33202                 else => .{ .all_s1, s1 }, // doesn't override anything later
  33203             },
  33204             .coercible_struct => switch (s1) {
  33205                 .exact => .{ .all_s1, .exact },
  33206                 else => .{ .all_s0, .coercible_struct },
  33207             },
  33208             .exact => .{ .all_s0, .exact },
  33209         };
  33210 
  33211         switch (reason_method) {
  33212             .all_s0 => {
  33213                 if (!s0_is_a) {
  33214                     reason_peer.* = b_peer_idx;
  33215                 }
  33216             },
  33217             .all_s1 => {
  33218                 if (s0_is_a) {
  33219                     reason_peer.* = b_peer_idx;
  33220                 }
  33221             },
  33222             .either => {
  33223                 // Prefer the earliest peer
  33224                 reason_peer.* = @min(reason_peer.*, b_peer_idx);
  33225             },
  33226         }
  33227 
  33228         return strat;
  33229     }
  33230 
  33231     fn select(ty: Type, mod: *Module) PeerResolveStrategy {
  33232         return switch (ty.zigTypeTag(mod)) {
  33233             .Type, .Void, .Bool, .Opaque, .Frame, .AnyFrame => .exact,
  33234             .NoReturn, .Undefined => .unknown,
  33235             .Null => .nullable,
  33236             .ComptimeInt => .comptime_int,
  33237             .Int => .fixed_int,
  33238             .ComptimeFloat => .comptime_float,
  33239             .Float => .fixed_float,
  33240             .Pointer => if (ty.ptrInfo(mod).flags.size == .C) .c_ptr else .ptr,
  33241             .Array => .array,
  33242             .Vector => .vector,
  33243             .Optional => .optional,
  33244             .ErrorSet => .error_set,
  33245             .ErrorUnion => .error_union,
  33246             .EnumLiteral, .Enum, .Union => .enum_or_union,
  33247             .Struct => if (ty.isTupleOrAnonStruct(mod)) .coercible_struct else .exact,
  33248             .Fn => .func,
  33249         };
  33250     }
  33251 };
  33252 
  33253 const PeerResolveResult = union(enum) {
  33254     /// The peer type resolution was successful, and resulted in the given type.
  33255     success: Type,
  33256     /// There was some generic conflict between two peers.
  33257     conflict: struct {
  33258         peer_idx_a: usize,
  33259         peer_idx_b: usize,
  33260     },
  33261     /// There was an error when resolving the type of a struct or tuple field.
  33262     field_error: struct {
  33263         /// The name of the field which caused the failure.
  33264         field_name: []const u8,
  33265         /// The type of this field in each peer.
  33266         field_types: []Type,
  33267         /// The error from resolving the field type. Guaranteed not to be `success`.
  33268         sub_result: *PeerResolveResult,
  33269     },
  33270 
  33271     fn report(
  33272         result: PeerResolveResult,
  33273         sema: *Sema,
  33274         block: *Block,
  33275         src: LazySrcLoc,
  33276         instructions: []const Air.Inst.Ref,
  33277         candidate_srcs: Module.PeerTypeCandidateSrc,
  33278     ) !*Module.ErrorMsg {
  33279         const mod = sema.mod;
  33280         const decl_ptr = mod.declPtr(block.src_decl);
  33281 
  33282         var opt_msg: ?*Module.ErrorMsg = null;
  33283         errdefer if (opt_msg) |msg| msg.destroy(sema.gpa);
  33284 
  33285         // If we mention fields we'll want to include field types, so put peer types in a buffer
  33286         var peer_tys = try sema.arena.alloc(Type, instructions.len);
  33287         for (peer_tys, instructions) |*ty, inst| {
  33288             ty.* = sema.typeOf(inst);
  33289         }
  33290 
  33291         var cur = result;
  33292         while (true) {
  33293             var conflict_idx: [2]usize = undefined;
  33294 
  33295             switch (cur) {
  33296                 .success => unreachable,
  33297                 .conflict => |conflict| {
  33298                     // Fall through to two-peer conflict handling below
  33299                     conflict_idx = .{
  33300                         conflict.peer_idx_a,
  33301                         conflict.peer_idx_b,
  33302                     };
  33303                 },
  33304                 .field_error => |field_error| {
  33305                     const fmt = "struct field '{s}' has conflicting types";
  33306                     const args = .{field_error.field_name};
  33307                     if (opt_msg) |msg| {
  33308                         try sema.errNote(block, src, msg, fmt, args);
  33309                     } else {
  33310                         opt_msg = try sema.errMsg(block, src, fmt, args);
  33311                     }
  33312 
  33313                     // Continue on to child error
  33314                     cur = field_error.sub_result.*;
  33315                     peer_tys = field_error.field_types;
  33316                     continue;
  33317                 },
  33318             }
  33319 
  33320             // This is the path for reporting a generic conflict between two peers.
  33321 
  33322             if (conflict_idx[1] < conflict_idx[0]) {
  33323                 // b comes first in source, so it's better if it comes first in the error
  33324                 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]);
  33325             }
  33326 
  33327             const conflict_tys: [2]Type = .{
  33328                 peer_tys[conflict_idx[0]],
  33329                 peer_tys[conflict_idx[1]],
  33330             };
  33331             const conflict_srcs: [2]?LazySrcLoc = .{
  33332                 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[0]),
  33333                 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[1]),
  33334             };
  33335 
  33336             const fmt = "incompatible types: '{}' and '{}'";
  33337             const args = .{
  33338                 conflict_tys[0].fmt(mod),
  33339                 conflict_tys[1].fmt(mod),
  33340             };
  33341             const msg = if (opt_msg) |msg| msg: {
  33342                 try sema.errNote(block, src, msg, fmt, args);
  33343                 break :msg msg;
  33344             } else msg: {
  33345                 const msg = try sema.errMsg(block, src, fmt, args);
  33346                 opt_msg = msg;
  33347                 break :msg msg;
  33348             };
  33349 
  33350             if (conflict_srcs[0]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[0].fmt(mod)});
  33351             if (conflict_srcs[1]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[1].fmt(mod)});
  33352 
  33353             // No child error
  33354             break;
  33355         }
  33356 
  33357         return opt_msg.?;
  33358     }
  33359 };
  33360 
  33361 fn resolvePeerTypes(
  33362     sema: *Sema,
  33363     block: *Block,
  33364     src: LazySrcLoc,
  33365     instructions: []const Air.Inst.Ref,
  33366     candidate_srcs: Module.PeerTypeCandidateSrc,
  33367 ) !Type {
  33368     switch (instructions.len) {
  33369         0 => return Type.noreturn,
  33370         1 => return sema.typeOf(instructions[0]),
  33371         else => {},
  33372     }
  33373 
  33374     const peer_tys = try sema.arena.alloc(?Type, instructions.len);
  33375     const peer_vals = try sema.arena.alloc(?Value, instructions.len);
  33376 
  33377     for (instructions, peer_tys, peer_vals) |inst, *ty, *val| {
  33378         ty.* = sema.typeOf(inst);
  33379         val.* = try sema.resolveValue(inst);
  33380     }
  33381 
  33382     switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) {
  33383         .success => |ty| return ty,
  33384         else => |result| {
  33385             const msg = try result.report(sema, block, src, instructions, candidate_srcs);
  33386             return sema.failWithOwnedErrorMsg(block, msg);
  33387         },
  33388     }
  33389 }
  33390 
  33391 fn resolvePeerTypesInner(
  33392     sema: *Sema,
  33393     block: *Block,
  33394     src: LazySrcLoc,
  33395     peer_tys: []?Type,
  33396     peer_vals: []?Value,
  33397 ) !PeerResolveResult {
  33398     const mod = sema.mod;
  33399     const ip = &mod.intern_pool;
  33400 
  33401     var strat_reason: usize = 0;
  33402     var s: PeerResolveStrategy = .unknown;
  33403     for (peer_tys, 0..) |opt_ty, i| {
  33404         const ty = opt_ty orelse continue;
  33405         s = s.merge(PeerResolveStrategy.select(ty, mod), &strat_reason, i);
  33406     }
  33407 
  33408     if (s == .unknown) {
  33409         // The whole thing was noreturn or undefined - try to do an exact match
  33410         s = .exact;
  33411     } else {
  33412         // There was something other than noreturn and undefined, so we can ignore those peers
  33413         for (peer_tys) |*ty_ptr| {
  33414             const ty = ty_ptr.* orelse continue;
  33415             switch (ty.zigTypeTag(mod)) {
  33416                 .NoReturn, .Undefined => ty_ptr.* = null,
  33417                 else => {},
  33418             }
  33419         }
  33420     }
  33421 
  33422     const target = mod.getTarget();
  33423 
  33424     switch (s) {
  33425         .unknown => unreachable,
  33426 
  33427         .error_set => {
  33428             var final_set: ?Type = null;
  33429             for (peer_tys, 0..) |opt_ty, i| {
  33430                 const ty = opt_ty orelse continue;
  33431                 if (ty.zigTypeTag(mod) != .ErrorSet) return .{ .conflict = .{
  33432                     .peer_idx_a = strat_reason,
  33433                     .peer_idx_b = i,
  33434                 } };
  33435                 if (final_set) |cur_set| {
  33436                     final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty);
  33437                 } else {
  33438                     final_set = ty;
  33439                 }
  33440             }
  33441             return .{ .success = final_set.? };
  33442         },
  33443 
  33444         .error_union => {
  33445             var final_set: ?Type = null;
  33446             for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
  33447                 const ty = ty_ptr.* orelse continue;
  33448                 const set_ty = switch (ty.zigTypeTag(mod)) {
  33449                     .ErrorSet => blk: {
  33450                         ty_ptr.* = null; // no payload to decide on
  33451                         val_ptr.* = null;
  33452                         break :blk ty;
  33453                     },
  33454                     .ErrorUnion => blk: {
  33455                         const set_ty = ty.errorUnionSet(mod);
  33456                         ty_ptr.* = ty.errorUnionPayload(mod);
  33457                         if (val_ptr.*) |eu_val| switch (ip.indexToKey(eu_val.toIntern())) {
  33458                             .error_union => |eu| switch (eu.val) {
  33459                                 .payload => |payload_ip| val_ptr.* = Value.fromInterned(payload_ip),
  33460                                 .err_name => val_ptr.* = null,
  33461                             },
  33462                             .undef => val_ptr.* = Value.fromInterned((try sema.mod.intern(.{ .undef = ty_ptr.*.?.toIntern() }))),
  33463                             else => unreachable,
  33464                         };
  33465                         break :blk set_ty;
  33466                     },
  33467                     else => continue, // whole type is the payload
  33468                 };
  33469                 if (final_set) |cur_set| {
  33470                     final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty);
  33471                 } else {
  33472                     final_set = set_ty;
  33473                 }
  33474             }
  33475             assert(final_set != null);
  33476             const final_payload = switch (try sema.resolvePeerTypesInner(
  33477                 block,
  33478                 src,
  33479                 peer_tys,
  33480                 peer_vals,
  33481             )) {
  33482                 .success => |ty| ty,
  33483                 else => |result| return result,
  33484             };
  33485             return .{ .success = try mod.errorUnionType(final_set.?, final_payload) };
  33486         },
  33487 
  33488         .nullable => {
  33489             for (peer_tys, 0..) |opt_ty, i| {
  33490                 const ty = opt_ty orelse continue;
  33491                 if (!ty.eql(Type.null, mod)) return .{ .conflict = .{
  33492                     .peer_idx_a = strat_reason,
  33493                     .peer_idx_b = i,
  33494                 } };
  33495             }
  33496             return .{ .success = Type.null };
  33497         },
  33498 
  33499         .optional => {
  33500             for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
  33501                 const ty = ty_ptr.* orelse continue;
  33502                 switch (ty.zigTypeTag(mod)) {
  33503                     .Null => {
  33504                         ty_ptr.* = null;
  33505                         val_ptr.* = null;
  33506                     },
  33507                     .Optional => {
  33508                         ty_ptr.* = ty.optionalChild(mod);
  33509                         if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(mod)) opt_val.optionalValue(mod) else null;
  33510                     },
  33511                     else => {},
  33512                 }
  33513             }
  33514             const child_ty = switch (try sema.resolvePeerTypesInner(
  33515                 block,
  33516                 src,
  33517                 peer_tys,
  33518                 peer_vals,
  33519             )) {
  33520                 .success => |ty| ty,
  33521                 else => |result| return result,
  33522             };
  33523             return .{ .success = try mod.optionalType(child_ty.toIntern()) };
  33524         },
  33525 
  33526         .array => {
  33527             // Index of the first non-null peer
  33528             var opt_first_idx: ?usize = null;
  33529             // Index of the first array or vector peer (i.e. not a tuple)
  33530             var opt_first_arr_idx: ?usize = null;
  33531             // Set to non-null once we see any peer, even a tuple
  33532             var len: u64 = undefined;
  33533             var sentinel: ?Value = undefined;
  33534             // Only set once we see a non-tuple peer
  33535             var elem_ty: Type = undefined;
  33536 
  33537             for (peer_tys, 0..) |*ty_ptr, i| {
  33538                 const ty = ty_ptr.* orelse continue;
  33539 
  33540                 if (!ty.isArrayOrVector(mod)) {
  33541                     // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced.
  33542                     const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
  33543                         .peer_idx_a = strat_reason,
  33544                         .peer_idx_b = i,
  33545                     } };
  33546 
  33547                     if (opt_first_idx) |first_idx| {
  33548                         if (arr_like.len != len) return .{ .conflict = .{
  33549                             .peer_idx_a = first_idx,
  33550                             .peer_idx_b = i,
  33551                         } };
  33552                     } else {
  33553                         opt_first_idx = i;
  33554                         len = arr_like.len;
  33555                     }
  33556 
  33557                     sentinel = null;
  33558 
  33559                     continue;
  33560                 }
  33561 
  33562                 const first_arr_idx = opt_first_arr_idx orelse {
  33563                     if (opt_first_idx == null) {
  33564                         opt_first_idx = i;
  33565                         len = ty.arrayLen(mod);
  33566                         sentinel = ty.sentinel(mod);
  33567                     }
  33568                     opt_first_arr_idx = i;
  33569                     elem_ty = ty.childType(mod);
  33570                     continue;
  33571                 };
  33572 
  33573                 if (ty.arrayLen(mod) != len) return .{ .conflict = .{
  33574                     .peer_idx_a = first_arr_idx,
  33575                     .peer_idx_b = i,
  33576                 } };
  33577 
  33578                 if (!ty.childType(mod).eql(elem_ty, mod)) {
  33579                     return .{ .conflict = .{
  33580                         .peer_idx_a = first_arr_idx,
  33581                         .peer_idx_b = i,
  33582                     } };
  33583                 }
  33584 
  33585                 if (sentinel) |cur_sent| {
  33586                     if (ty.sentinel(mod)) |peer_sent| {
  33587                         if (!peer_sent.eql(cur_sent, elem_ty, mod)) sentinel = null;
  33588                     } else {
  33589                         sentinel = null;
  33590                     }
  33591                 }
  33592             }
  33593 
  33594             // There should always be at least one array or vector peer
  33595             assert(opt_first_arr_idx != null);
  33596 
  33597             return .{ .success = try mod.arrayType(.{
  33598                 .len = len,
  33599                 .child = elem_ty.toIntern(),
  33600                 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none,
  33601             }) };
  33602         },
  33603 
  33604         .vector => {
  33605             var len: ?u64 = null;
  33606             var first_idx: usize = undefined;
  33607             for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| {
  33608                 const ty = ty_ptr.* orelse continue;
  33609 
  33610                 if (!ty.isArrayOrVector(mod)) {
  33611                     // Allow tuples of the correct length
  33612                     const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
  33613                         .peer_idx_a = strat_reason,
  33614                         .peer_idx_b = i,
  33615                     } };
  33616 
  33617                     if (len) |expect_len| {
  33618                         if (arr_like.len != expect_len) return .{ .conflict = .{
  33619                             .peer_idx_a = first_idx,
  33620                             .peer_idx_b = i,
  33621                         } };
  33622                     } else {
  33623                         len = arr_like.len;
  33624                         first_idx = i;
  33625                     }
  33626 
  33627                     // Tuples won't participate in the child type resolution. We'll resolve without
  33628                     // them, and if the tuples have a bad type, we'll get a coercion error later.
  33629                     ty_ptr.* = null;
  33630                     val_ptr.* = null;
  33631 
  33632                     continue;
  33633                 }
  33634 
  33635                 if (len) |expect_len| {
  33636                     if (ty.arrayLen(mod) != expect_len) return .{ .conflict = .{
  33637                         .peer_idx_a = first_idx,
  33638                         .peer_idx_b = i,
  33639                     } };
  33640                 } else {
  33641                     len = ty.arrayLen(mod);
  33642                     first_idx = i;
  33643                 }
  33644 
  33645                 ty_ptr.* = ty.childType(mod);
  33646                 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR
  33647             }
  33648 
  33649             const child_ty = switch (try sema.resolvePeerTypesInner(
  33650                 block,
  33651                 src,
  33652                 peer_tys,
  33653                 peer_vals,
  33654             )) {
  33655                 .success => |ty| ty,
  33656                 else => |result| return result,
  33657             };
  33658 
  33659             return .{ .success = try mod.vectorType(.{
  33660                 .len = @intCast(len.?),
  33661                 .child = child_ty.toIntern(),
  33662             }) };
  33663         },
  33664 
  33665         .c_ptr => {
  33666             var opt_ptr_info: ?InternPool.Key.PtrType = null;
  33667             var first_idx: usize = undefined;
  33668             for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
  33669                 const ty = opt_ty orelse continue;
  33670                 switch (ty.zigTypeTag(mod)) {
  33671                     .ComptimeInt => continue, // comptime-known integers can always coerce to C pointers
  33672                     .Int => {
  33673                         if (opt_val != null) {
  33674                             // Always allow the coercion for comptime-known ints
  33675                             continue;
  33676                         } else {
  33677                             // Runtime-known, so check if the type is no bigger than a usize
  33678                             const ptr_bits = target.ptrBitWidth();
  33679                             const bits = ty.intInfo(mod).bits;
  33680                             if (bits <= ptr_bits) continue;
  33681                         }
  33682                     },
  33683                     .Null => continue,
  33684                     else => {},
  33685                 }
  33686 
  33687                 if (!ty.isPtrAtRuntime(mod)) return .{ .conflict = .{
  33688                     .peer_idx_a = strat_reason,
  33689                     .peer_idx_b = i,
  33690                 } };
  33691 
  33692                 // Goes through optionals
  33693                 const peer_info = ty.ptrInfo(mod);
  33694 
  33695                 var ptr_info = opt_ptr_info orelse {
  33696                     opt_ptr_info = peer_info;
  33697                     opt_ptr_info.?.flags.size = .C;
  33698                     first_idx = i;
  33699                     continue;
  33700                 };
  33701 
  33702                 // Try peer -> cur, then cur -> peer
  33703                 ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), Type.fromInterned(peer_info.child))) orelse {
  33704                     return .{ .conflict = .{
  33705                         .peer_idx_a = first_idx,
  33706                         .peer_idx_b = i,
  33707                     } };
  33708                 }).toIntern();
  33709 
  33710                 if (ptr_info.sentinel != .none and peer_info.sentinel != .none) {
  33711                     const peer_sent = try ip.getCoerced(sema.gpa, ptr_info.sentinel, ptr_info.child);
  33712                     const ptr_sent = try ip.getCoerced(sema.gpa, peer_info.sentinel, ptr_info.child);
  33713                     if (ptr_sent == peer_sent) {
  33714                         ptr_info.sentinel = ptr_sent;
  33715                     } else {
  33716                         ptr_info.sentinel = .none;
  33717                     }
  33718                 } else {
  33719                     ptr_info.sentinel = .none;
  33720                 }
  33721 
  33722                 // Note that the align can be always non-zero; Module.ptrType will canonicalize it
  33723                 ptr_info.flags.alignment = InternPool.Alignment.min(
  33724                     if (ptr_info.flags.alignment != .none)
  33725                         ptr_info.flags.alignment
  33726                     else
  33727                         Type.fromInterned(ptr_info.child).abiAlignment(mod),
  33728 
  33729                     if (peer_info.flags.alignment != .none)
  33730                         peer_info.flags.alignment
  33731                     else
  33732                         Type.fromInterned(peer_info.child).abiAlignment(mod),
  33733                 );
  33734                 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
  33735                     return .{ .conflict = .{
  33736                         .peer_idx_a = first_idx,
  33737                         .peer_idx_b = i,
  33738                     } };
  33739                 }
  33740 
  33741                 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
  33742                     ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
  33743                 {
  33744                     return .{ .conflict = .{
  33745                         .peer_idx_a = first_idx,
  33746                         .peer_idx_b = i,
  33747                     } };
  33748                 }
  33749 
  33750                 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
  33751                 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
  33752 
  33753                 opt_ptr_info = ptr_info;
  33754             }
  33755             return .{ .success = try sema.ptrType(opt_ptr_info.?) };
  33756         },
  33757 
  33758         .ptr => {
  33759             // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only
  33760             // if there were no actual slices. Else, we want the slice index to report a conflict.
  33761             var opt_slice_idx: ?usize = null;
  33762 
  33763             var opt_ptr_info: ?InternPool.Key.PtrType = null;
  33764             var first_idx: usize = undefined;
  33765             var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error
  33766 
  33767             for (peer_tys, 0..) |opt_ty, i| {
  33768                 const ty = opt_ty orelse continue;
  33769                 const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(mod)) {
  33770                     .Pointer => ty.ptrInfo(mod),
  33771                     .Fn => .{
  33772                         .child = ty.toIntern(),
  33773                         .flags = .{
  33774                             .address_space = target_util.defaultAddressSpace(target, .global_constant),
  33775                         },
  33776                     },
  33777                     else => return .{ .conflict = .{
  33778                         .peer_idx_a = strat_reason,
  33779                         .peer_idx_b = i,
  33780                     } },
  33781                 };
  33782 
  33783                 switch (peer_info.flags.size) {
  33784                     .One, .Many => {},
  33785                     .Slice => opt_slice_idx = i,
  33786                     .C => return .{ .conflict = .{
  33787                         .peer_idx_a = strat_reason,
  33788                         .peer_idx_b = i,
  33789                     } },
  33790                 }
  33791 
  33792                 var ptr_info = opt_ptr_info orelse {
  33793                     opt_ptr_info = peer_info;
  33794                     first_idx = i;
  33795                     continue;
  33796                 };
  33797 
  33798                 other_idx = i;
  33799 
  33800                 // We want to return this in a lot of cases, so alias it here for convenience
  33801                 const generic_err: PeerResolveResult = .{ .conflict = .{
  33802                     .peer_idx_a = first_idx,
  33803                     .peer_idx_b = i,
  33804                 } };
  33805 
  33806                 // Note that the align can be always non-zero; Type.ptr will canonicalize it
  33807                 ptr_info.flags.alignment = Alignment.min(
  33808                     if (ptr_info.flags.alignment != .none)
  33809                         ptr_info.flags.alignment
  33810                     else
  33811                         try sema.typeAbiAlignment(Type.fromInterned(ptr_info.child)),
  33812 
  33813                     if (peer_info.flags.alignment != .none)
  33814                         peer_info.flags.alignment
  33815                     else
  33816                         try sema.typeAbiAlignment(Type.fromInterned(peer_info.child)),
  33817                 );
  33818 
  33819                 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
  33820                     return generic_err;
  33821                 }
  33822 
  33823                 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
  33824                     ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
  33825                 {
  33826                     return generic_err;
  33827                 }
  33828 
  33829                 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
  33830                 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
  33831 
  33832                 const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) {
  33833                     .One => switch (ip.indexToKey(peer_info.child)) {
  33834                         .array_type => |array_type| array_type.sentinel,
  33835                         else => .none,
  33836                     },
  33837                     .Many, .Slice => peer_info.sentinel,
  33838                     .C => unreachable,
  33839                 };
  33840 
  33841                 const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) {
  33842                     .One => switch (ip.indexToKey(ptr_info.child)) {
  33843                         .array_type => |array_type| array_type.sentinel,
  33844                         else => .none,
  33845                     },
  33846                     .Many, .Slice => ptr_info.sentinel,
  33847                     .C => unreachable,
  33848                 };
  33849 
  33850                 // We abstract array handling slightly so that tuple pointers can work like array pointers
  33851                 const peer_pointee_array = sema.typeIsArrayLike(Type.fromInterned(peer_info.child));
  33852                 const cur_pointee_array = sema.typeIsArrayLike(Type.fromInterned(ptr_info.child));
  33853 
  33854                 // This switch is just responsible for deciding the size and pointee (not including
  33855                 // single-pointer array sentinel).
  33856                 good: {
  33857                     switch (peer_info.flags.size) {
  33858                         .One => switch (ptr_info.flags.size) {
  33859                             .One => {
  33860                                 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), Type.fromInterned(peer_info.child))) |pointee| {
  33861                                     ptr_info.child = pointee.toIntern();
  33862                                     break :good;
  33863                                 }
  33864 
  33865                                 const cur_arr = cur_pointee_array orelse return generic_err;
  33866                                 const peer_arr = peer_pointee_array orelse return generic_err;
  33867 
  33868                                 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| {
  33869                                     // *[n:x]T + *[n:y]T = *[n]T
  33870                                     if (cur_arr.len == peer_arr.len) {
  33871                                         ptr_info.child = (try mod.arrayType(.{
  33872                                             .len = cur_arr.len,
  33873                                             .child = elem_ty.toIntern(),
  33874                                         })).toIntern();
  33875                                         break :good;
  33876                                     }
  33877                                     // *[a]T + *[b]T = []T
  33878                                     ptr_info.flags.size = .Slice;
  33879                                     ptr_info.child = elem_ty.toIntern();
  33880                                     break :good;
  33881                                 }
  33882 
  33883                                 if (peer_arr.elem_ty.toIntern() == .noreturn_type) {
  33884                                     // *struct{} + *[a]T = []T
  33885                                     ptr_info.flags.size = .Slice;
  33886                                     ptr_info.child = cur_arr.elem_ty.toIntern();
  33887                                     break :good;
  33888                                 }
  33889 
  33890                                 if (cur_arr.elem_ty.toIntern() == .noreturn_type) {
  33891                                     // *[a]T + *struct{} = []T
  33892                                     ptr_info.flags.size = .Slice;
  33893                                     ptr_info.child = peer_arr.elem_ty.toIntern();
  33894                                     break :good;
  33895                                 }
  33896 
  33897                                 return generic_err;
  33898                             },
  33899                             .Many => {
  33900                                 // Only works for *[n]T + [*]T -> [*]T
  33901                                 const arr = peer_pointee_array orelse return generic_err;
  33902                                 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), arr.elem_ty)) |pointee| {
  33903                                     ptr_info.child = pointee.toIntern();
  33904                                     break :good;
  33905                                 }
  33906                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33907                                     // *struct{} + [*]T -> [*]T
  33908                                     break :good;
  33909                                 }
  33910                                 return generic_err;
  33911                             },
  33912                             .Slice => {
  33913                                 // Only works for *[n]T + []T -> []T
  33914                                 const arr = peer_pointee_array orelse return generic_err;
  33915                                 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), arr.elem_ty)) |pointee| {
  33916                                     ptr_info.child = pointee.toIntern();
  33917                                     break :good;
  33918                                 }
  33919                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33920                                     // *struct{} + []T -> []T
  33921                                     break :good;
  33922                                 }
  33923                                 return generic_err;
  33924                             },
  33925                             .C => unreachable,
  33926                         },
  33927                         .Many => switch (ptr_info.flags.size) {
  33928                             .One => {
  33929                                 // Only works for [*]T + *[n]T -> [*]T
  33930                                 const arr = cur_pointee_array orelse return generic_err;
  33931                                 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, Type.fromInterned(peer_info.child))) |pointee| {
  33932                                     ptr_info.flags.size = .Many;
  33933                                     ptr_info.child = pointee.toIntern();
  33934                                     break :good;
  33935                                 }
  33936                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33937                                     // [*]T + *struct{} -> [*]T
  33938                                     ptr_info.flags.size = .Many;
  33939                                     ptr_info.child = peer_info.child;
  33940                                     break :good;
  33941                                 }
  33942                                 return generic_err;
  33943                             },
  33944                             .Many => {
  33945                                 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), Type.fromInterned(peer_info.child))) |pointee| {
  33946                                     ptr_info.child = pointee.toIntern();
  33947                                     break :good;
  33948                                 }
  33949                                 return generic_err;
  33950                             },
  33951                             .Slice => {
  33952                                 // Only works if no peers are actually slices
  33953                                 if (opt_slice_idx) |slice_idx| {
  33954                                     return .{ .conflict = .{
  33955                                         .peer_idx_a = slice_idx,
  33956                                         .peer_idx_b = i,
  33957                                     } };
  33958                                 }
  33959                                 // Okay, then works for [*]T + "[]T" -> [*]T
  33960                                 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), Type.fromInterned(peer_info.child))) |pointee| {
  33961                                     ptr_info.flags.size = .Many;
  33962                                     ptr_info.child = pointee.toIntern();
  33963                                     break :good;
  33964                                 }
  33965                                 return generic_err;
  33966                             },
  33967                             .C => unreachable,
  33968                         },
  33969                         .Slice => switch (ptr_info.flags.size) {
  33970                             .One => {
  33971                                 // Only works for []T + *[n]T -> []T
  33972                                 const arr = cur_pointee_array orelse return generic_err;
  33973                                 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, Type.fromInterned(peer_info.child))) |pointee| {
  33974                                     ptr_info.flags.size = .Slice;
  33975                                     ptr_info.child = pointee.toIntern();
  33976                                     break :good;
  33977                                 }
  33978                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33979                                     // []T + *struct{} -> []T
  33980                                     ptr_info.flags.size = .Slice;
  33981                                     ptr_info.child = peer_info.child;
  33982                                     break :good;
  33983                                 }
  33984                                 return generic_err;
  33985                             },
  33986                             .Many => {
  33987                                 // Impossible! (current peer is an actual slice)
  33988                                 return generic_err;
  33989                             },
  33990                             .Slice => {
  33991                                 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), Type.fromInterned(peer_info.child))) |pointee| {
  33992                                     ptr_info.child = pointee.toIntern();
  33993                                     break :good;
  33994                                 }
  33995                                 return generic_err;
  33996                             },
  33997                             .C => unreachable,
  33998                         },
  33999                         .C => unreachable,
  34000                     }
  34001                 }
  34002 
  34003                 const sentinel_ty = switch (ptr_info.flags.size) {
  34004                     .One => switch (ip.indexToKey(ptr_info.child)) {
  34005                         .array_type => |array_type| array_type.child,
  34006                         else => ptr_info.child,
  34007                     },
  34008                     .Many, .Slice, .C => ptr_info.child,
  34009                 };
  34010 
  34011                 sentinel: {
  34012                     no_sentinel: {
  34013                         if (peer_sentinel == .none) break :no_sentinel;
  34014                         if (cur_sentinel == .none) break :no_sentinel;
  34015                         const peer_sent_coerced = try ip.getCoerced(sema.gpa, peer_sentinel, sentinel_ty);
  34016                         const cur_sent_coerced = try ip.getCoerced(sema.gpa, cur_sentinel, sentinel_ty);
  34017                         if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel;
  34018                         // Sentinels match
  34019                         if (ptr_info.flags.size == .One) switch (ip.indexToKey(ptr_info.child)) {
  34020                             .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{
  34021                                 .len = array_type.len,
  34022                                 .child = array_type.child,
  34023                                 .sentinel = cur_sent_coerced,
  34024                             })).toIntern(),
  34025                             else => unreachable,
  34026                         } else {
  34027                             ptr_info.sentinel = cur_sent_coerced;
  34028                         }
  34029                         break :sentinel;
  34030                     }
  34031                     // Clear existing sentinel
  34032                     ptr_info.sentinel = .none;
  34033                     switch (ip.indexToKey(ptr_info.child)) {
  34034                         .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{
  34035                             .len = array_type.len,
  34036                             .child = array_type.child,
  34037                             .sentinel = .none,
  34038                         })).toIntern(),
  34039                         else => {},
  34040                     }
  34041                 }
  34042 
  34043                 opt_ptr_info = ptr_info;
  34044             }
  34045 
  34046             // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance)
  34047             // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to
  34048             // coerce the empty struct to a specific type, but no peer provided one. We need to
  34049             // detect this case and emit an error.
  34050             const pointee = opt_ptr_info.?.child;
  34051             switch (pointee) {
  34052                 .noreturn_type => return .{ .conflict = .{
  34053                     .peer_idx_a = first_idx,
  34054                     .peer_idx_b = other_idx,
  34055                 } },
  34056                 else => switch (ip.indexToKey(pointee)) {
  34057                     .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{
  34058                         .peer_idx_a = first_idx,
  34059                         .peer_idx_b = other_idx,
  34060                     } },
  34061                     else => {},
  34062                 },
  34063             }
  34064 
  34065             return .{ .success = try sema.ptrType(opt_ptr_info.?) };
  34066         },
  34067 
  34068         .func => {
  34069             var opt_cur_ty: ?Type = null;
  34070             var first_idx: usize = undefined;
  34071             for (peer_tys, 0..) |opt_ty, i| {
  34072                 const ty = opt_ty orelse continue;
  34073                 const cur_ty = opt_cur_ty orelse {
  34074                     opt_cur_ty = ty;
  34075                     first_idx = i;
  34076                     continue;
  34077                 };
  34078                 if (ty.zigTypeTag(mod) != .Fn) return .{ .conflict = .{
  34079                     .peer_idx_a = strat_reason,
  34080                     .peer_idx_b = i,
  34081                 } };
  34082                 // ty -> cur_ty
  34083                 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, target, src, src)) {
  34084                     continue;
  34085                 }
  34086                 // cur_ty -> ty
  34087                 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, target, src, src)) {
  34088                     opt_cur_ty = ty;
  34089                     continue;
  34090                 }
  34091                 return .{ .conflict = .{
  34092                     .peer_idx_a = first_idx,
  34093                     .peer_idx_b = i,
  34094                 } };
  34095             }
  34096             return .{ .success = opt_cur_ty.? };
  34097         },
  34098 
  34099         .enum_or_union => {
  34100             var opt_cur_ty: ?Type = null;
  34101             // The peer index which gave the current type
  34102             var cur_ty_idx: usize = undefined;
  34103 
  34104             for (peer_tys, 0..) |opt_ty, i| {
  34105                 const ty = opt_ty orelse continue;
  34106                 switch (ty.zigTypeTag(mod)) {
  34107                     .EnumLiteral, .Enum, .Union => {},
  34108                     else => return .{ .conflict = .{
  34109                         .peer_idx_a = strat_reason,
  34110                         .peer_idx_b = i,
  34111                     } },
  34112                 }
  34113                 const cur_ty = opt_cur_ty orelse {
  34114                     opt_cur_ty = ty;
  34115                     cur_ty_idx = i;
  34116                     continue;
  34117                 };
  34118 
  34119                 // We want to return this in a lot of cases, so alias it here for convenience
  34120                 const generic_err: PeerResolveResult = .{ .conflict = .{
  34121                     .peer_idx_a = cur_ty_idx,
  34122                     .peer_idx_b = i,
  34123                 } };
  34124 
  34125                 switch (cur_ty.zigTypeTag(mod)) {
  34126                     .EnumLiteral => {
  34127                         opt_cur_ty = ty;
  34128                         cur_ty_idx = i;
  34129                     },
  34130                     .Enum => switch (ty.zigTypeTag(mod)) {
  34131                         .EnumLiteral => {},
  34132                         .Enum => {
  34133                             if (!ty.eql(cur_ty, mod)) return generic_err;
  34134                         },
  34135                         .Union => {
  34136                             const tag_ty = ty.unionTagTypeHypothetical(mod);
  34137                             if (!tag_ty.eql(cur_ty, mod)) return generic_err;
  34138                             opt_cur_ty = ty;
  34139                             cur_ty_idx = i;
  34140                         },
  34141                         else => unreachable,
  34142                     },
  34143                     .Union => switch (ty.zigTypeTag(mod)) {
  34144                         .EnumLiteral => {},
  34145                         .Enum => {
  34146                             const cur_tag_ty = cur_ty.unionTagTypeHypothetical(mod);
  34147                             if (!ty.eql(cur_tag_ty, mod)) return generic_err;
  34148                         },
  34149                         .Union => {
  34150                             if (!ty.eql(cur_ty, mod)) return generic_err;
  34151                         },
  34152                         else => unreachable,
  34153                     },
  34154                     else => unreachable,
  34155                 }
  34156             }
  34157             return .{ .success = opt_cur_ty.? };
  34158         },
  34159 
  34160         .comptime_int => {
  34161             for (peer_tys, 0..) |opt_ty, i| {
  34162                 const ty = opt_ty orelse continue;
  34163                 switch (ty.zigTypeTag(mod)) {
  34164                     .ComptimeInt => {},
  34165                     else => return .{ .conflict = .{
  34166                         .peer_idx_a = strat_reason,
  34167                         .peer_idx_b = i,
  34168                     } },
  34169                 }
  34170             }
  34171             return .{ .success = Type.comptime_int };
  34172         },
  34173 
  34174         .comptime_float => {
  34175             for (peer_tys, 0..) |opt_ty, i| {
  34176                 const ty = opt_ty orelse continue;
  34177                 switch (ty.zigTypeTag(mod)) {
  34178                     .ComptimeInt, .ComptimeFloat => {},
  34179                     else => return .{ .conflict = .{
  34180                         .peer_idx_a = strat_reason,
  34181                         .peer_idx_b = i,
  34182                     } },
  34183                 }
  34184             }
  34185             return .{ .success = Type.comptime_float };
  34186         },
  34187 
  34188         .fixed_int => {
  34189             var idx_unsigned: ?usize = null;
  34190             var idx_signed: ?usize = null;
  34191 
  34192             // TODO: this is for compatibility with legacy behavior. See beneath the loop.
  34193             var any_comptime_known = false;
  34194 
  34195             for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| {
  34196                 const ty = opt_ty orelse continue;
  34197                 const opt_val = ptr_opt_val.*;
  34198 
  34199                 const peer_tag = ty.zigTypeTag(mod);
  34200                 switch (peer_tag) {
  34201                     .ComptimeInt => {
  34202                         // If the value is undefined, we can't refine to a fixed-width int
  34203                         if (opt_val == null or opt_val.?.isUndef(mod)) return .{ .conflict = .{
  34204                             .peer_idx_a = strat_reason,
  34205                             .peer_idx_b = i,
  34206                         } };
  34207                         any_comptime_known = true;
  34208                         ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?);
  34209                         continue;
  34210                     },
  34211                     .Int => {},
  34212                     else => return .{ .conflict = .{
  34213                         .peer_idx_a = strat_reason,
  34214                         .peer_idx_b = i,
  34215                     } },
  34216                 }
  34217 
  34218                 if (opt_val != null) any_comptime_known = true;
  34219 
  34220                 const info = ty.intInfo(mod);
  34221 
  34222                 const idx_ptr = switch (info.signedness) {
  34223                     .unsigned => &idx_unsigned,
  34224                     .signed => &idx_signed,
  34225                 };
  34226 
  34227                 const largest_idx = idx_ptr.* orelse {
  34228                     idx_ptr.* = i;
  34229                     continue;
  34230                 };
  34231 
  34232                 const cur_info = peer_tys[largest_idx].?.intInfo(mod);
  34233                 if (info.bits > cur_info.bits) {
  34234                     idx_ptr.* = i;
  34235                 }
  34236             }
  34237 
  34238             if (idx_signed == null) {
  34239                 return .{ .success = peer_tys[idx_unsigned.?].? };
  34240             }
  34241 
  34242             if (idx_unsigned == null) {
  34243                 return .{ .success = peer_tys[idx_signed.?].? };
  34244             }
  34245 
  34246             const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(mod);
  34247             const signed_info = peer_tys[idx_signed.?].?.intInfo(mod);
  34248             if (signed_info.bits > unsigned_info.bits) {
  34249                 return .{ .success = peer_tys[idx_signed.?].? };
  34250             }
  34251 
  34252             // TODO: this is for compatibility with legacy behavior. Before this version of PTR was
  34253             // implemented, the algorithm very often returned false positives, with the expectation
  34254             // that you'd just hit a coercion error later. One of these was that for integers, the
  34255             // largest type would always be returned, even if it couldn't fit everything. This had
  34256             // an unintentional consequence to semantics, which is that if values were known at
  34257             // comptime, they would be coerced down to the smallest type where possible. This
  34258             // behavior is unintuitive and order-dependent, so in my opinion should be eliminated,
  34259             // but for now we'll retain compatibility.
  34260             if (any_comptime_known) {
  34261                 if (unsigned_info.bits > signed_info.bits) {
  34262                     return .{ .success = peer_tys[idx_unsigned.?].? };
  34263                 }
  34264                 const idx = @min(idx_unsigned.?, idx_signed.?);
  34265                 return .{ .success = peer_tys[idx].? };
  34266             }
  34267 
  34268             return .{ .conflict = .{
  34269                 .peer_idx_a = idx_unsigned.?,
  34270                 .peer_idx_b = idx_signed.?,
  34271             } };
  34272         },
  34273 
  34274         .fixed_float => {
  34275             var opt_cur_ty: ?Type = null;
  34276 
  34277             for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
  34278                 const ty = opt_ty orelse continue;
  34279                 switch (ty.zigTypeTag(mod)) {
  34280                     .ComptimeFloat, .ComptimeInt => {},
  34281                     .Int => {
  34282                         if (opt_val == null) return .{ .conflict = .{
  34283                             .peer_idx_a = strat_reason,
  34284                             .peer_idx_b = i,
  34285                         } };
  34286                     },
  34287                     .Float => {
  34288                         if (opt_cur_ty) |cur_ty| {
  34289                             if (cur_ty.eql(ty, mod)) continue;
  34290                             // Recreate the type so we eliminate any c_longdouble
  34291                             const bits = @max(cur_ty.floatBits(target), ty.floatBits(target));
  34292                             opt_cur_ty = switch (bits) {
  34293                                 16 => Type.f16,
  34294                                 32 => Type.f32,
  34295                                 64 => Type.f64,
  34296                                 80 => Type.f80,
  34297                                 128 => Type.f128,
  34298                                 else => unreachable,
  34299                             };
  34300                         } else {
  34301                             opt_cur_ty = ty;
  34302                         }
  34303                     },
  34304                     else => return .{ .conflict = .{
  34305                         .peer_idx_a = strat_reason,
  34306                         .peer_idx_b = i,
  34307                     } },
  34308                 }
  34309             }
  34310 
  34311             // Note that fixed_float is only chosen if there is at least one fixed-width float peer,
  34312             // so opt_cur_ty must be non-null.
  34313             return .{ .success = opt_cur_ty.? };
  34314         },
  34315 
  34316         .coercible_struct => {
  34317             // First, check that every peer has the same approximate structure (field count and names)
  34318 
  34319             var opt_first_idx: ?usize = null;
  34320             var is_tuple: bool = undefined;
  34321             var field_count: usize = undefined;
  34322             // Only defined for non-tuples.
  34323             var field_names: []InternPool.NullTerminatedString = undefined;
  34324 
  34325             for (peer_tys, 0..) |opt_ty, i| {
  34326                 const ty = opt_ty orelse continue;
  34327 
  34328                 if (!ty.isTupleOrAnonStruct(mod)) {
  34329                     return .{ .conflict = .{
  34330                         .peer_idx_a = strat_reason,
  34331                         .peer_idx_b = i,
  34332                     } };
  34333                 }
  34334 
  34335                 const first_idx = opt_first_idx orelse {
  34336                     opt_first_idx = i;
  34337                     is_tuple = ty.isTuple(mod);
  34338                     field_count = ty.structFieldCount(mod);
  34339                     if (!is_tuple) {
  34340                         const names = ip.indexToKey(ty.toIntern()).anon_struct_type.names.get(ip);
  34341                         field_names = try sema.arena.dupe(InternPool.NullTerminatedString, names);
  34342                     }
  34343                     continue;
  34344                 };
  34345 
  34346                 if (ty.isTuple(mod) != is_tuple or ty.structFieldCount(mod) != field_count) {
  34347                     return .{ .conflict = .{
  34348                         .peer_idx_a = first_idx,
  34349                         .peer_idx_b = i,
  34350                     } };
  34351                 }
  34352 
  34353                 if (!is_tuple) {
  34354                     for (field_names, 0..) |expected, field_index_usize| {
  34355                         const field_index: u32 = @intCast(field_index_usize);
  34356                         const actual = ty.structFieldName(field_index, mod).unwrap().?;
  34357                         if (actual == expected) continue;
  34358                         return .{ .conflict = .{
  34359                             .peer_idx_a = first_idx,
  34360                             .peer_idx_b = i,
  34361                         } };
  34362                     }
  34363                 }
  34364             }
  34365 
  34366             assert(opt_first_idx != null);
  34367 
  34368             // Now, we'll recursively resolve the field types
  34369             const field_types = try sema.arena.alloc(InternPool.Index, field_count);
  34370             // Values for `comptime` fields - `.none` used for non-comptime fields
  34371             const field_vals = try sema.arena.alloc(InternPool.Index, field_count);
  34372             const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len);
  34373             const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len);
  34374 
  34375             for (field_types, field_vals, 0..) |*field_ty, *field_val, field_idx| {
  34376                 // Fill buffers with types and values of the field
  34377                 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| {
  34378                     const ty = opt_ty orelse {
  34379                         peer_field_ty.* = null;
  34380                         peer_field_val.* = null;
  34381                         continue;
  34382                     };
  34383                     peer_field_ty.* = ty.structFieldType(field_idx, mod);
  34384                     peer_field_val.* = if (opt_val) |val| try val.fieldValue(mod, field_idx) else null;
  34385                 }
  34386 
  34387                 // Resolve field type recursively
  34388                 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) {
  34389                     .success => |ty| ty.toIntern(),
  34390                     else => |result| {
  34391                         const result_buf = try sema.arena.create(PeerResolveResult);
  34392                         result_buf.* = result;
  34393                         const field_name = if (is_tuple) name: {
  34394                             break :name try std.fmt.allocPrint(sema.arena, "{d}", .{field_idx});
  34395                         } else try sema.arena.dupe(u8, ip.stringToSlice(field_names[field_idx]));
  34396 
  34397                         // The error info needs the field types, but we can't reuse sub_peer_tys
  34398                         // since the recursive call may have clobbered it.
  34399                         const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len);
  34400                         for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| {
  34401                             // Already-resolved types won't be referenced by the error so it's fine
  34402                             // to leave them undefined.
  34403                             const ty = opt_ty orelse continue;
  34404                             peer_field_ty.* = ty.structFieldType(field_idx, mod);
  34405                         }
  34406 
  34407                         return .{ .field_error = .{
  34408                             .field_name = field_name,
  34409                             .field_types = peer_field_tys,
  34410                             .sub_result = result_buf,
  34411                         } };
  34412                     },
  34413                 };
  34414 
  34415                 // Decide if this is a comptime field. If it is comptime in all peers, and the
  34416                 // coerced comptime values are all the same, we say it is comptime, else not.
  34417 
  34418                 var comptime_val: ?Value = null;
  34419                 for (peer_tys) |opt_ty| {
  34420                     const struct_ty = opt_ty orelse continue;
  34421                     try sema.resolveStructFieldInits(struct_ty);
  34422 
  34423                     const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse {
  34424                         comptime_val = null;
  34425                         break;
  34426                     };
  34427                     const uncoerced_field = Air.internedToRef(uncoerced_field_val.toIntern());
  34428                     const coerced_inst = sema.coerceExtra(block, Type.fromInterned(field_ty.*), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) {
  34429                         // 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
  34430                         error.NotCoercible => {
  34431                             comptime_val = null;
  34432                             break;
  34433                         },
  34434                         else => |e| return e,
  34435                     };
  34436                     const coerced_val = (try sema.resolveValue(coerced_inst)) orelse continue;
  34437                     const existing = comptime_val orelse {
  34438                         comptime_val = coerced_val;
  34439                         continue;
  34440                     };
  34441                     if (!coerced_val.eql(existing, Type.fromInterned(field_ty.*), mod)) {
  34442                         comptime_val = null;
  34443                         break;
  34444                     }
  34445                 }
  34446 
  34447                 field_val.* = if (comptime_val) |v| v.toIntern() else .none;
  34448             }
  34449 
  34450             const final_ty = try ip.getAnonStructType(mod.gpa, .{
  34451                 .types = field_types,
  34452                 .names = if (is_tuple) &.{} else field_names,
  34453                 .values = field_vals,
  34454             });
  34455 
  34456             return .{ .success = Type.fromInterned(final_ty) };
  34457         },
  34458 
  34459         .exact => {
  34460             var expect_ty: ?Type = null;
  34461             var first_idx: usize = undefined;
  34462             for (peer_tys, 0..) |opt_ty, i| {
  34463                 const ty = opt_ty orelse continue;
  34464                 if (expect_ty) |expect| {
  34465                     if (!ty.eql(expect, mod)) return .{ .conflict = .{
  34466                         .peer_idx_a = first_idx,
  34467                         .peer_idx_b = i,
  34468                     } };
  34469                 } else {
  34470                     expect_ty = ty;
  34471                     first_idx = i;
  34472                 }
  34473             }
  34474             return .{ .success = expect_ty.? };
  34475         },
  34476     }
  34477 }
  34478 
  34479 fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type {
  34480     // e0 -> e1
  34481     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) {
  34482         return e1;
  34483     }
  34484 
  34485     // e1 -> e0
  34486     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) {
  34487         return e0;
  34488     }
  34489 
  34490     return sema.errorSetMerge(e0, e1);
  34491 }
  34492 
  34493 fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type {
  34494     // ty_b -> ty_a
  34495     if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, sema.mod.getTarget(), src, src)) {
  34496         return ty_a;
  34497     }
  34498 
  34499     // ty_a -> ty_b
  34500     if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, sema.mod.getTarget(), src, src)) {
  34501         return ty_b;
  34502     }
  34503 
  34504     return null;
  34505 }
  34506 
  34507 const ArrayLike = struct {
  34508     len: u64,
  34509     /// `noreturn` indicates that this type is `struct{}` so can coerce to anything
  34510     elem_ty: Type,
  34511 };
  34512 fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike {
  34513     const mod = sema.mod;
  34514     return switch (ty.zigTypeTag(mod)) {
  34515         .Array => .{
  34516             .len = ty.arrayLen(mod),
  34517             .elem_ty = ty.childType(mod),
  34518         },
  34519         .Struct => {
  34520             const field_count = ty.structFieldCount(mod);
  34521             if (field_count == 0) return .{
  34522                 .len = 0,
  34523                 .elem_ty = Type.noreturn,
  34524             };
  34525             if (!ty.isTuple(mod)) return null;
  34526             const elem_ty = ty.structFieldType(0, mod);
  34527             for (1..field_count) |i| {
  34528                 if (!ty.structFieldType(i, mod).eql(elem_ty, mod)) {
  34529                     return null;
  34530                 }
  34531             }
  34532             return .{
  34533                 .len = field_count,
  34534                 .elem_ty = elem_ty,
  34535             };
  34536         },
  34537         else => null,
  34538     };
  34539 }
  34540 
  34541 pub fn resolveIes(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!void {
  34542     const mod = sema.mod;
  34543     const ip = &mod.intern_pool;
  34544 
  34545     if (sema.fn_ret_ty_ies) |ies| {
  34546         try sema.resolveInferredErrorSetPtr(block, src, ies);
  34547         assert(ies.resolved != .none);
  34548         ip.funcIesResolved(sema.func_index).* = ies.resolved;
  34549     }
  34550 }
  34551 
  34552 pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void {
  34553     const mod = sema.mod;
  34554     const ip = &mod.intern_pool;
  34555     const fn_ty_info = mod.typeToFunc(fn_ty).?;
  34556 
  34557     try sema.resolveTypeFully(Type.fromInterned(fn_ty_info.return_type));
  34558 
  34559     if (mod.comp.config.any_error_tracing and
  34560         Type.fromInterned(fn_ty_info.return_type).isError(mod))
  34561     {
  34562         // Ensure the type exists so that backends can assume that.
  34563         _ = try sema.getBuiltinType("StackTrace");
  34564     }
  34565 
  34566     for (0..fn_ty_info.param_types.len) |i| {
  34567         try sema.resolveTypeFully(Type.fromInterned(fn_ty_info.param_types.get(ip)[i]));
  34568     }
  34569 }
  34570 
  34571 /// Make it so that calling hash() and eql() on `val` will not assert due
  34572 /// to a type not having its layout resolved.
  34573 fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value {
  34574     const mod = sema.mod;
  34575     switch (mod.intern_pool.indexToKey(val.toIntern())) {
  34576         .int => |int| switch (int.storage) {
  34577             .u64, .i64, .big_int => return val,
  34578             .lazy_align, .lazy_size => return mod.intValue(
  34579                 Type.fromInterned(int.ty),
  34580                 (try val.getUnsignedIntAdvanced(mod, sema)).?,
  34581             ),
  34582         },
  34583         .ptr => |ptr| {
  34584             const resolved_len = switch (ptr.len) {
  34585                 .none => .none,
  34586                 else => (try sema.resolveLazyValue(Value.fromInterned(ptr.len))).toIntern(),
  34587             };
  34588             switch (ptr.addr) {
  34589                 .decl, .mut_decl, .anon_decl => return if (resolved_len == ptr.len)
  34590                     val
  34591                 else
  34592                     Value.fromInterned((try mod.intern(.{ .ptr = .{
  34593                         .ty = ptr.ty,
  34594                         .addr = switch (ptr.addr) {
  34595                             .decl => |decl| .{ .decl = decl },
  34596                             .mut_decl => |mut_decl| .{ .mut_decl = mut_decl },
  34597                             .anon_decl => |anon_decl| .{ .anon_decl = anon_decl },
  34598                             else => unreachable,
  34599                         },
  34600                         .len = resolved_len,
  34601                     } }))),
  34602                 .comptime_field => |field_val| {
  34603                     const resolved_field_val =
  34604                         (try sema.resolveLazyValue(Value.fromInterned(field_val))).toIntern();
  34605                     return if (resolved_field_val == field_val and resolved_len == ptr.len)
  34606                         val
  34607                     else
  34608                         Value.fromInterned((try mod.intern(.{ .ptr = .{
  34609                             .ty = ptr.ty,
  34610                             .addr = .{ .comptime_field = resolved_field_val },
  34611                             .len = resolved_len,
  34612                         } })));
  34613                 },
  34614                 .int => |int| {
  34615                     const resolved_int = (try sema.resolveLazyValue(Value.fromInterned(int))).toIntern();
  34616                     return if (resolved_int == int and resolved_len == ptr.len)
  34617                         val
  34618                     else
  34619                         Value.fromInterned((try mod.intern(.{ .ptr = .{
  34620                             .ty = ptr.ty,
  34621                             .addr = .{ .int = resolved_int },
  34622                             .len = resolved_len,
  34623                         } })));
  34624                 },
  34625                 .eu_payload, .opt_payload => |base| {
  34626                     const resolved_base = (try sema.resolveLazyValue(Value.fromInterned(base))).toIntern();
  34627                     return if (resolved_base == base and resolved_len == ptr.len)
  34628                         val
  34629                     else
  34630                         Value.fromInterned((try mod.intern(.{ .ptr = .{
  34631                             .ty = ptr.ty,
  34632                             .addr = switch (ptr.addr) {
  34633                                 .eu_payload => .{ .eu_payload = resolved_base },
  34634                                 .opt_payload => .{ .opt_payload = resolved_base },
  34635                                 else => unreachable,
  34636                             },
  34637                             .len = ptr.len,
  34638                         } })));
  34639                 },
  34640                 .elem, .field => |base_index| {
  34641                     const resolved_base = (try sema.resolveLazyValue(Value.fromInterned(base_index.base))).toIntern();
  34642                     return if (resolved_base == base_index.base and resolved_len == ptr.len)
  34643                         val
  34644                     else
  34645                         Value.fromInterned((try mod.intern(.{ .ptr = .{
  34646                             .ty = ptr.ty,
  34647                             .addr = switch (ptr.addr) {
  34648                                 .elem => .{ .elem = .{
  34649                                     .base = resolved_base,
  34650                                     .index = base_index.index,
  34651                                 } },
  34652                                 .field => .{ .field = .{
  34653                                     .base = resolved_base,
  34654                                     .index = base_index.index,
  34655                                 } },
  34656                                 else => unreachable,
  34657                             },
  34658                             .len = ptr.len,
  34659                         } })));
  34660                 },
  34661             }
  34662         },
  34663         .aggregate => |aggregate| switch (aggregate.storage) {
  34664             .bytes => return val,
  34665             .elems => |elems| {
  34666                 var resolved_elems: []InternPool.Index = &.{};
  34667                 for (elems, 0..) |elem, i| {
  34668                     const resolved_elem = (try sema.resolveLazyValue(Value.fromInterned(elem))).toIntern();
  34669                     if (resolved_elems.len == 0 and resolved_elem != elem) {
  34670                         resolved_elems = try sema.arena.alloc(InternPool.Index, elems.len);
  34671                         @memcpy(resolved_elems[0..i], elems[0..i]);
  34672                     }
  34673                     if (resolved_elems.len > 0) resolved_elems[i] = resolved_elem;
  34674                 }
  34675                 return if (resolved_elems.len == 0) val else Value.fromInterned((try mod.intern(.{ .aggregate = .{
  34676                     .ty = aggregate.ty,
  34677                     .storage = .{ .elems = resolved_elems },
  34678                 } })));
  34679             },
  34680             .repeated_elem => |elem| {
  34681                 const resolved_elem = (try sema.resolveLazyValue(Value.fromInterned(elem))).toIntern();
  34682                 return if (resolved_elem == elem) val else Value.fromInterned((try mod.intern(.{ .aggregate = .{
  34683                     .ty = aggregate.ty,
  34684                     .storage = .{ .repeated_elem = resolved_elem },
  34685                 } })));
  34686             },
  34687         },
  34688         .un => |un| {
  34689             const resolved_tag = (try sema.resolveLazyValue(Value.fromInterned(un.tag))).toIntern();
  34690             const resolved_val = (try sema.resolveLazyValue(Value.fromInterned(un.val))).toIntern();
  34691             return if (resolved_tag == un.tag and resolved_val == un.val)
  34692                 val
  34693             else
  34694                 Value.fromInterned((try mod.intern(.{ .un = .{
  34695                     .ty = un.ty,
  34696                     .tag = resolved_tag,
  34697                     .val = resolved_val,
  34698                 } })));
  34699         },
  34700         else => return val,
  34701     }
  34702 }
  34703 
  34704 pub fn resolveTypeLayout(sema: *Sema, ty: Type) CompileError!void {
  34705     const mod = sema.mod;
  34706     switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  34707         .simple_type => |simple_type| return sema.resolveSimpleType(simple_type),
  34708         else => {},
  34709     }
  34710     switch (ty.zigTypeTag(mod)) {
  34711         .Struct => return sema.resolveStructLayout(ty),
  34712         .Union => return sema.resolveUnionLayout(ty),
  34713         .Array => {
  34714             if (ty.arrayLenIncludingSentinel(mod) == 0) return;
  34715             const elem_ty = ty.childType(mod);
  34716             return sema.resolveTypeLayout(elem_ty);
  34717         },
  34718         .Optional => {
  34719             const payload_ty = ty.optionalChild(mod);
  34720             // In case of querying the ABI alignment of this optional, we will ask
  34721             // for hasRuntimeBits() of the payload type, so we need "requires comptime"
  34722             // to be known already before this function returns.
  34723             _ = try sema.typeRequiresComptime(payload_ty);
  34724             return sema.resolveTypeLayout(payload_ty);
  34725         },
  34726         .ErrorUnion => {
  34727             const payload_ty = ty.errorUnionPayload(mod);
  34728             return sema.resolveTypeLayout(payload_ty);
  34729         },
  34730         .Fn => {
  34731             const info = mod.typeToFunc(ty).?;
  34732             if (info.is_generic) {
  34733                 // Resolving of generic function types is deferred to when
  34734                 // the function is instantiated.
  34735                 return;
  34736             }
  34737             const ip = &mod.intern_pool;
  34738             for (0..info.param_types.len) |i| {
  34739                 const param_ty = info.param_types.get(ip)[i];
  34740                 try sema.resolveTypeLayout(Type.fromInterned(param_ty));
  34741             }
  34742             try sema.resolveTypeLayout(Type.fromInterned(info.return_type));
  34743         },
  34744         else => {},
  34745     }
  34746 }
  34747 
  34748 /// Resolve a struct's alignment only without triggering resolution of its layout.
  34749 /// Asserts that the alignment is not yet resolved and the layout is non-packed.
  34750 pub fn resolveStructAlignment(
  34751     sema: *Sema,
  34752     ty: InternPool.Index,
  34753     struct_type: InternPool.Key.StructType,
  34754 ) CompileError!Alignment {
  34755     const mod = sema.mod;
  34756     const ip = &mod.intern_pool;
  34757     const target = mod.getTarget();
  34758 
  34759     assert(struct_type.flagsPtr(ip).alignment == .none);
  34760     assert(struct_type.layout != .Packed);
  34761 
  34762     if (struct_type.flagsPtr(ip).field_types_wip) {
  34763         // We'll guess "pointer-aligned", if the struct has an
  34764         // underaligned pointer field then some allocations
  34765         // might require explicit alignment.
  34766         struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
  34767         const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
  34768         struct_type.flagsPtr(ip).alignment = result;
  34769         return result;
  34770     }
  34771 
  34772     try sema.resolveTypeFieldsStruct(ty, struct_type);
  34773 
  34774     if (struct_type.setAlignmentWip(ip)) {
  34775         // We'll guess "pointer-aligned", if the struct has an
  34776         // underaligned pointer field then some allocations
  34777         // might require explicit alignment.
  34778         struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
  34779         const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
  34780         struct_type.flagsPtr(ip).alignment = result;
  34781         return result;
  34782     }
  34783     defer struct_type.clearAlignmentWip(ip);
  34784 
  34785     var result: Alignment = .@"1";
  34786 
  34787     for (0..struct_type.field_types.len) |i| {
  34788         const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  34789         if (struct_type.fieldIsComptime(ip, i) or try sema.typeRequiresComptime(field_ty))
  34790             continue;
  34791         const field_align = try sema.structFieldAlignment(
  34792             struct_type.fieldAlign(ip, i),
  34793             field_ty,
  34794             struct_type.layout,
  34795         );
  34796         result = result.maxStrict(field_align);
  34797     }
  34798 
  34799     struct_type.flagsPtr(ip).alignment = result;
  34800     return result;
  34801 }
  34802 
  34803 fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
  34804     const mod = sema.mod;
  34805     const ip = &mod.intern_pool;
  34806     const struct_type = mod.typeToStruct(ty) orelse return;
  34807 
  34808     if (struct_type.haveLayout(ip))
  34809         return;
  34810 
  34811     try sema.resolveTypeFields(ty);
  34812 
  34813     if (struct_type.layout == .Packed) {
  34814         try semaBackingIntType(mod, struct_type);
  34815         return;
  34816     }
  34817 
  34818     if (struct_type.setLayoutWip(ip)) {
  34819         const msg = try Module.ErrorMsg.create(
  34820             sema.gpa,
  34821             mod.declPtr(struct_type.decl.unwrap().?).srcLoc(mod),
  34822             "struct '{}' depends on itself",
  34823             .{ty.fmt(mod)},
  34824         );
  34825         return sema.failWithOwnedErrorMsg(null, msg);
  34826     }
  34827     defer struct_type.clearLayoutWip(ip);
  34828 
  34829     const aligns = try sema.arena.alloc(Alignment, struct_type.field_types.len);
  34830     const sizes = try sema.arena.alloc(u64, struct_type.field_types.len);
  34831 
  34832     var big_align: Alignment = .@"1";
  34833 
  34834     for (aligns, sizes, 0..) |*field_align, *field_size, i| {
  34835         const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  34836         if (struct_type.fieldIsComptime(ip, i) or try sema.typeRequiresComptime(field_ty)) {
  34837             struct_type.offsets.get(ip)[i] = 0;
  34838             field_size.* = 0;
  34839             field_align.* = .none;
  34840             continue;
  34841         }
  34842 
  34843         field_size.* = sema.typeAbiSize(field_ty) catch |err| switch (err) {
  34844             error.AnalysisFail => {
  34845                 const msg = sema.err orelse return err;
  34846                 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{});
  34847                 return err;
  34848             },
  34849             else => return err,
  34850         };
  34851         field_align.* = try sema.structFieldAlignment(
  34852             struct_type.fieldAlign(ip, i),
  34853             field_ty,
  34854             struct_type.layout,
  34855         );
  34856         big_align = big_align.maxStrict(field_align.*);
  34857     }
  34858 
  34859     if (struct_type.flagsPtr(ip).assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) {
  34860         const msg = try Module.ErrorMsg.create(
  34861             sema.gpa,
  34862             mod.declPtr(struct_type.decl.unwrap().?).srcLoc(mod),
  34863             "struct layout depends on it having runtime bits",
  34864             .{},
  34865         );
  34866         return sema.failWithOwnedErrorMsg(null, msg);
  34867     }
  34868 
  34869     if (struct_type.flagsPtr(ip).assumed_pointer_aligned and
  34870         big_align.compareStrict(.neq, Alignment.fromByteUnits(@divExact(mod.getTarget().ptrBitWidth(), 8))))
  34871     {
  34872         const msg = try Module.ErrorMsg.create(
  34873             sema.gpa,
  34874             mod.declPtr(struct_type.decl.unwrap().?).srcLoc(mod),
  34875             "struct layout depends on being pointer aligned",
  34876             .{},
  34877         );
  34878         return sema.failWithOwnedErrorMsg(null, msg);
  34879     }
  34880 
  34881     if (struct_type.hasReorderedFields()) {
  34882         const runtime_order = struct_type.runtime_order.get(ip);
  34883 
  34884         for (runtime_order, 0..) |*ro, i| {
  34885             const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  34886             if (struct_type.fieldIsComptime(ip, i) or try sema.typeRequiresComptime(field_ty)) {
  34887                 ro.* = .omitted;
  34888             } else {
  34889                 ro.* = @enumFromInt(i);
  34890             }
  34891         }
  34892 
  34893         const RuntimeOrder = InternPool.Key.StructType.RuntimeOrder;
  34894 
  34895         const AlignSortContext = struct {
  34896             aligns: []const Alignment,
  34897 
  34898             fn lessThan(ctx: @This(), a: RuntimeOrder, b: RuntimeOrder) bool {
  34899                 if (a == .omitted) return false;
  34900                 if (b == .omitted) return true;
  34901                 const a_align = ctx.aligns[@intFromEnum(a)];
  34902                 const b_align = ctx.aligns[@intFromEnum(b)];
  34903                 return a_align.compare(.gt, b_align);
  34904             }
  34905         };
  34906         if (struct_type.isTuple(ip) or !mod.backendSupportsFeature(.field_reordering)) {
  34907             // TODO: don't handle tuples differently. This logic exists only because it
  34908             // uncovers latent bugs if removed. Fix the latent bugs and remove this logic!
  34909             // Likewise, implement field reordering support in all the backends!
  34910             // This logic does not reorder fields; it only moves the omitted ones to the end
  34911             // so that logic elsewhere does not need to special-case tuples.
  34912             var i: usize = 0;
  34913             var off: usize = 0;
  34914             while (i + off < runtime_order.len) {
  34915                 if (runtime_order[i + off] == .omitted) {
  34916                     off += 1;
  34917                     continue;
  34918                 }
  34919                 runtime_order[i] = runtime_order[i + off];
  34920                 i += 1;
  34921             }
  34922             @memset(runtime_order[i..], .omitted);
  34923         } else {
  34924             mem.sortUnstable(RuntimeOrder, runtime_order, AlignSortContext{
  34925                 .aligns = aligns,
  34926             }, AlignSortContext.lessThan);
  34927         }
  34928     }
  34929 
  34930     // Calculate size, alignment, and field offsets.
  34931     const offsets = struct_type.offsets.get(ip);
  34932     var it = struct_type.iterateRuntimeOrder(ip);
  34933     var offset: u64 = 0;
  34934     while (it.next()) |i| {
  34935         offsets[i] = @intCast(aligns[i].forward(offset));
  34936         offset = offsets[i] + sizes[i];
  34937     }
  34938     struct_type.size(ip).* = @intCast(big_align.forward(offset));
  34939     const flags = struct_type.flagsPtr(ip);
  34940     flags.alignment = big_align;
  34941     flags.layout_resolved = true;
  34942     _ = try sema.typeRequiresComptime(ty);
  34943 }
  34944 
  34945 fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) CompileError!void {
  34946     const gpa = mod.gpa;
  34947     const ip = &mod.intern_pool;
  34948 
  34949     const decl_index = struct_type.decl.unwrap().?;
  34950     const decl = mod.declPtr(decl_index);
  34951 
  34952     const zir = mod.namespacePtr(struct_type.namespace.unwrap().?).file_scope.zir;
  34953 
  34954     var analysis_arena = std.heap.ArenaAllocator.init(gpa);
  34955     defer analysis_arena.deinit();
  34956 
  34957     var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa);
  34958     defer comptime_mutable_decls.deinit();
  34959 
  34960     var sema: Sema = .{
  34961         .mod = mod,
  34962         .gpa = gpa,
  34963         .arena = analysis_arena.allocator(),
  34964         .code = zir,
  34965         .owner_decl = decl,
  34966         .owner_decl_index = decl_index,
  34967         .func_index = .none,
  34968         .func_is_naked = false,
  34969         .fn_ret_ty = Type.void,
  34970         .fn_ret_ty_ies = null,
  34971         .owner_func_index = .none,
  34972         .comptime_mutable_decls = &comptime_mutable_decls,
  34973     };
  34974     defer sema.deinit();
  34975 
  34976     var block: Block = .{
  34977         .parent = null,
  34978         .sema = &sema,
  34979         .src_decl = decl_index,
  34980         .namespace = struct_type.namespace.unwrap() orelse decl.src_namespace,
  34981         .wip_capture_scope = try mod.createCaptureScope(decl.src_scope),
  34982         .instructions = .{},
  34983         .inlining = null,
  34984         .is_comptime = true,
  34985     };
  34986     defer assert(block.instructions.items.len == 0);
  34987 
  34988     const fields_bit_sum = blk: {
  34989         var accumulator: u64 = 0;
  34990         for (0..struct_type.field_types.len) |i| {
  34991             const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  34992             accumulator += try field_ty.bitSizeAdvanced(mod, &sema);
  34993         }
  34994         break :blk accumulator;
  34995     };
  34996 
  34997     const extended = zir.instructions.items(.data)[@intFromEnum(struct_type.zir_index)].extended;
  34998     assert(extended.opcode == .struct_decl);
  34999     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
  35000 
  35001     if (small.has_backing_int) {
  35002         var extra_index: usize = extended.operand;
  35003         extra_index += @intFromBool(small.has_src_node);
  35004         extra_index += @intFromBool(small.has_fields_len);
  35005         extra_index += @intFromBool(small.has_decls_len);
  35006 
  35007         const backing_int_body_len = zir.extra[extra_index];
  35008         extra_index += 1;
  35009 
  35010         const backing_int_src: LazySrcLoc = .{ .node_offset_container_tag = 0 };
  35011         const backing_int_ty = blk: {
  35012             if (backing_int_body_len == 0) {
  35013                 const backing_int_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  35014                 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref);
  35015             } else {
  35016                 const body = zir.bodySlice(extra_index, backing_int_body_len);
  35017                 const ty_ref = try sema.resolveBody(&block, body, struct_type.zir_index);
  35018                 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref);
  35019             }
  35020         };
  35021 
  35022         try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum);
  35023         struct_type.backingIntType(ip).* = backing_int_ty.toIntern();
  35024     } else {
  35025         if (fields_bit_sum > std.math.maxInt(u16)) {
  35026             return sema.fail(&block, LazySrcLoc.nodeOffset(0), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum});
  35027         }
  35028         const backing_int_ty = try mod.intType(.unsigned, @intCast(fields_bit_sum));
  35029         struct_type.backingIntType(ip).* = backing_int_ty.toIntern();
  35030     }
  35031 
  35032     for (comptime_mutable_decls.items) |ct_decl_index| {
  35033         const ct_decl = mod.declPtr(ct_decl_index);
  35034         _ = try ct_decl.internValue(mod);
  35035     }
  35036 }
  35037 
  35038 fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void {
  35039     const mod = sema.mod;
  35040 
  35041     if (!backing_int_ty.isInt(mod)) {
  35042         return sema.fail(block, src, "expected backing integer type, found '{}'", .{backing_int_ty.fmt(sema.mod)});
  35043     }
  35044     if (backing_int_ty.bitSize(mod) != fields_bit_sum) {
  35045         return sema.fail(
  35046             block,
  35047             src,
  35048             "backing integer type '{}' has bit size {} but the struct fields have a total bit size of {}",
  35049             .{ backing_int_ty.fmt(sema.mod), backing_int_ty.bitSize(mod), fields_bit_sum },
  35050         );
  35051     }
  35052 }
  35053 
  35054 fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  35055     const mod = sema.mod;
  35056     if (!ty.isIndexable(mod)) {
  35057         const msg = msg: {
  35058             const msg = try sema.errMsg(block, src, "type '{}' does not support indexing", .{ty.fmt(sema.mod)});
  35059             errdefer msg.destroy(sema.gpa);
  35060             try sema.errNote(block, src, msg, "operand must be an array, slice, tuple, or vector", .{});
  35061             break :msg msg;
  35062         };
  35063         return sema.failWithOwnedErrorMsg(block, msg);
  35064     }
  35065 }
  35066 
  35067 fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  35068     const mod = sema.mod;
  35069     if (ty.zigTypeTag(mod) == .Pointer) {
  35070         switch (ty.ptrSize(mod)) {
  35071             .Slice, .Many, .C => return,
  35072             .One => {
  35073                 const elem_ty = ty.childType(mod);
  35074                 if (elem_ty.zigTypeTag(mod) == .Array) return;
  35075                 // TODO https://github.com/ziglang/zig/issues/15479
  35076                 // if (elem_ty.isTuple()) return;
  35077             },
  35078         }
  35079     }
  35080     const msg = msg: {
  35081         const msg = try sema.errMsg(block, src, "type '{}' is not an indexable pointer", .{ty.fmt(sema.mod)});
  35082         errdefer msg.destroy(sema.gpa);
  35083         try sema.errNote(block, src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{});
  35084         break :msg msg;
  35085     };
  35086     return sema.failWithOwnedErrorMsg(block, msg);
  35087 }
  35088 
  35089 /// Resolve a unions's alignment only without triggering resolution of its layout.
  35090 /// Asserts that the alignment is not yet resolved.
  35091 pub fn resolveUnionAlignment(
  35092     sema: *Sema,
  35093     ty: Type,
  35094     union_type: InternPool.Key.UnionType,
  35095 ) CompileError!Alignment {
  35096     const mod = sema.mod;
  35097     const ip = &mod.intern_pool;
  35098     const target = mod.getTarget();
  35099 
  35100     assert(!union_type.haveLayout(ip));
  35101 
  35102     if (union_type.flagsPtr(ip).status == .field_types_wip) {
  35103         // We'll guess "pointer-aligned", if the union has an
  35104         // underaligned pointer field then some allocations
  35105         // might require explicit alignment.
  35106         union_type.flagsPtr(ip).assumed_pointer_aligned = true;
  35107         const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
  35108         union_type.flagsPtr(ip).alignment = result;
  35109         return result;
  35110     }
  35111 
  35112     try sema.resolveTypeFieldsUnion(ty, union_type);
  35113 
  35114     const union_obj = ip.loadUnionType(union_type);
  35115     var max_align: Alignment = .@"1";
  35116     for (0..union_obj.field_names.len) |field_index| {
  35117         const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
  35118         if (!(try sema.typeHasRuntimeBits(field_ty))) continue;
  35119 
  35120         const explicit_align = union_obj.fieldAlign(ip, @intCast(field_index));
  35121         const field_align = if (explicit_align != .none)
  35122             explicit_align
  35123         else
  35124             try sema.typeAbiAlignment(field_ty);
  35125 
  35126         max_align = max_align.max(field_align);
  35127     }
  35128 
  35129     union_type.flagsPtr(ip).alignment = max_align;
  35130     return max_align;
  35131 }
  35132 
  35133 /// This logic must be kept in sync with `Module.getUnionLayout`.
  35134 fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
  35135     const mod = sema.mod;
  35136     const ip = &mod.intern_pool;
  35137 
  35138     const union_type = ip.indexToKey(ty.ip_index).union_type;
  35139     try sema.resolveTypeFieldsUnion(ty, union_type);
  35140 
  35141     const union_obj = ip.loadUnionType(union_type);
  35142     switch (union_obj.flagsPtr(ip).status) {
  35143         .none, .have_field_types => {},
  35144         .field_types_wip, .layout_wip => {
  35145             const msg = try Module.ErrorMsg.create(
  35146                 sema.gpa,
  35147                 mod.declPtr(union_obj.decl).srcLoc(mod),
  35148                 "union '{}' depends on itself",
  35149                 .{ty.fmt(mod)},
  35150             );
  35151             return sema.failWithOwnedErrorMsg(null, msg);
  35152         },
  35153         .have_layout, .fully_resolved_wip, .fully_resolved => return,
  35154     }
  35155 
  35156     const prev_status = union_obj.flagsPtr(ip).status;
  35157     errdefer if (union_obj.flagsPtr(ip).status == .layout_wip) {
  35158         union_obj.flagsPtr(ip).status = prev_status;
  35159     };
  35160 
  35161     union_obj.flagsPtr(ip).status = .layout_wip;
  35162 
  35163     var max_size: u64 = 0;
  35164     var max_align: Alignment = .@"1";
  35165     for (0..union_obj.field_names.len) |field_index| {
  35166         const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
  35167         if (!(try sema.typeHasRuntimeBits(field_ty))) continue;
  35168 
  35169         max_size = @max(max_size, sema.typeAbiSize(field_ty) catch |err| switch (err) {
  35170             error.AnalysisFail => {
  35171                 const msg = sema.err orelse return err;
  35172                 try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{});
  35173                 return err;
  35174             },
  35175             else => return err,
  35176         });
  35177 
  35178         const explicit_align = union_obj.fieldAlign(ip, @intCast(field_index));
  35179         const field_align = if (explicit_align != .none)
  35180             explicit_align
  35181         else
  35182             try sema.typeAbiAlignment(field_ty);
  35183 
  35184         max_align = max_align.max(field_align);
  35185     }
  35186 
  35187     const flags = union_obj.flagsPtr(ip);
  35188     const has_runtime_tag = flags.runtime_tag.hasTag() and try sema.typeHasRuntimeBits(Type.fromInterned(union_obj.enum_tag_ty));
  35189     const size, const alignment, const padding = if (has_runtime_tag) layout: {
  35190         const enum_tag_type = Type.fromInterned(union_obj.enum_tag_ty);
  35191         const tag_align = try sema.typeAbiAlignment(enum_tag_type);
  35192         const tag_size = try sema.typeAbiSize(enum_tag_type);
  35193 
  35194         // Put the tag before or after the payload depending on which one's
  35195         // alignment is greater.
  35196         var size: u64 = 0;
  35197         var padding: u32 = 0;
  35198         if (tag_align.compare(.gte, max_align)) {
  35199             // {Tag, Payload}
  35200             size += tag_size;
  35201             size = max_align.forward(size);
  35202             size += max_size;
  35203             const prev_size = size;
  35204             size = tag_align.forward(size);
  35205             padding = @intCast(size - prev_size);
  35206         } else {
  35207             // {Payload, Tag}
  35208             size += max_size;
  35209             size = tag_align.forward(size);
  35210             size += tag_size;
  35211             const prev_size = size;
  35212             size = max_align.forward(size);
  35213             padding = @intCast(size - prev_size);
  35214         }
  35215 
  35216         break :layout .{ size, max_align.max(tag_align), padding };
  35217     } else .{ max_align.forward(max_size), max_align, 0 };
  35218 
  35219     union_type.size(ip).* = @intCast(size);
  35220     union_type.padding(ip).* = padding;
  35221     flags.alignment = alignment;
  35222     flags.status = .have_layout;
  35223 
  35224     if (union_obj.flagsPtr(ip).assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) {
  35225         const msg = try Module.ErrorMsg.create(
  35226             sema.gpa,
  35227             mod.declPtr(union_obj.decl).srcLoc(mod),
  35228             "union layout depends on it having runtime bits",
  35229             .{},
  35230         );
  35231         return sema.failWithOwnedErrorMsg(null, msg);
  35232     }
  35233 
  35234     if (union_obj.flagsPtr(ip).assumed_pointer_aligned and
  35235         alignment.compareStrict(.neq, Alignment.fromByteUnits(@divExact(mod.getTarget().ptrBitWidth(), 8))))
  35236     {
  35237         const msg = try Module.ErrorMsg.create(
  35238             sema.gpa,
  35239             mod.declPtr(union_obj.decl).srcLoc(mod),
  35240             "union layout depends on being pointer aligned",
  35241             .{},
  35242         );
  35243         return sema.failWithOwnedErrorMsg(null, msg);
  35244     }
  35245 }
  35246 
  35247 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to
  35248 /// be resolved.
  35249 pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void {
  35250     const mod = sema.mod;
  35251     const ip = &mod.intern_pool;
  35252     switch (ty.zigTypeTag(mod)) {
  35253         .Pointer => {
  35254             return sema.resolveTypeFully(ty.childType(mod));
  35255         },
  35256         .Struct => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  35257             .struct_type => return sema.resolveStructFully(ty),
  35258             .anon_struct_type => |tuple| {
  35259                 for (tuple.types.get(ip)) |field_ty| {
  35260                     try sema.resolveTypeFully(Type.fromInterned(field_ty));
  35261                 }
  35262             },
  35263             .simple_type => |simple_type| try sema.resolveSimpleType(simple_type),
  35264             else => {},
  35265         },
  35266         .Union => return sema.resolveUnionFully(ty),
  35267         .Array => return sema.resolveTypeFully(ty.childType(mod)),
  35268         .Optional => {
  35269             return sema.resolveTypeFully(ty.optionalChild(mod));
  35270         },
  35271         .ErrorUnion => return sema.resolveTypeFully(ty.errorUnionPayload(mod)),
  35272         .Fn => {
  35273             const info = mod.typeToFunc(ty).?;
  35274             if (info.is_generic) {
  35275                 // Resolving of generic function types is deferred to when
  35276                 // the function is instantiated.
  35277                 return;
  35278             }
  35279             for (0..info.param_types.len) |i| {
  35280                 const param_ty = info.param_types.get(ip)[i];
  35281                 try sema.resolveTypeFully(Type.fromInterned(param_ty));
  35282             }
  35283             try sema.resolveTypeFully(Type.fromInterned(info.return_type));
  35284         },
  35285         else => {},
  35286     }
  35287 }
  35288 
  35289 fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void {
  35290     try sema.resolveStructLayout(ty);
  35291 
  35292     const mod = sema.mod;
  35293     const ip = &mod.intern_pool;
  35294     const struct_type = mod.typeToStruct(ty).?;
  35295 
  35296     if (struct_type.setFullyResolved(ip)) return;
  35297     errdefer struct_type.clearFullyResolved(ip);
  35298 
  35299     // After we have resolve struct layout we have to go over the fields again to
  35300     // make sure pointer fields get their child types resolved as well.
  35301     // See also similar code for unions.
  35302 
  35303     for (0..struct_type.field_types.len) |i| {
  35304         const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  35305         try sema.resolveTypeFully(field_ty);
  35306     }
  35307 }
  35308 
  35309 fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void {
  35310     try sema.resolveUnionLayout(ty);
  35311 
  35312     const mod = sema.mod;
  35313     const ip = &mod.intern_pool;
  35314     const union_obj = mod.typeToUnion(ty).?;
  35315     switch (union_obj.flagsPtr(ip).status) {
  35316         .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
  35317         .fully_resolved_wip, .fully_resolved => return,
  35318     }
  35319 
  35320     {
  35321         // After we have resolve union layout we have to go over the fields again to
  35322         // make sure pointer fields get their child types resolved as well.
  35323         // See also similar code for structs.
  35324         const prev_status = union_obj.flagsPtr(ip).status;
  35325         errdefer union_obj.flagsPtr(ip).status = prev_status;
  35326 
  35327         union_obj.flagsPtr(ip).status = .fully_resolved_wip;
  35328         for (0..union_obj.field_types.len) |field_index| {
  35329             const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
  35330             try sema.resolveTypeFully(field_ty);
  35331         }
  35332         union_obj.flagsPtr(ip).status = .fully_resolved;
  35333     }
  35334 
  35335     // And let's not forget comptime-only status.
  35336     _ = try sema.typeRequiresComptime(ty);
  35337 }
  35338 
  35339 pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!void {
  35340     const mod = sema.mod;
  35341     const ip = &mod.intern_pool;
  35342     const ty_ip = ty.toIntern();
  35343 
  35344     switch (ty_ip) {
  35345         .var_args_param_type => unreachable,
  35346 
  35347         .none => unreachable,
  35348 
  35349         .u0_type,
  35350         .i0_type,
  35351         .u1_type,
  35352         .u8_type,
  35353         .i8_type,
  35354         .u16_type,
  35355         .i16_type,
  35356         .u29_type,
  35357         .u32_type,
  35358         .i32_type,
  35359         .u64_type,
  35360         .i64_type,
  35361         .u80_type,
  35362         .u128_type,
  35363         .i128_type,
  35364         .usize_type,
  35365         .isize_type,
  35366         .c_char_type,
  35367         .c_short_type,
  35368         .c_ushort_type,
  35369         .c_int_type,
  35370         .c_uint_type,
  35371         .c_long_type,
  35372         .c_ulong_type,
  35373         .c_longlong_type,
  35374         .c_ulonglong_type,
  35375         .c_longdouble_type,
  35376         .f16_type,
  35377         .f32_type,
  35378         .f64_type,
  35379         .f80_type,
  35380         .f128_type,
  35381         .anyopaque_type,
  35382         .bool_type,
  35383         .void_type,
  35384         .type_type,
  35385         .anyerror_type,
  35386         .adhoc_inferred_error_set_type,
  35387         .comptime_int_type,
  35388         .comptime_float_type,
  35389         .noreturn_type,
  35390         .anyframe_type,
  35391         .null_type,
  35392         .undefined_type,
  35393         .enum_literal_type,
  35394         .manyptr_u8_type,
  35395         .manyptr_const_u8_type,
  35396         .manyptr_const_u8_sentinel_0_type,
  35397         .single_const_pointer_to_comptime_int_type,
  35398         .slice_const_u8_type,
  35399         .slice_const_u8_sentinel_0_type,
  35400         .optional_noreturn_type,
  35401         .anyerror_void_error_union_type,
  35402         .generic_poison_type,
  35403         .empty_struct_type,
  35404         => {},
  35405 
  35406         .undef => unreachable,
  35407         .zero => unreachable,
  35408         .zero_usize => unreachable,
  35409         .zero_u8 => unreachable,
  35410         .one => unreachable,
  35411         .one_usize => unreachable,
  35412         .one_u8 => unreachable,
  35413         .four_u8 => unreachable,
  35414         .negative_one => unreachable,
  35415         .calling_convention_c => unreachable,
  35416         .calling_convention_inline => unreachable,
  35417         .void_value => unreachable,
  35418         .unreachable_value => unreachable,
  35419         .null_value => unreachable,
  35420         .bool_true => unreachable,
  35421         .bool_false => unreachable,
  35422         .empty_struct => unreachable,
  35423         .generic_poison => unreachable,
  35424 
  35425         else => switch (ip.items.items(.tag)[@intFromEnum(ty_ip)]) {
  35426             .type_struct,
  35427             .type_struct_ns,
  35428             .type_struct_packed,
  35429             .type_struct_packed_inits,
  35430             => try sema.resolveTypeFieldsStruct(ty_ip, ip.indexToKey(ty_ip).struct_type),
  35431 
  35432             .type_union => try sema.resolveTypeFieldsUnion(Type.fromInterned(ty_ip), ip.indexToKey(ty_ip).union_type),
  35433             .simple_type => try sema.resolveSimpleType(ip.indexToKey(ty_ip).simple_type),
  35434             else => {},
  35435         },
  35436     }
  35437 }
  35438 
  35439 /// Fully resolves a simple type. This is usually a nop, but for builtin types with
  35440 /// special InternPool indices (such as std.builtin.Type) it will analyze and fully
  35441 /// resolve the container type.
  35442 fn resolveSimpleType(sema: *Sema, simple_type: InternPool.SimpleType) CompileError!void {
  35443     const builtin_type_name: []const u8 = switch (simple_type) {
  35444         .atomic_order => "AtomicOrder",
  35445         .atomic_rmw_op => "AtomicRmwOp",
  35446         .calling_convention => "CallingConvention",
  35447         .address_space => "AddressSpace",
  35448         .float_mode => "FloatMode",
  35449         .reduce_op => "ReduceOp",
  35450         .call_modifier => "CallModifer",
  35451         .prefetch_options => "PrefetchOptions",
  35452         .export_options => "ExportOptions",
  35453         .extern_options => "ExternOptions",
  35454         .type_info => "Type",
  35455         else => return,
  35456     };
  35457     // This will fully resolve the type.
  35458     _ = try sema.getBuiltinType(builtin_type_name);
  35459 }
  35460 
  35461 pub fn resolveTypeFieldsStruct(
  35462     sema: *Sema,
  35463     ty: InternPool.Index,
  35464     struct_type: InternPool.Key.StructType,
  35465 ) CompileError!void {
  35466     const mod = sema.mod;
  35467     const ip = &mod.intern_pool;
  35468     // If there is no owner decl it means the struct has no fields.
  35469     const owner_decl = struct_type.decl.unwrap() orelse return;
  35470 
  35471     switch (mod.declPtr(owner_decl).analysis) {
  35472         .file_failure,
  35473         .dependency_failure,
  35474         .sema_failure,
  35475         .sema_failure_retryable,
  35476         => {
  35477             sema.owner_decl.analysis = .dependency_failure;
  35478             sema.owner_decl.generation = mod.generation;
  35479             return error.AnalysisFail;
  35480         },
  35481         else => {},
  35482     }
  35483 
  35484     if (struct_type.haveFieldTypes(ip)) return;
  35485 
  35486     if (struct_type.setTypesWip(ip)) {
  35487         const msg = try Module.ErrorMsg.create(
  35488             sema.gpa,
  35489             mod.declPtr(owner_decl).srcLoc(mod),
  35490             "struct '{}' depends on itself",
  35491             .{Type.fromInterned(ty).fmt(mod)},
  35492         );
  35493         return sema.failWithOwnedErrorMsg(null, msg);
  35494     }
  35495     defer struct_type.clearTypesWip(ip);
  35496 
  35497     try semaStructFields(mod, sema.arena, struct_type);
  35498 }
  35499 
  35500 pub fn resolveStructFieldInits(sema: *Sema, ty: Type) CompileError!void {
  35501     const mod = sema.mod;
  35502     const ip = &mod.intern_pool;
  35503     const struct_type = mod.typeToStruct(ty) orelse return;
  35504     const owner_decl = struct_type.decl.unwrap() orelse return;
  35505 
  35506     // Inits can start as resolved
  35507     if (struct_type.haveFieldInits(ip)) return;
  35508 
  35509     try sema.resolveStructLayout(ty);
  35510 
  35511     if (struct_type.setInitsWip(ip)) {
  35512         const msg = try Module.ErrorMsg.create(
  35513             sema.gpa,
  35514             mod.declPtr(owner_decl).srcLoc(mod),
  35515             "struct '{}' depends on itself",
  35516             .{ty.fmt(mod)},
  35517         );
  35518         return sema.failWithOwnedErrorMsg(null, msg);
  35519     }
  35520     defer struct_type.clearInitsWip(ip);
  35521 
  35522     try semaStructFieldInits(mod, sema.arena, struct_type);
  35523     struct_type.setHaveFieldInits(ip);
  35524 }
  35525 
  35526 pub fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void {
  35527     const mod = sema.mod;
  35528     const ip = &mod.intern_pool;
  35529     const owner_decl = mod.declPtr(union_type.decl);
  35530     switch (owner_decl.analysis) {
  35531         .file_failure,
  35532         .dependency_failure,
  35533         .sema_failure,
  35534         .sema_failure_retryable,
  35535         => {
  35536             sema.owner_decl.analysis = .dependency_failure;
  35537             sema.owner_decl.generation = mod.generation;
  35538             return error.AnalysisFail;
  35539         },
  35540         else => {},
  35541     }
  35542     switch (union_type.flagsPtr(ip).status) {
  35543         .none => {},
  35544         .field_types_wip => {
  35545             const msg = try Module.ErrorMsg.create(
  35546                 sema.gpa,
  35547                 owner_decl.srcLoc(mod),
  35548                 "union '{}' depends on itself",
  35549                 .{ty.fmt(mod)},
  35550             );
  35551             return sema.failWithOwnedErrorMsg(null, msg);
  35552         },
  35553         .have_field_types,
  35554         .have_layout,
  35555         .layout_wip,
  35556         .fully_resolved_wip,
  35557         .fully_resolved,
  35558         => return,
  35559     }
  35560 
  35561     union_type.flagsPtr(ip).status = .field_types_wip;
  35562     errdefer union_type.flagsPtr(ip).status = .none;
  35563     try semaUnionFields(mod, sema.arena, union_type);
  35564     union_type.flagsPtr(ip).status = .have_field_types;
  35565 }
  35566 
  35567 /// Returns a normal error set corresponding to the fully populated inferred
  35568 /// error set.
  35569 fn resolveInferredErrorSet(
  35570     sema: *Sema,
  35571     block: *Block,
  35572     src: LazySrcLoc,
  35573     ies_index: InternPool.Index,
  35574 ) CompileError!InternPool.Index {
  35575     const mod = sema.mod;
  35576     const ip = &mod.intern_pool;
  35577     const func_index = ip.iesFuncIndex(ies_index);
  35578     const func = mod.funcInfo(func_index);
  35579     const resolved_ty = func.resolvedErrorSet(ip).*;
  35580     if (resolved_ty != .none) return resolved_ty;
  35581     if (func.analysis(ip).state == .in_progress)
  35582         return sema.fail(block, src, "unable to resolve inferred error set", .{});
  35583 
  35584     // In order to ensure that all dependencies are properly added to the set,
  35585     // we need to ensure the function body is analyzed of the inferred error
  35586     // set. However, in the case of comptime/inline function calls with
  35587     // inferred error sets, each call gets an adhoc InferredErrorSet object, which
  35588     // has no corresponding function body.
  35589     const ies_func_owner_decl = mod.declPtr(func.owner_decl);
  35590     const ies_func_info = mod.typeToFunc(ies_func_owner_decl.ty).?;
  35591     // if ies declared by a inline function with generic return type, the return_type should be generic_poison,
  35592     // because inline function does not create a new declaration, and the ies has been filled with analyzeCall,
  35593     // so here we can simply skip this case.
  35594     if (ies_func_info.return_type == .generic_poison_type) {
  35595         assert(ies_func_info.cc == .Inline);
  35596     } else if (ip.errorUnionSet(ies_func_info.return_type) == ies_index) {
  35597         if (ies_func_info.is_generic) {
  35598             const msg = msg: {
  35599                 const msg = try sema.errMsg(block, src, "unable to resolve inferred error set of generic function", .{});
  35600                 errdefer msg.destroy(sema.gpa);
  35601 
  35602                 try sema.mod.errNoteNonLazy(ies_func_owner_decl.srcLoc(mod), msg, "generic function declared here", .{});
  35603                 break :msg msg;
  35604             };
  35605             return sema.failWithOwnedErrorMsg(block, msg);
  35606         }
  35607         // In this case we are dealing with the actual InferredErrorSet object that
  35608         // corresponds to the function, not one created to track an inline/comptime call.
  35609         try sema.ensureFuncBodyAnalyzed(func_index);
  35610     }
  35611 
  35612     // This will now have been resolved by the logic at the end of `Module.analyzeFnBody`
  35613     // which calls `resolveInferredErrorSetPtr`.
  35614     const final_resolved_ty = func.resolvedErrorSet(ip).*;
  35615     assert(final_resolved_ty != .none);
  35616     return final_resolved_ty;
  35617 }
  35618 
  35619 pub fn resolveInferredErrorSetPtr(
  35620     sema: *Sema,
  35621     block: *Block,
  35622     src: LazySrcLoc,
  35623     ies: *InferredErrorSet,
  35624 ) CompileError!void {
  35625     const mod = sema.mod;
  35626     const ip = &mod.intern_pool;
  35627 
  35628     if (ies.resolved != .none) return;
  35629 
  35630     const ies_index = ip.errorUnionSet(sema.fn_ret_ty.toIntern());
  35631 
  35632     for (ies.inferred_error_sets.keys()) |other_ies_index| {
  35633         if (ies_index == other_ies_index) continue;
  35634         switch (try sema.resolveInferredErrorSet(block, src, other_ies_index)) {
  35635             .anyerror_type => {
  35636                 ies.resolved = .anyerror_type;
  35637                 return;
  35638             },
  35639             else => |error_set_ty_index| {
  35640                 const names = ip.indexToKey(error_set_ty_index).error_set_type.names;
  35641                 for (names.get(ip)) |name| {
  35642                     try ies.errors.put(sema.arena, name, {});
  35643                 }
  35644             },
  35645         }
  35646     }
  35647 
  35648     const resolved_error_set_ty = try mod.errorSetFromUnsortedNames(ies.errors.keys());
  35649     ies.resolved = resolved_error_set_ty.toIntern();
  35650 }
  35651 
  35652 fn resolveAdHocInferredErrorSet(
  35653     sema: *Sema,
  35654     block: *Block,
  35655     src: LazySrcLoc,
  35656     value: InternPool.Index,
  35657 ) CompileError!InternPool.Index {
  35658     const mod = sema.mod;
  35659     const gpa = sema.gpa;
  35660     const ip = &mod.intern_pool;
  35661     const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value));
  35662     if (new_ty == .none) return value;
  35663     return ip.getCoerced(gpa, value, new_ty);
  35664 }
  35665 
  35666 fn resolveAdHocInferredErrorSetTy(
  35667     sema: *Sema,
  35668     block: *Block,
  35669     src: LazySrcLoc,
  35670     ty: InternPool.Index,
  35671 ) CompileError!InternPool.Index {
  35672     const ies = sema.fn_ret_ty_ies orelse return .none;
  35673     const mod = sema.mod;
  35674     const gpa = sema.gpa;
  35675     const ip = &mod.intern_pool;
  35676     const error_union_info = switch (ip.indexToKey(ty)) {
  35677         .error_union_type => |x| x,
  35678         else => return .none,
  35679     };
  35680     if (error_union_info.error_set_type != .adhoc_inferred_error_set_type)
  35681         return .none;
  35682 
  35683     try sema.resolveInferredErrorSetPtr(block, src, ies);
  35684     const new_ty = try ip.get(gpa, .{ .error_union_type = .{
  35685         .error_set_type = ies.resolved,
  35686         .payload_type = error_union_info.payload_type,
  35687     } });
  35688     return new_ty;
  35689 }
  35690 
  35691 fn resolveInferredErrorSetTy(
  35692     sema: *Sema,
  35693     block: *Block,
  35694     src: LazySrcLoc,
  35695     ty: InternPool.Index,
  35696 ) CompileError!InternPool.Index {
  35697     const mod = sema.mod;
  35698     const ip = &mod.intern_pool;
  35699     if (ty == .anyerror_type) return ty;
  35700     switch (ip.indexToKey(ty)) {
  35701         .error_set_type => return ty,
  35702         .inferred_error_set_type => return sema.resolveInferredErrorSet(block, src, ty),
  35703         else => unreachable,
  35704     }
  35705 }
  35706 
  35707 fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct {
  35708     /// fields_len
  35709     usize,
  35710     Zir.Inst.StructDecl.Small,
  35711     /// extra_index
  35712     usize,
  35713 } {
  35714     const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
  35715     assert(extended.opcode == .struct_decl);
  35716     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
  35717     var extra_index: usize = extended.operand;
  35718 
  35719     extra_index += @intFromBool(small.has_src_node);
  35720 
  35721     const fields_len = if (small.has_fields_len) blk: {
  35722         const fields_len = zir.extra[extra_index];
  35723         extra_index += 1;
  35724         break :blk fields_len;
  35725     } else 0;
  35726 
  35727     const decls_len = if (small.has_decls_len) decls_len: {
  35728         const decls_len = zir.extra[extra_index];
  35729         extra_index += 1;
  35730         break :decls_len decls_len;
  35731     } else 0;
  35732 
  35733     // The backing integer cannot be handled until `resolveStructLayout()`.
  35734     if (small.has_backing_int) {
  35735         const backing_int_body_len = zir.extra[extra_index];
  35736         extra_index += 1; // backing_int_body_len
  35737         if (backing_int_body_len == 0) {
  35738             extra_index += 1; // backing_int_ref
  35739         } else {
  35740             extra_index += backing_int_body_len; // backing_int_body_inst
  35741         }
  35742     }
  35743 
  35744     // Skip over decls.
  35745     var decls_it = zir.declIteratorInner(extra_index, decls_len);
  35746     while (decls_it.next()) |_| {}
  35747     extra_index = decls_it.extra_index;
  35748 
  35749     return .{ fields_len, small, extra_index };
  35750 }
  35751 
  35752 fn semaStructFields(
  35753     mod: *Module,
  35754     arena: Allocator,
  35755     struct_type: InternPool.Key.StructType,
  35756 ) CompileError!void {
  35757     const gpa = mod.gpa;
  35758     const ip = &mod.intern_pool;
  35759     const decl_index = struct_type.decl.unwrap() orelse return;
  35760     const decl = mod.declPtr(decl_index);
  35761     const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
  35762     const zir = mod.namespacePtr(namespace_index).file_scope.zir;
  35763     const zir_index = struct_type.zir_index;
  35764 
  35765     const src = LazySrcLoc.nodeOffset(0);
  35766     const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
  35767 
  35768     if (fields_len == 0) switch (struct_type.layout) {
  35769         .Packed => {
  35770             try semaBackingIntType(mod, struct_type);
  35771             return;
  35772         },
  35773         .Auto, .Extern => {
  35774             struct_type.flagsPtr(ip).layout_resolved = true;
  35775             return;
  35776         },
  35777     };
  35778 
  35779     var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa);
  35780     defer comptime_mutable_decls.deinit();
  35781 
  35782     var sema: Sema = .{
  35783         .mod = mod,
  35784         .gpa = gpa,
  35785         .arena = arena,
  35786         .code = zir,
  35787         .owner_decl = decl,
  35788         .owner_decl_index = decl_index,
  35789         .func_index = .none,
  35790         .func_is_naked = false,
  35791         .fn_ret_ty = Type.void,
  35792         .fn_ret_ty_ies = null,
  35793         .owner_func_index = .none,
  35794         .comptime_mutable_decls = &comptime_mutable_decls,
  35795     };
  35796     defer sema.deinit();
  35797 
  35798     var block_scope: Block = .{
  35799         .parent = null,
  35800         .sema = &sema,
  35801         .src_decl = decl_index,
  35802         .namespace = namespace_index,
  35803         .wip_capture_scope = try mod.createCaptureScope(decl.src_scope),
  35804         .instructions = .{},
  35805         .inlining = null,
  35806         .is_comptime = true,
  35807     };
  35808     defer assert(block_scope.instructions.items.len == 0);
  35809 
  35810     const Field = struct {
  35811         type_body_len: u32 = 0,
  35812         align_body_len: u32 = 0,
  35813         init_body_len: u32 = 0,
  35814         type_ref: Zir.Inst.Ref = .none,
  35815     };
  35816     const fields = try sema.arena.alloc(Field, fields_len);
  35817 
  35818     var any_inits = false;
  35819     var any_aligned = false;
  35820 
  35821     {
  35822         const bits_per_field = 4;
  35823         const fields_per_u32 = 32 / bits_per_field;
  35824         const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  35825         const flags_index = extra_index;
  35826         var bit_bag_index: usize = flags_index;
  35827         extra_index += bit_bags_count;
  35828         var cur_bit_bag: u32 = undefined;
  35829         var field_i: u32 = 0;
  35830         while (field_i < fields_len) : (field_i += 1) {
  35831             if (field_i % fields_per_u32 == 0) {
  35832                 cur_bit_bag = zir.extra[bit_bag_index];
  35833                 bit_bag_index += 1;
  35834             }
  35835             const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  35836             cur_bit_bag >>= 1;
  35837             const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
  35838             cur_bit_bag >>= 1;
  35839             const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0;
  35840             cur_bit_bag >>= 1;
  35841             const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
  35842             cur_bit_bag >>= 1;
  35843 
  35844             if (is_comptime) struct_type.setFieldComptime(ip, field_i);
  35845 
  35846             var opt_field_name_zir: ?[:0]const u8 = null;
  35847             if (!small.is_tuple) {
  35848                 opt_field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
  35849                 extra_index += 1;
  35850             }
  35851             extra_index += 1; // doc_comment
  35852 
  35853             fields[field_i] = .{};
  35854 
  35855             if (has_type_body) {
  35856                 fields[field_i].type_body_len = zir.extra[extra_index];
  35857             } else {
  35858                 fields[field_i].type_ref = @enumFromInt(zir.extra[extra_index]);
  35859             }
  35860             extra_index += 1;
  35861 
  35862             // This string needs to outlive the ZIR code.
  35863             if (opt_field_name_zir) |field_name_zir| {
  35864                 const field_name = try ip.getOrPutString(gpa, field_name_zir);
  35865                 if (struct_type.addFieldName(ip, field_name)) |other_index| {
  35866                     const msg = msg: {
  35867                         const field_src = mod.fieldSrcLoc(decl_index, .{ .index = field_i }).lazy;
  35868                         const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{}'", .{field_name.fmt(ip)});
  35869                         errdefer msg.destroy(gpa);
  35870 
  35871                         const prev_field_src = mod.fieldSrcLoc(decl_index, .{ .index = other_index });
  35872                         try mod.errNoteNonLazy(prev_field_src, msg, "other field here", .{});
  35873                         try sema.errNote(&block_scope, src, msg, "struct declared here", .{});
  35874                         break :msg msg;
  35875                     };
  35876                     return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35877                 }
  35878             }
  35879 
  35880             if (has_align) {
  35881                 fields[field_i].align_body_len = zir.extra[extra_index];
  35882                 extra_index += 1;
  35883                 any_aligned = true;
  35884             }
  35885             if (has_init) {
  35886                 fields[field_i].init_body_len = zir.extra[extra_index];
  35887                 extra_index += 1;
  35888                 any_inits = true;
  35889             }
  35890         }
  35891     }
  35892 
  35893     // Next we do only types and alignments, saving the inits for a second pass,
  35894     // so that init values may depend on type layout.
  35895 
  35896     for (fields, 0..) |zir_field, field_i| {
  35897         const field_ty: Type = ty: {
  35898             if (zir_field.type_ref != .none) {
  35899                 break :ty sema.resolveType(&block_scope, .unneeded, zir_field.type_ref) catch |err| switch (err) {
  35900                     error.NeededSourceLocation => {
  35901                         const ty_src = mod.fieldSrcLoc(decl_index, .{
  35902                             .index = field_i,
  35903                             .range = .type,
  35904                         }).lazy;
  35905                         _ = try sema.resolveType(&block_scope, ty_src, zir_field.type_ref);
  35906                         unreachable;
  35907                     },
  35908                     else => |e| return e,
  35909                 };
  35910             }
  35911             assert(zir_field.type_body_len != 0);
  35912             const body = zir.bodySlice(extra_index, zir_field.type_body_len);
  35913             extra_index += body.len;
  35914             const ty_ref = try sema.resolveBody(&block_scope, body, zir_index);
  35915             break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) {
  35916                 error.NeededSourceLocation => {
  35917                     const ty_src = mod.fieldSrcLoc(decl_index, .{
  35918                         .index = field_i,
  35919                         .range = .type,
  35920                     }).lazy;
  35921                     _ = try sema.analyzeAsType(&block_scope, ty_src, ty_ref);
  35922                     unreachable;
  35923                 },
  35924                 else => |e| return e,
  35925             };
  35926         };
  35927         if (field_ty.isGenericPoison()) {
  35928             return error.GenericPoison;
  35929         }
  35930 
  35931         struct_type.field_types.get(ip)[field_i] = field_ty.toIntern();
  35932 
  35933         if (field_ty.zigTypeTag(mod) == .Opaque) {
  35934             const msg = msg: {
  35935                 const ty_src = mod.fieldSrcLoc(decl_index, .{
  35936                     .index = field_i,
  35937                     .range = .type,
  35938                 }).lazy;
  35939                 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  35940                 errdefer msg.destroy(sema.gpa);
  35941 
  35942                 try sema.addDeclaredHereNote(msg, field_ty);
  35943                 break :msg msg;
  35944             };
  35945             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35946         }
  35947         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  35948             const msg = msg: {
  35949                 const ty_src = mod.fieldSrcLoc(decl_index, .{
  35950                     .index = field_i,
  35951                     .range = .type,
  35952                 }).lazy;
  35953                 const msg = try sema.errMsg(&block_scope, ty_src, "struct fields cannot be 'noreturn'", .{});
  35954                 errdefer msg.destroy(sema.gpa);
  35955 
  35956                 try sema.addDeclaredHereNote(msg, field_ty);
  35957                 break :msg msg;
  35958             };
  35959             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35960         }
  35961         switch (struct_type.layout) {
  35962             .Extern => if (!try sema.validateExternType(field_ty, .struct_field)) {
  35963                 const msg = msg: {
  35964                     const ty_src = mod.fieldSrcLoc(decl_index, .{
  35965                         .index = field_i,
  35966                         .range = .type,
  35967                     });
  35968                     const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  35969                     errdefer msg.destroy(sema.gpa);
  35970 
  35971                     try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .struct_field);
  35972 
  35973                     try sema.addDeclaredHereNote(msg, field_ty);
  35974                     break :msg msg;
  35975                 };
  35976                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35977             },
  35978             .Packed => if (!try sema.validatePackedType(field_ty)) {
  35979                 const msg = msg: {
  35980                     const ty_src = mod.fieldSrcLoc(decl_index, .{
  35981                         .index = field_i,
  35982                         .range = .type,
  35983                     });
  35984                     const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  35985                     errdefer msg.destroy(sema.gpa);
  35986 
  35987                     try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty);
  35988 
  35989                     try sema.addDeclaredHereNote(msg, field_ty);
  35990                     break :msg msg;
  35991                 };
  35992                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35993             },
  35994             else => {},
  35995         }
  35996 
  35997         if (zir_field.align_body_len > 0) {
  35998             const body = zir.bodySlice(extra_index, zir_field.align_body_len);
  35999             extra_index += body.len;
  36000             const align_ref = try sema.resolveBody(&block_scope, body, zir_index);
  36001             const field_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) {
  36002                 error.NeededSourceLocation => {
  36003                     const align_src = mod.fieldSrcLoc(decl_index, .{
  36004                         .index = field_i,
  36005                         .range = .alignment,
  36006                     }).lazy;
  36007                     _ = try sema.analyzeAsAlign(&block_scope, align_src, align_ref);
  36008                     unreachable;
  36009                 },
  36010                 else => |e| return e,
  36011             };
  36012             struct_type.field_aligns.get(ip)[field_i] = field_align;
  36013         }
  36014 
  36015         extra_index += zir_field.init_body_len;
  36016     }
  36017 
  36018     struct_type.clearTypesWip(ip);
  36019     if (!any_inits) struct_type.setHaveFieldInits(ip);
  36020 
  36021     for (comptime_mutable_decls.items) |ct_decl_index| {
  36022         const ct_decl = mod.declPtr(ct_decl_index);
  36023         _ = try ct_decl.internValue(mod);
  36024     }
  36025 }
  36026 
  36027 // This logic must be kept in sync with `semaStructFields`
  36028 fn semaStructFieldInits(
  36029     mod: *Module,
  36030     arena: Allocator,
  36031     struct_type: InternPool.Key.StructType,
  36032 ) CompileError!void {
  36033     const gpa = mod.gpa;
  36034     const ip = &mod.intern_pool;
  36035 
  36036     assert(!struct_type.haveFieldInits(ip));
  36037 
  36038     const decl_index = struct_type.decl.unwrap() orelse return;
  36039     const decl = mod.declPtr(decl_index);
  36040     const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
  36041     const zir = mod.namespacePtr(namespace_index).file_scope.zir;
  36042     const zir_index = struct_type.zir_index;
  36043     const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
  36044 
  36045     var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa);
  36046     defer comptime_mutable_decls.deinit();
  36047 
  36048     var sema: Sema = .{
  36049         .mod = mod,
  36050         .gpa = gpa,
  36051         .arena = arena,
  36052         .code = zir,
  36053         .owner_decl = decl,
  36054         .owner_decl_index = decl_index,
  36055         .func_index = .none,
  36056         .func_is_naked = false,
  36057         .fn_ret_ty = Type.void,
  36058         .fn_ret_ty_ies = null,
  36059         .owner_func_index = .none,
  36060         .comptime_mutable_decls = &comptime_mutable_decls,
  36061     };
  36062     defer sema.deinit();
  36063 
  36064     var block_scope: Block = .{
  36065         .parent = null,
  36066         .sema = &sema,
  36067         .src_decl = decl_index,
  36068         .namespace = namespace_index,
  36069         .wip_capture_scope = try mod.createCaptureScope(decl.src_scope),
  36070         .instructions = .{},
  36071         .inlining = null,
  36072         .is_comptime = true,
  36073     };
  36074     defer assert(block_scope.instructions.items.len == 0);
  36075 
  36076     const Field = struct {
  36077         type_body_len: u32 = 0,
  36078         align_body_len: u32 = 0,
  36079         init_body_len: u32 = 0,
  36080     };
  36081     const fields = try sema.arena.alloc(Field, fields_len);
  36082 
  36083     var any_inits = false;
  36084 
  36085     {
  36086         const bits_per_field = 4;
  36087         const fields_per_u32 = 32 / bits_per_field;
  36088         const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  36089         const flags_index = extra_index;
  36090         var bit_bag_index: usize = flags_index;
  36091         extra_index += bit_bags_count;
  36092         var cur_bit_bag: u32 = undefined;
  36093         var field_i: u32 = 0;
  36094         while (field_i < fields_len) : (field_i += 1) {
  36095             if (field_i % fields_per_u32 == 0) {
  36096                 cur_bit_bag = zir.extra[bit_bag_index];
  36097                 bit_bag_index += 1;
  36098             }
  36099             const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  36100             cur_bit_bag >>= 1;
  36101             const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
  36102             cur_bit_bag >>= 2;
  36103             const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
  36104             cur_bit_bag >>= 1;
  36105 
  36106             if (!small.is_tuple) {
  36107                 extra_index += 1;
  36108             }
  36109             extra_index += 1; // doc_comment
  36110 
  36111             fields[field_i] = .{};
  36112 
  36113             if (has_type_body) fields[field_i].type_body_len = zir.extra[extra_index];
  36114             extra_index += 1;
  36115 
  36116             if (has_align) {
  36117                 fields[field_i].align_body_len = zir.extra[extra_index];
  36118                 extra_index += 1;
  36119             }
  36120             if (has_init) {
  36121                 fields[field_i].init_body_len = zir.extra[extra_index];
  36122                 extra_index += 1;
  36123                 any_inits = true;
  36124             }
  36125         }
  36126     }
  36127 
  36128     if (any_inits) {
  36129         for (fields, 0..) |zir_field, field_i| {
  36130             extra_index += zir_field.type_body_len;
  36131             extra_index += zir_field.align_body_len;
  36132             const body = zir.bodySlice(extra_index, zir_field.init_body_len);
  36133             extra_index += zir_field.init_body_len;
  36134 
  36135             if (body.len == 0) continue;
  36136 
  36137             // Pre-populate the type mapping the body expects to be there.
  36138             // In init bodies, the zir index of the struct itself is used
  36139             // to refer to the current field type.
  36140 
  36141             const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_i]);
  36142             const type_ref = Air.internedToRef(field_ty.toIntern());
  36143             try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index});
  36144             sema.inst_map.putAssumeCapacity(zir_index, type_ref);
  36145 
  36146             const init = try sema.resolveBody(&block_scope, body, zir_index);
  36147             const coerced = sema.coerce(&block_scope, field_ty, init, .unneeded) catch |err| switch (err) {
  36148                 error.NeededSourceLocation => {
  36149                     const init_src = mod.fieldSrcLoc(decl_index, .{
  36150                         .index = field_i,
  36151                         .range = .value,
  36152                     }).lazy;
  36153                     _ = try sema.coerce(&block_scope, field_ty, init, init_src);
  36154                     unreachable;
  36155                 },
  36156                 else => |e| return e,
  36157             };
  36158             const default_val = (try sema.resolveValue(coerced)) orelse {
  36159                 const init_src = mod.fieldSrcLoc(decl_index, .{
  36160                     .index = field_i,
  36161                     .range = .value,
  36162                 }).lazy;
  36163                 return sema.failWithNeededComptime(&block_scope, init_src, .{
  36164                     .needed_comptime_reason = "struct field default value must be comptime-known",
  36165                 });
  36166             };
  36167 
  36168             const field_init = try default_val.intern(field_ty, mod);
  36169             struct_type.field_inits.get(ip)[field_i] = field_init;
  36170         }
  36171     }
  36172 
  36173     for (comptime_mutable_decls.items) |ct_decl_index| {
  36174         const ct_decl = mod.declPtr(ct_decl_index);
  36175         _ = try ct_decl.internValue(mod);
  36176     }
  36177 }
  36178 
  36179 fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.UnionType) CompileError!void {
  36180     const tracy = trace(@src());
  36181     defer tracy.end();
  36182 
  36183     const gpa = mod.gpa;
  36184     const ip = &mod.intern_pool;
  36185     const decl_index = union_type.decl;
  36186     const zir = mod.namespacePtr(union_type.namespace).file_scope.zir;
  36187     const extended = zir.instructions.items(.data)[@intFromEnum(union_type.zir_index)].extended;
  36188     assert(extended.opcode == .union_decl);
  36189     const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
  36190     var extra_index: usize = extended.operand;
  36191 
  36192     const src = LazySrcLoc.nodeOffset(0);
  36193     extra_index += @intFromBool(small.has_src_node);
  36194 
  36195     const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: {
  36196         const ty_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  36197         extra_index += 1;
  36198         break :blk ty_ref;
  36199     } else .none;
  36200 
  36201     const body_len = if (small.has_body_len) blk: {
  36202         const body_len = zir.extra[extra_index];
  36203         extra_index += 1;
  36204         break :blk body_len;
  36205     } else 0;
  36206 
  36207     const fields_len = if (small.has_fields_len) blk: {
  36208         const fields_len = zir.extra[extra_index];
  36209         extra_index += 1;
  36210         break :blk fields_len;
  36211     } else 0;
  36212 
  36213     const decls_len = if (small.has_decls_len) decls_len: {
  36214         const decls_len = zir.extra[extra_index];
  36215         extra_index += 1;
  36216         break :decls_len decls_len;
  36217     } else 0;
  36218 
  36219     // Skip over decls.
  36220     var decls_it = zir.declIteratorInner(extra_index, decls_len);
  36221     while (decls_it.next()) |_| {}
  36222     extra_index = decls_it.extra_index;
  36223 
  36224     const body = zir.bodySlice(extra_index, body_len);
  36225     extra_index += body.len;
  36226 
  36227     const decl = mod.declPtr(decl_index);
  36228 
  36229     var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa);
  36230     defer comptime_mutable_decls.deinit();
  36231 
  36232     var sema: Sema = .{
  36233         .mod = mod,
  36234         .gpa = gpa,
  36235         .arena = arena,
  36236         .code = zir,
  36237         .owner_decl = decl,
  36238         .owner_decl_index = decl_index,
  36239         .func_index = .none,
  36240         .func_is_naked = false,
  36241         .fn_ret_ty = Type.void,
  36242         .fn_ret_ty_ies = null,
  36243         .owner_func_index = .none,
  36244         .comptime_mutable_decls = &comptime_mutable_decls,
  36245     };
  36246     defer sema.deinit();
  36247 
  36248     var block_scope: Block = .{
  36249         .parent = null,
  36250         .sema = &sema,
  36251         .src_decl = decl_index,
  36252         .namespace = union_type.namespace,
  36253         .wip_capture_scope = try mod.createCaptureScope(decl.src_scope),
  36254         .instructions = .{},
  36255         .inlining = null,
  36256         .is_comptime = true,
  36257     };
  36258     defer assert(block_scope.instructions.items.len == 0);
  36259 
  36260     if (body.len != 0) {
  36261         try sema.analyzeBody(&block_scope, body);
  36262     }
  36263 
  36264     for (comptime_mutable_decls.items) |ct_decl_index| {
  36265         const ct_decl = mod.declPtr(ct_decl_index);
  36266         _ = try ct_decl.internValue(mod);
  36267     }
  36268 
  36269     var int_tag_ty: Type = undefined;
  36270     var enum_field_names: []InternPool.NullTerminatedString = &.{};
  36271     var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{};
  36272     var explicit_tags_seen: []bool = &.{};
  36273     if (tag_type_ref != .none) {
  36274         const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x };
  36275         const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref);
  36276         if (small.auto_enum_tag) {
  36277             // The provided type is an integer type and we must construct the enum tag type here.
  36278             int_tag_ty = provided_ty;
  36279             if (int_tag_ty.zigTypeTag(mod) != .Int and int_tag_ty.zigTypeTag(mod) != .ComptimeInt) {
  36280                 return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{}'", .{int_tag_ty.fmt(mod)});
  36281             }
  36282 
  36283             if (fields_len > 0) {
  36284                 const field_count_val = try mod.intValue(Type.comptime_int, fields_len - 1);
  36285                 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) {
  36286                     const msg = msg: {
  36287                         const msg = try sema.errMsg(&block_scope, tag_ty_src, "specified integer tag type cannot represent every field", .{});
  36288                         errdefer msg.destroy(sema.gpa);
  36289                         try sema.errNote(&block_scope, tag_ty_src, msg, "type '{}' cannot fit values in range 0...{d}", .{
  36290                             int_tag_ty.fmt(mod),
  36291                             fields_len - 1,
  36292                         });
  36293                         break :msg msg;
  36294                     };
  36295                     return sema.failWithOwnedErrorMsg(&block_scope, msg);
  36296                 }
  36297                 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  36298                 try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len);
  36299             }
  36300         } else {
  36301             // The provided type is the enum tag type.
  36302             union_type.tagTypePtr(ip).* = provided_ty.toIntern();
  36303             const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) {
  36304                 .enum_type => |x| x,
  36305                 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{provided_ty.fmt(mod)}),
  36306             };
  36307             // The fields of the union must match the enum exactly.
  36308             // A flag per field is used to check for missing and extraneous fields.
  36309             explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
  36310             @memset(explicit_tags_seen, false);
  36311         }
  36312     } else {
  36313         // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis
  36314         // purposes, we still auto-generate an enum tag type the same way. That the union is
  36315         // untagged is represented by the Type tag (union vs union_tagged).
  36316         enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  36317     }
  36318 
  36319     var field_types: std.ArrayListUnmanaged(InternPool.Index) = .{};
  36320     var field_aligns: std.ArrayListUnmanaged(InternPool.Alignment) = .{};
  36321     var field_name_table: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
  36322 
  36323     try field_types.ensureTotalCapacityPrecise(sema.arena, fields_len);
  36324     if (small.any_aligned_fields)
  36325         try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len);
  36326     try field_name_table.ensureTotalCapacity(sema.arena, fields_len);
  36327 
  36328     const bits_per_field = 4;
  36329     const fields_per_u32 = 32 / bits_per_field;
  36330     const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  36331     var bit_bag_index: usize = extra_index;
  36332     extra_index += bit_bags_count;
  36333     var cur_bit_bag: u32 = undefined;
  36334     var field_i: u32 = 0;
  36335     var last_tag_val: ?Value = null;
  36336     while (field_i < fields_len) : (field_i += 1) {
  36337         if (field_i % fields_per_u32 == 0) {
  36338             cur_bit_bag = zir.extra[bit_bag_index];
  36339             bit_bag_index += 1;
  36340         }
  36341         const has_type = @as(u1, @truncate(cur_bit_bag)) != 0;
  36342         cur_bit_bag >>= 1;
  36343         const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  36344         cur_bit_bag >>= 1;
  36345         const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0;
  36346         cur_bit_bag >>= 1;
  36347         const unused = @as(u1, @truncate(cur_bit_bag)) != 0;
  36348         cur_bit_bag >>= 1;
  36349         _ = unused;
  36350 
  36351         const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
  36352         extra_index += 1;
  36353 
  36354         // doc_comment
  36355         extra_index += 1;
  36356 
  36357         const field_type_ref: Zir.Inst.Ref = if (has_type) blk: {
  36358             const field_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  36359             extra_index += 1;
  36360             break :blk field_type_ref;
  36361         } else .none;
  36362 
  36363         const align_ref: Zir.Inst.Ref = if (has_align) blk: {
  36364             const align_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  36365             extra_index += 1;
  36366             break :blk align_ref;
  36367         } else .none;
  36368 
  36369         const tag_ref: Air.Inst.Ref = if (has_tag) blk: {
  36370             const tag_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  36371             extra_index += 1;
  36372             break :blk try sema.resolveInst(tag_ref);
  36373         } else .none;
  36374 
  36375         if (enum_field_vals.capacity() > 0) {
  36376             const enum_tag_val = if (tag_ref != .none) blk: {
  36377                 const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) {
  36378                     error.NeededSourceLocation => {
  36379                         const val_src = mod.fieldSrcLoc(union_type.decl, .{
  36380                             .index = field_i,
  36381                             .range = .value,
  36382                         }).lazy;
  36383                         _ = try sema.semaUnionFieldVal(&block_scope, val_src, int_tag_ty, tag_ref);
  36384                         unreachable;
  36385                     },
  36386                     else => |e| return e,
  36387                 };
  36388                 last_tag_val = val;
  36389 
  36390                 break :blk val;
  36391             } else blk: {
  36392                 const val = if (last_tag_val) |val|
  36393                     try sema.intAdd(val, Value.one_comptime_int, int_tag_ty, undefined)
  36394                 else
  36395                     try mod.intValue(int_tag_ty, 0);
  36396                 last_tag_val = val;
  36397 
  36398                 break :blk val;
  36399             };
  36400             const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern());
  36401             if (gop.found_existing) {
  36402                 const field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i }).lazy;
  36403                 const other_field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = gop.index }).lazy;
  36404                 const msg = msg: {
  36405                     const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{enum_tag_val.fmtValue(int_tag_ty, mod)});
  36406                     errdefer msg.destroy(gpa);
  36407                     try sema.errNote(&block_scope, other_field_src, msg, "other occurrence here", .{});
  36408                     break :msg msg;
  36409                 };
  36410                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  36411             }
  36412         }
  36413 
  36414         // This string needs to outlive the ZIR code.
  36415         const field_name = try ip.getOrPutString(gpa, field_name_zir);
  36416         if (enum_field_names.len != 0) {
  36417             enum_field_names[field_i] = field_name;
  36418         }
  36419 
  36420         const field_ty: Type = if (!has_type)
  36421             Type.void
  36422         else if (field_type_ref == .none)
  36423             Type.noreturn
  36424         else
  36425             sema.resolveType(&block_scope, .unneeded, field_type_ref) catch |err| switch (err) {
  36426                 error.NeededSourceLocation => {
  36427                     const ty_src = mod.fieldSrcLoc(union_type.decl, .{
  36428                         .index = field_i,
  36429                         .range = .type,
  36430                     }).lazy;
  36431                     _ = try sema.resolveType(&block_scope, ty_src, field_type_ref);
  36432                     unreachable;
  36433                 },
  36434                 else => |e| return e,
  36435             };
  36436 
  36437         if (field_ty.isGenericPoison()) {
  36438             return error.GenericPoison;
  36439         }
  36440 
  36441         const gop = field_name_table.getOrPutAssumeCapacity(field_name);
  36442         if (gop.found_existing) {
  36443             const msg = msg: {
  36444                 const field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i }).lazy;
  36445                 const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{}'", .{
  36446                     field_name.fmt(ip),
  36447                 });
  36448                 errdefer msg.destroy(gpa);
  36449 
  36450                 const prev_field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = gop.index }).lazy;
  36451                 try mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl, mod), msg, "other field here", .{});
  36452                 try sema.errNote(&block_scope, src, msg, "union declared here", .{});
  36453                 break :msg msg;
  36454             };
  36455             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  36456         }
  36457 
  36458         if (explicit_tags_seen.len > 0) {
  36459             const tag_info = ip.indexToKey(union_type.tagTypePtr(ip).*).enum_type;
  36460             const enum_index = tag_info.nameIndex(ip, field_name) orelse {
  36461                 const msg = msg: {
  36462                     const ty_src = mod.fieldSrcLoc(union_type.decl, .{
  36463                         .index = field_i,
  36464                         .range = .type,
  36465                     }).lazy;
  36466                     const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{}' in enum '{}'", .{
  36467                         field_name.fmt(ip), Type.fromInterned(union_type.tagTypePtr(ip).*).fmt(mod),
  36468                     });
  36469                     errdefer msg.destroy(sema.gpa);
  36470                     try sema.addDeclaredHereNote(msg, Type.fromInterned(union_type.tagTypePtr(ip).*));
  36471                     break :msg msg;
  36472                 };
  36473                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  36474             };
  36475             // No check for duplicate because the check already happened in order
  36476             // to create the enum type in the first place.
  36477             assert(!explicit_tags_seen[enum_index]);
  36478             explicit_tags_seen[enum_index] = true;
  36479 
  36480             // Enforce the enum fields and the union fields being in the same order.
  36481             if (enum_index != field_i) {
  36482                 const msg = msg: {
  36483                     const ty_src = mod.fieldSrcLoc(union_type.decl, .{
  36484                         .index = field_i,
  36485                         .range = .type,
  36486                     }).lazy;
  36487                     const enum_field_src = mod.fieldSrcLoc(tag_info.decl, .{ .index = enum_index }).lazy;
  36488                     const msg = try sema.errMsg(&block_scope, ty_src, "union field '{}' ordered differently than corresponding enum field", .{
  36489                         field_name.fmt(ip),
  36490                     });
  36491                     errdefer msg.destroy(sema.gpa);
  36492                     try sema.errNote(&block_scope, enum_field_src, msg, "enum field here", .{});
  36493                     break :msg msg;
  36494                 };
  36495                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  36496             }
  36497         }
  36498 
  36499         if (field_ty.zigTypeTag(mod) == .Opaque) {
  36500             const msg = msg: {
  36501                 const ty_src = mod.fieldSrcLoc(union_type.decl, .{
  36502                     .index = field_i,
  36503                     .range = .type,
  36504                 }).lazy;
  36505                 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
  36506                 errdefer msg.destroy(sema.gpa);
  36507 
  36508                 try sema.addDeclaredHereNote(msg, field_ty);
  36509                 break :msg msg;
  36510             };
  36511             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  36512         }
  36513         const layout = union_type.getLayout(ip);
  36514         if (layout == .Extern and
  36515             !try sema.validateExternType(field_ty, .union_field))
  36516         {
  36517             const msg = msg: {
  36518                 const ty_src = mod.fieldSrcLoc(union_type.decl, .{
  36519                     .index = field_i,
  36520                     .range = .type,
  36521                 });
  36522                 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  36523                 errdefer msg.destroy(sema.gpa);
  36524 
  36525                 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .union_field);
  36526 
  36527                 try sema.addDeclaredHereNote(msg, field_ty);
  36528                 break :msg msg;
  36529             };
  36530             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  36531         } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) {
  36532             const msg = msg: {
  36533                 const ty_src = mod.fieldSrcLoc(union_type.decl, .{
  36534                     .index = field_i,
  36535                     .range = .type,
  36536                 });
  36537                 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  36538                 errdefer msg.destroy(sema.gpa);
  36539 
  36540                 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty);
  36541 
  36542                 try sema.addDeclaredHereNote(msg, field_ty);
  36543                 break :msg msg;
  36544             };
  36545             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  36546         }
  36547 
  36548         field_types.appendAssumeCapacity(field_ty.toIntern());
  36549 
  36550         if (small.any_aligned_fields) {
  36551             field_aligns.appendAssumeCapacity(if (align_ref != .none)
  36552                 sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) {
  36553                     error.NeededSourceLocation => {
  36554                         const align_src = mod.fieldSrcLoc(union_type.decl, .{
  36555                             .index = field_i,
  36556                             .range = .alignment,
  36557                         }).lazy;
  36558                         _ = try sema.resolveAlign(&block_scope, align_src, align_ref);
  36559                         unreachable;
  36560                     },
  36561                     else => |e| return e,
  36562                 }
  36563             else
  36564                 .none);
  36565         } else {
  36566             assert(align_ref == .none);
  36567         }
  36568     }
  36569 
  36570     union_type.setFieldTypes(ip, field_types.items);
  36571     union_type.setFieldAligns(ip, field_aligns.items);
  36572 
  36573     if (explicit_tags_seen.len > 0) {
  36574         const tag_info = ip.indexToKey(union_type.tagTypePtr(ip).*).enum_type;
  36575         if (tag_info.names.len > fields_len) {
  36576             const msg = msg: {
  36577                 const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{});
  36578                 errdefer msg.destroy(sema.gpa);
  36579 
  36580                 for (tag_info.names.get(ip), 0..) |field_name, field_index| {
  36581                     if (explicit_tags_seen[field_index]) continue;
  36582                     try sema.addFieldErrNote(Type.fromInterned(union_type.tagTypePtr(ip).*), field_index, msg, "field '{}' missing, declared here", .{
  36583                         field_name.fmt(ip),
  36584                     });
  36585                 }
  36586                 try sema.addDeclaredHereNote(msg, Type.fromInterned(union_type.tagTypePtr(ip).*));
  36587                 break :msg msg;
  36588             };
  36589             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  36590         }
  36591     } else if (enum_field_vals.count() > 0) {
  36592         const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), mod.declPtr(union_type.decl));
  36593         union_type.tagTypePtr(ip).* = enum_ty;
  36594     } else {
  36595         const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_type.decl.toOptional());
  36596         union_type.tagTypePtr(ip).* = enum_ty;
  36597     }
  36598 }
  36599 
  36600 fn semaUnionFieldVal(sema: *Sema, block: *Block, src: LazySrcLoc, int_tag_ty: Type, tag_ref: Air.Inst.Ref) CompileError!Value {
  36601     const coerced = try sema.coerce(block, int_tag_ty, tag_ref, src);
  36602     return sema.resolveConstDefinedValue(block, src, coerced, .{
  36603         .needed_comptime_reason = "enum tag value must be comptime-known",
  36604     });
  36605 }
  36606 
  36607 fn generateUnionTagTypeNumbered(
  36608     sema: *Sema,
  36609     block: *Block,
  36610     enum_field_names: []const InternPool.NullTerminatedString,
  36611     enum_field_vals: []const InternPool.Index,
  36612     decl: *Module.Decl,
  36613 ) !InternPool.Index {
  36614     const mod = sema.mod;
  36615     const gpa = sema.gpa;
  36616     const ip = &mod.intern_pool;
  36617 
  36618     const src_decl = mod.declPtr(block.src_decl);
  36619     const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope);
  36620     errdefer mod.destroyDecl(new_decl_index);
  36621     const fqn = try decl.getFullyQualifiedName(mod);
  36622     const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)});
  36623     try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, .{
  36624         .ty = Type.noreturn,
  36625         .val = Value.@"unreachable",
  36626     }, name);
  36627     errdefer mod.abortAnonDecl(new_decl_index);
  36628 
  36629     const new_decl = mod.declPtr(new_decl_index);
  36630     new_decl.name_fully_qualified = true;
  36631     new_decl.owns_tv = true;
  36632     new_decl.name_fully_qualified = true;
  36633 
  36634     const enum_ty = try ip.getEnum(gpa, .{
  36635         .decl = new_decl_index,
  36636         .namespace = .none,
  36637         .tag_ty = if (enum_field_vals.len == 0)
  36638             (try mod.intType(.unsigned, 0)).toIntern()
  36639         else
  36640             ip.typeOf(enum_field_vals[0]),
  36641         .names = enum_field_names,
  36642         .values = enum_field_vals,
  36643         .tag_mode = .explicit,
  36644     });
  36645 
  36646     new_decl.ty = Type.type;
  36647     new_decl.val = Value.fromInterned(enum_ty);
  36648 
  36649     try mod.finalizeAnonDecl(new_decl_index);
  36650     return enum_ty;
  36651 }
  36652 
  36653 fn generateUnionTagTypeSimple(
  36654     sema: *Sema,
  36655     block: *Block,
  36656     enum_field_names: []const InternPool.NullTerminatedString,
  36657     maybe_decl_index: InternPool.OptionalDeclIndex,
  36658 ) !InternPool.Index {
  36659     const mod = sema.mod;
  36660     const ip = &mod.intern_pool;
  36661     const gpa = sema.gpa;
  36662 
  36663     const new_decl_index = new_decl_index: {
  36664         const decl_index = maybe_decl_index.unwrap() orelse {
  36665             break :new_decl_index try mod.createAnonymousDecl(block, .{
  36666                 .ty = Type.noreturn,
  36667                 .val = Value.@"unreachable",
  36668             });
  36669         };
  36670         const fqn = try mod.declPtr(decl_index).getFullyQualifiedName(mod);
  36671         const src_decl = mod.declPtr(block.src_decl);
  36672         const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope);
  36673         errdefer mod.destroyDecl(new_decl_index);
  36674         const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)});
  36675         try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, .{
  36676             .ty = Type.noreturn,
  36677             .val = Value.@"unreachable",
  36678         }, name);
  36679         mod.declPtr(new_decl_index).name_fully_qualified = true;
  36680         break :new_decl_index new_decl_index;
  36681     };
  36682     errdefer mod.abortAnonDecl(new_decl_index);
  36683 
  36684     const enum_ty = try ip.getEnum(gpa, .{
  36685         .decl = new_decl_index,
  36686         .namespace = .none,
  36687         .tag_ty = if (enum_field_names.len == 0)
  36688             (try mod.intType(.unsigned, 0)).toIntern()
  36689         else
  36690             (try mod.smallestUnsignedInt(enum_field_names.len - 1)).toIntern(),
  36691         .names = enum_field_names,
  36692         .values = &.{},
  36693         .tag_mode = .auto,
  36694     });
  36695 
  36696     const new_decl = mod.declPtr(new_decl_index);
  36697     new_decl.owns_tv = true;
  36698     new_decl.ty = Type.type;
  36699     new_decl.val = Value.fromInterned(enum_ty);
  36700 
  36701     try mod.finalizeAnonDecl(new_decl_index);
  36702     return enum_ty;
  36703 }
  36704 
  36705 fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref {
  36706     const mod = sema.mod;
  36707     const gpa = sema.gpa;
  36708     const src = LazySrcLoc.nodeOffset(0);
  36709 
  36710     var block: Block = .{
  36711         .parent = null,
  36712         .sema = sema,
  36713         .src_decl = sema.owner_decl_index,
  36714         .namespace = sema.owner_decl.src_namespace,
  36715         .wip_capture_scope = try mod.createCaptureScope(sema.owner_decl.src_scope),
  36716         .instructions = .{},
  36717         .inlining = null,
  36718         .is_comptime = true,
  36719     };
  36720     defer block.instructions.deinit(gpa);
  36721 
  36722     const decl_index = try getBuiltinDecl(sema, &block, name);
  36723     return sema.analyzeDeclVal(&block, src, decl_index);
  36724 }
  36725 
  36726 fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!InternPool.DeclIndex {
  36727     const gpa = sema.gpa;
  36728 
  36729     const src = LazySrcLoc.nodeOffset(0);
  36730 
  36731     const mod = sema.mod;
  36732     const ip = &mod.intern_pool;
  36733     const std_mod = mod.std_mod;
  36734     const std_file = (mod.importPkg(std_mod) catch unreachable).file;
  36735     const opt_builtin_inst = (try sema.namespaceLookupRef(
  36736         block,
  36737         src,
  36738         mod.declPtr(std_file.root_decl.unwrap().?).src_namespace,
  36739         try ip.getOrPutString(gpa, "builtin"),
  36740     )) orelse @panic("lib/std.zig is corrupt and missing 'builtin'");
  36741     const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst, src);
  36742     const builtin_ty = sema.analyzeAsType(block, src, builtin_inst) catch |err| switch (err) {
  36743         error.AnalysisFail => std.debug.panic("std.builtin is corrupt", .{}),
  36744         else => |e| return e,
  36745     };
  36746     const decl_index = (try sema.namespaceLookup(
  36747         block,
  36748         src,
  36749         builtin_ty.getNamespaceIndex(mod).unwrap().?,
  36750         try ip.getOrPutString(gpa, name),
  36751     )) orelse std.debug.panic("lib/std/builtin.zig is corrupt and missing '{s}'", .{name});
  36752     return decl_index;
  36753 }
  36754 
  36755 fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type {
  36756     const mod = sema.mod;
  36757     const ty_inst = try sema.getBuiltin(name);
  36758 
  36759     var block: Block = .{
  36760         .parent = null,
  36761         .sema = sema,
  36762         .src_decl = sema.owner_decl_index,
  36763         .namespace = sema.owner_decl.src_namespace,
  36764         .wip_capture_scope = try mod.createCaptureScope(sema.owner_decl.src_scope),
  36765         .instructions = .{},
  36766         .inlining = null,
  36767         .is_comptime = true,
  36768     };
  36769     defer block.instructions.deinit(sema.gpa);
  36770     const src = LazySrcLoc.nodeOffset(0);
  36771 
  36772     const result_ty = sema.analyzeAsType(&block, src, ty_inst) catch |err| switch (err) {
  36773         error.AnalysisFail => std.debug.panic("std.builtin.{s} is corrupt", .{name}),
  36774         else => |e| return e,
  36775     };
  36776     try sema.resolveTypeFully(result_ty); // Should not fail
  36777     return result_ty;
  36778 }
  36779 
  36780 /// There is another implementation of this in `Type.onePossibleValue`. This one
  36781 /// in `Sema` is for calling during semantic analysis, and performs field resolution
  36782 /// to get the answer. The one in `Type` is for calling during codegen and asserts
  36783 /// that the types are already resolved.
  36784 /// TODO assert the return value matches `ty.onePossibleValue`
  36785 pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
  36786     const mod = sema.mod;
  36787     const ip = &mod.intern_pool;
  36788     return switch (ty.toIntern()) {
  36789         .u0_type,
  36790         .i0_type,
  36791         => try mod.intValue(ty, 0),
  36792         .u1_type,
  36793         .u8_type,
  36794         .i8_type,
  36795         .u16_type,
  36796         .i16_type,
  36797         .u29_type,
  36798         .u32_type,
  36799         .i32_type,
  36800         .u64_type,
  36801         .i64_type,
  36802         .u80_type,
  36803         .u128_type,
  36804         .i128_type,
  36805         .usize_type,
  36806         .isize_type,
  36807         .c_char_type,
  36808         .c_short_type,
  36809         .c_ushort_type,
  36810         .c_int_type,
  36811         .c_uint_type,
  36812         .c_long_type,
  36813         .c_ulong_type,
  36814         .c_longlong_type,
  36815         .c_ulonglong_type,
  36816         .c_longdouble_type,
  36817         .f16_type,
  36818         .f32_type,
  36819         .f64_type,
  36820         .f80_type,
  36821         .f128_type,
  36822         .anyopaque_type,
  36823         .bool_type,
  36824         .type_type,
  36825         .anyerror_type,
  36826         .adhoc_inferred_error_set_type,
  36827         .comptime_int_type,
  36828         .comptime_float_type,
  36829         .enum_literal_type,
  36830         .atomic_order_type,
  36831         .atomic_rmw_op_type,
  36832         .calling_convention_type,
  36833         .address_space_type,
  36834         .float_mode_type,
  36835         .reduce_op_type,
  36836         .call_modifier_type,
  36837         .prefetch_options_type,
  36838         .export_options_type,
  36839         .extern_options_type,
  36840         .type_info_type,
  36841         .manyptr_u8_type,
  36842         .manyptr_const_u8_type,
  36843         .manyptr_const_u8_sentinel_0_type,
  36844         .single_const_pointer_to_comptime_int_type,
  36845         .slice_const_u8_type,
  36846         .slice_const_u8_sentinel_0_type,
  36847         .anyerror_void_error_union_type,
  36848         => null,
  36849         .void_type => Value.void,
  36850         .noreturn_type => Value.@"unreachable",
  36851         .anyframe_type => unreachable,
  36852         .null_type => Value.null,
  36853         .undefined_type => Value.undef,
  36854         .optional_noreturn_type => try mod.nullValue(ty),
  36855         .generic_poison_type => error.GenericPoison,
  36856         .empty_struct_type => Value.empty_struct,
  36857         // values, not types
  36858         .undef,
  36859         .zero,
  36860         .zero_usize,
  36861         .zero_u8,
  36862         .one,
  36863         .one_usize,
  36864         .one_u8,
  36865         .four_u8,
  36866         .negative_one,
  36867         .calling_convention_c,
  36868         .calling_convention_inline,
  36869         .void_value,
  36870         .unreachable_value,
  36871         .null_value,
  36872         .bool_true,
  36873         .bool_false,
  36874         .empty_struct,
  36875         .generic_poison,
  36876         // invalid
  36877         .var_args_param_type,
  36878         .none,
  36879         => unreachable,
  36880 
  36881         _ => switch (ip.items.items(.tag)[@intFromEnum(ty.toIntern())]) {
  36882             .type_int_signed, // i0 handled above
  36883             .type_int_unsigned, // u0 handled above
  36884             .type_pointer,
  36885             .type_slice,
  36886             .type_optional, // ?noreturn handled above
  36887             .type_anyframe,
  36888             .type_error_union,
  36889             .type_anyerror_union,
  36890             .type_error_set,
  36891             .type_inferred_error_set,
  36892             .type_opaque,
  36893             .type_function,
  36894             => null,
  36895 
  36896             .simple_type, // handled above
  36897             // values, not types
  36898             .undef,
  36899             .simple_value,
  36900             .ptr_decl,
  36901             .ptr_anon_decl,
  36902             .ptr_anon_decl_aligned,
  36903             .ptr_mut_decl,
  36904             .ptr_comptime_field,
  36905             .ptr_int,
  36906             .ptr_eu_payload,
  36907             .ptr_opt_payload,
  36908             .ptr_elem,
  36909             .ptr_field,
  36910             .ptr_slice,
  36911             .opt_payload,
  36912             .opt_null,
  36913             .int_u8,
  36914             .int_u16,
  36915             .int_u32,
  36916             .int_i32,
  36917             .int_usize,
  36918             .int_comptime_int_u32,
  36919             .int_comptime_int_i32,
  36920             .int_small,
  36921             .int_positive,
  36922             .int_negative,
  36923             .int_lazy_align,
  36924             .int_lazy_size,
  36925             .error_set_error,
  36926             .error_union_error,
  36927             .error_union_payload,
  36928             .enum_literal,
  36929             .enum_tag,
  36930             .float_f16,
  36931             .float_f32,
  36932             .float_f64,
  36933             .float_f80,
  36934             .float_f128,
  36935             .float_c_longdouble_f80,
  36936             .float_c_longdouble_f128,
  36937             .float_comptime_float,
  36938             .variable,
  36939             .extern_func,
  36940             .func_decl,
  36941             .func_instance,
  36942             .func_coerced,
  36943             .only_possible_value,
  36944             .union_value,
  36945             .bytes,
  36946             .aggregate,
  36947             .repeated,
  36948             // memoized value, not types
  36949             .memoized_call,
  36950             => unreachable,
  36951 
  36952             .type_array_big,
  36953             .type_array_small,
  36954             .type_vector,
  36955             .type_enum_auto,
  36956             .type_enum_explicit,
  36957             .type_enum_nonexhaustive,
  36958             .type_struct,
  36959             .type_struct_ns,
  36960             .type_struct_anon,
  36961             .type_struct_packed,
  36962             .type_struct_packed_inits,
  36963             .type_tuple_anon,
  36964             .type_union,
  36965             => switch (ip.indexToKey(ty.toIntern())) {
  36966                 inline .array_type, .vector_type => |seq_type, seq_tag| {
  36967                     const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none;
  36968                     if (seq_type.len + @intFromBool(has_sentinel) == 0) return Value.fromInterned((try mod.intern(.{ .aggregate = .{
  36969                         .ty = ty.toIntern(),
  36970                         .storage = .{ .elems = &.{} },
  36971                     } })));
  36972 
  36973                     if (try sema.typeHasOnePossibleValue(Type.fromInterned(seq_type.child))) |opv| {
  36974                         return Value.fromInterned((try mod.intern(.{ .aggregate = .{
  36975                             .ty = ty.toIntern(),
  36976                             .storage = .{ .repeated_elem = opv.toIntern() },
  36977                         } })));
  36978                     }
  36979                     return null;
  36980                 },
  36981 
  36982                 .struct_type => |struct_type| {
  36983                     try sema.resolveTypeFields(ty);
  36984 
  36985                     if (struct_type.field_types.len == 0) {
  36986                         // In this case the struct has no fields at all and
  36987                         // therefore has one possible value.
  36988                         return Value.fromInterned((try mod.intern(.{ .aggregate = .{
  36989                             .ty = ty.toIntern(),
  36990                             .storage = .{ .elems = &.{} },
  36991                         } })));
  36992                     }
  36993 
  36994                     const field_vals = try sema.arena.alloc(
  36995                         InternPool.Index,
  36996                         struct_type.field_types.len,
  36997                     );
  36998                     for (field_vals, 0..) |*field_val, i| {
  36999                         if (struct_type.fieldIsComptime(ip, i)) {
  37000                             try sema.resolveStructFieldInits(ty);
  37001                             field_val.* = struct_type.field_inits.get(ip)[i];
  37002                             continue;
  37003                         }
  37004                         const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
  37005                         if (field_ty.eql(ty, mod)) {
  37006                             const msg = try Module.ErrorMsg.create(
  37007                                 sema.gpa,
  37008                                 mod.declPtr(struct_type.decl.unwrap().?).srcLoc(mod),
  37009                                 "struct '{}' depends on itself",
  37010                                 .{ty.fmt(mod)},
  37011                             );
  37012                             try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{});
  37013                             return sema.failWithOwnedErrorMsg(null, msg);
  37014                         }
  37015                         if (try sema.typeHasOnePossibleValue(field_ty)) |field_opv| {
  37016                             field_val.* = try field_opv.intern(field_ty, mod);
  37017                         } else return null;
  37018                     }
  37019 
  37020                     // In this case the struct has no runtime-known fields and
  37021                     // therefore has one possible value.
  37022                     return Value.fromInterned((try mod.intern(.{ .aggregate = .{
  37023                         .ty = ty.toIntern(),
  37024                         .storage = .{ .elems = field_vals },
  37025                     } })));
  37026                 },
  37027 
  37028                 .anon_struct_type => |tuple| {
  37029                     for (tuple.values.get(ip)) |val| {
  37030                         if (val == .none) return null;
  37031                     }
  37032                     // In this case the struct has all comptime-known fields and
  37033                     // therefore has one possible value.
  37034                     // TODO: write something like getCoercedInts to avoid needing to dupe
  37035                     return Value.fromInterned((try mod.intern(.{ .aggregate = .{
  37036                         .ty = ty.toIntern(),
  37037                         .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values.get(ip)) },
  37038                     } })));
  37039                 },
  37040 
  37041                 .union_type => |union_type| {
  37042                     try sema.resolveTypeFields(ty);
  37043                     const union_obj = ip.loadUnionType(union_type);
  37044                     const tag_val = (try sema.typeHasOnePossibleValue(Type.fromInterned(union_obj.enum_tag_ty))) orelse
  37045                         return null;
  37046                     if (union_obj.field_types.len == 0) {
  37047                         const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
  37048                         return Value.fromInterned(only);
  37049                     }
  37050                     const only_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]);
  37051                     if (only_field_ty.eql(ty, mod)) {
  37052                         const msg = try Module.ErrorMsg.create(
  37053                             sema.gpa,
  37054                             mod.declPtr(union_obj.decl).srcLoc(mod),
  37055                             "union '{}' depends on itself",
  37056                             .{ty.fmt(mod)},
  37057                         );
  37058                         try sema.addFieldErrNote(ty, 0, msg, "while checking this field", .{});
  37059                         return sema.failWithOwnedErrorMsg(null, msg);
  37060                     }
  37061                     const val_val = (try sema.typeHasOnePossibleValue(only_field_ty)) orelse
  37062                         return null;
  37063                     const only = try mod.intern(.{ .un = .{
  37064                         .ty = ty.toIntern(),
  37065                         .tag = tag_val.toIntern(),
  37066                         .val = val_val.toIntern(),
  37067                     } });
  37068                     return Value.fromInterned(only);
  37069                 },
  37070 
  37071                 .enum_type => |enum_type| switch (enum_type.tag_mode) {
  37072                     .nonexhaustive => {
  37073                         if (enum_type.tag_ty == .comptime_int_type) return null;
  37074 
  37075                         if (try sema.typeHasOnePossibleValue(Type.fromInterned(enum_type.tag_ty))) |int_opv| {
  37076                             const only = try mod.intern(.{ .enum_tag = .{
  37077                                 .ty = ty.toIntern(),
  37078                                 .int = int_opv.toIntern(),
  37079                             } });
  37080                             return Value.fromInterned(only);
  37081                         }
  37082 
  37083                         return null;
  37084                     },
  37085                     .auto, .explicit => {
  37086                         if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(mod)) return null;
  37087 
  37088                         return Value.fromInterned(switch (enum_type.names.len) {
  37089                             0 => try mod.intern(.{ .empty_enum_value = ty.toIntern() }),
  37090                             1 => try mod.intern(.{ .enum_tag = .{
  37091                                 .ty = ty.toIntern(),
  37092                                 .int = if (enum_type.values.len == 0)
  37093                                     (try mod.intValue(Type.fromInterned(enum_type.tag_ty), 0)).toIntern()
  37094                                 else
  37095                                     try mod.intern_pool.getCoercedInts(
  37096                                         mod.gpa,
  37097                                         mod.intern_pool.indexToKey(enum_type.values.get(ip)[0]).int,
  37098                                         enum_type.tag_ty,
  37099                                     ),
  37100                             } }),
  37101                             else => return null,
  37102                         });
  37103                     },
  37104                 },
  37105 
  37106                 else => unreachable,
  37107             },
  37108         },
  37109     };
  37110 }
  37111 
  37112 /// Returns the type of the AIR instruction.
  37113 fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
  37114     return sema.getTmpAir().typeOf(inst, &sema.mod.intern_pool);
  37115 }
  37116 
  37117 pub fn getTmpAir(sema: Sema) Air {
  37118     return .{
  37119         .instructions = sema.air_instructions.slice(),
  37120         .extra = sema.air_extra.items,
  37121     };
  37122 }
  37123 
  37124 pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {
  37125     const fields = std.meta.fields(@TypeOf(extra));
  37126     try sema.air_extra.ensureUnusedCapacity(sema.gpa, fields.len);
  37127     return sema.addExtraAssumeCapacity(extra);
  37128 }
  37129 
  37130 pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {
  37131     const fields = std.meta.fields(@TypeOf(extra));
  37132     const result: u32 = @intCast(sema.air_extra.items.len);
  37133     inline for (fields) |field| {
  37134         sema.air_extra.appendAssumeCapacity(switch (field.type) {
  37135             u32 => @field(extra, field.name),
  37136             i32 => @bitCast(@field(extra, field.name)),
  37137             Air.Inst.Ref, InternPool.Index => @intFromEnum(@field(extra, field.name)),
  37138             else => @compileError("bad field type: " ++ @typeName(field.type)),
  37139         });
  37140     }
  37141     return result;
  37142 }
  37143 
  37144 fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void {
  37145     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(refs));
  37146 }
  37147 
  37148 fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index {
  37149     const air_datas = sema.air_instructions.items(.data);
  37150     const air_tags = sema.air_instructions.items(.tag);
  37151     switch (air_tags[@intFromEnum(inst_index)]) {
  37152         .br => return air_datas[@intFromEnum(inst_index)].br.block_inst,
  37153         else => return null,
  37154     }
  37155 }
  37156 
  37157 fn isComptimeKnown(
  37158     sema: *Sema,
  37159     inst: Air.Inst.Ref,
  37160 ) !bool {
  37161     return (try sema.resolveValue(inst)) != null;
  37162 }
  37163 
  37164 fn analyzeComptimeAlloc(
  37165     sema: *Sema,
  37166     block: *Block,
  37167     var_type: Type,
  37168     alignment: Alignment,
  37169 ) CompileError!Air.Inst.Ref {
  37170     const mod = sema.mod;
  37171 
  37172     // Needed to make an anon decl with type `var_type` (the `finish()` call below).
  37173     _ = try sema.typeHasOnePossibleValue(var_type);
  37174 
  37175     const ptr_type = try sema.ptrType(.{
  37176         .child = var_type.toIntern(),
  37177         .flags = .{
  37178             .alignment = alignment,
  37179             .address_space = target_util.defaultAddressSpace(mod.getTarget(), .global_constant),
  37180         },
  37181     });
  37182 
  37183     var anon_decl = try block.startAnonDecl(); // TODO: comptime value mutation without Decl
  37184     defer anon_decl.deinit();
  37185 
  37186     const decl_index = try anon_decl.finish(
  37187         var_type,
  37188         // There will be stores before the first load, but they may be to sub-elements or
  37189         // sub-fields. So we need to initialize with undef to allow the mechanism to expand
  37190         // into fields/elements and have those overridden with stored values.
  37191         Value.fromInterned((try mod.intern(.{ .undef = var_type.toIntern() }))),
  37192         alignment,
  37193     );
  37194     const decl = mod.declPtr(decl_index);
  37195     decl.alignment = alignment;
  37196 
  37197     try sema.comptime_mutable_decls.append(decl_index);
  37198     return Air.internedToRef((try mod.intern(.{ .ptr = .{
  37199         .ty = ptr_type.toIntern(),
  37200         .addr = .{ .mut_decl = .{
  37201             .decl = decl_index,
  37202             .runtime_index = block.runtime_index,
  37203         } },
  37204     } })));
  37205 }
  37206 
  37207 /// The places where a user can specify an address space attribute
  37208 pub const AddressSpaceContext = enum {
  37209     /// A function is specified to be placed in a certain address space.
  37210     function,
  37211 
  37212     /// A (global) variable is specified to be placed in a certain address space.
  37213     /// In contrast to .constant, these values (and thus the address space they will be
  37214     /// placed in) are required to be mutable.
  37215     variable,
  37216 
  37217     /// A (global) constant value is specified to be placed in a certain address space.
  37218     /// In contrast to .variable, values placed in this address space are not required to be mutable.
  37219     constant,
  37220 
  37221     /// A pointer is ascripted to point into a certain address space.
  37222     pointer,
  37223 };
  37224 
  37225 pub fn analyzeAddressSpace(
  37226     sema: *Sema,
  37227     block: *Block,
  37228     src: LazySrcLoc,
  37229     zir_ref: Zir.Inst.Ref,
  37230     ctx: AddressSpaceContext,
  37231 ) !std.builtin.AddressSpace {
  37232     const mod = sema.mod;
  37233     const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref, .{
  37234         .needed_comptime_reason = "address space must be comptime-known",
  37235     });
  37236     const address_space = mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val);
  37237     const target = sema.mod.getTarget();
  37238     const arch = target.cpu.arch;
  37239 
  37240     const is_nv = arch == .nvptx or arch == .nvptx64;
  37241     const is_amd = arch == .amdgcn;
  37242     const is_spirv = arch == .spirv32 or arch == .spirv64;
  37243     const is_gpu = is_nv or is_amd or is_spirv;
  37244 
  37245     const supported = switch (address_space) {
  37246         // TODO: on spir-v only when os is opencl.
  37247         .generic => true,
  37248         .gs, .fs, .ss => (arch == .x86 or arch == .x86_64) and ctx == .pointer,
  37249         // TODO: check that .shared and .local are left uninitialized
  37250         .param => is_nv,
  37251         .global, .shared, .local => is_gpu,
  37252         .constant => is_gpu and (ctx == .constant),
  37253         // TODO this should also check how many flash banks the cpu has
  37254         .flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr,
  37255     };
  37256 
  37257     if (!supported) {
  37258         // TODO error messages could be made more elaborate here
  37259         const entity = switch (ctx) {
  37260             .function => "functions",
  37261             .variable => "mutable values",
  37262             .constant => "constant values",
  37263             .pointer => "pointers",
  37264         };
  37265         return sema.fail(
  37266             block,
  37267             src,
  37268             "{s} with address space '{s}' are not supported on {s}",
  37269             .{ entity, @tagName(address_space), arch.genericName() },
  37270         );
  37271     }
  37272 
  37273     return address_space;
  37274 }
  37275 
  37276 /// Asserts the value is a pointer and dereferences it.
  37277 /// Returns `null` if the pointer contents cannot be loaded at comptime.
  37278 fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
  37279     const mod = sema.mod;
  37280     const load_ty = ptr_ty.childType(mod);
  37281     const res = try sema.pointerDerefExtra(block, src, ptr_val, load_ty);
  37282     switch (res) {
  37283         .runtime_load => return null,
  37284         .val => |v| return v,
  37285         .needed_well_defined => |ty| return sema.fail(
  37286             block,
  37287             src,
  37288             "comptime dereference requires '{}' to have a well-defined layout, but it does not.",
  37289             .{ty.fmt(sema.mod)},
  37290         ),
  37291         .out_of_bounds => |ty| return sema.fail(
  37292             block,
  37293             src,
  37294             "dereference of '{}' exceeds bounds of containing decl of type '{}'",
  37295             .{ ptr_ty.fmt(sema.mod), ty.fmt(sema.mod) },
  37296         ),
  37297     }
  37298 }
  37299 
  37300 const DerefResult = union(enum) {
  37301     runtime_load,
  37302     val: Value,
  37303     needed_well_defined: Type,
  37304     out_of_bounds: Type,
  37305 };
  37306 
  37307 fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, load_ty: Type) CompileError!DerefResult {
  37308     const mod = sema.mod;
  37309     const target = mod.getTarget();
  37310     const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) {
  37311         error.RuntimeLoad => return DerefResult{ .runtime_load = {} },
  37312         else => |e| return e,
  37313     };
  37314 
  37315     if (deref.pointee) |tv| {
  37316         const coerce_in_mem_ok =
  37317             (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or
  37318             (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok;
  37319         if (coerce_in_mem_ok) {
  37320             // We have a Value that lines up in virtual memory exactly with what we want to load,
  37321             // and it is in-memory coercible to load_ty. It may be returned without modifications.
  37322             // Move mutable decl values to the InternPool and assert other decls are already in
  37323             // the InternPool.
  37324             const uncoerced_val = if (deref.is_mutable) try tv.val.intern(tv.ty, mod) else tv.val.toIntern();
  37325             const coerced_val = try mod.getCoerced(Value.fromInterned(uncoerced_val), load_ty);
  37326             return .{ .val = coerced_val };
  37327         }
  37328     }
  37329 
  37330     // The type is not in-memory coercible or the direct dereference failed, so it must
  37331     // be bitcast according to the pointer type we are performing the load through.
  37332     if (!load_ty.hasWellDefinedLayout(mod)) {
  37333         return DerefResult{ .needed_well_defined = load_ty };
  37334     }
  37335 
  37336     const load_sz = try sema.typeAbiSize(load_ty);
  37337 
  37338     // Try the smaller bit-cast first, since that's more efficient than using the larger `parent`
  37339     if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(tv.ty))
  37340         return DerefResult{ .val = (try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0)) orelse return .runtime_load };
  37341 
  37342     // If that fails, try to bit-cast from the largest parent value with a well-defined layout
  37343     if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(parent.tv.ty))
  37344         return DerefResult{ .val = (try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset)) orelse return .runtime_load };
  37345 
  37346     if (deref.ty_without_well_defined_layout) |bad_ty| {
  37347         // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
  37348         // is that some type we encountered when de-referencing does not have a well-defined layout.
  37349         return DerefResult{ .needed_well_defined = bad_ty };
  37350     } else {
  37351         // If all encountered types had well-defined layouts, the parent is the root decl and it just
  37352         // wasn't big enough for the load.
  37353         return DerefResult{ .out_of_bounds = deref.parent.?.tv.ty };
  37354     }
  37355 }
  37356 
  37357 /// Used to convert a u64 value to a usize value, emitting a compile error if the number
  37358 /// is too big to fit.
  37359 fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize {
  37360     if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int;
  37361     return std.math.cast(usize, int) orelse return sema.fail(block, src, "expression produces integer value '{d}' which is too big for this compiler implementation to handle", .{int});
  37362 }
  37363 
  37364 /// For pointer-like optionals, it returns the pointer type. For pointers,
  37365 /// the type is returned unmodified.
  37366 /// This can return `error.AnalysisFail` because it sometimes requires resolving whether
  37367 /// a type has zero bits, which can cause a "foo depends on itself" compile error.
  37368 /// This logic must be kept in sync with `Type.isPtrLikeOptional`.
  37369 fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type {
  37370     const mod = sema.mod;
  37371     return switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  37372         .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  37373             .One, .Many, .C => ty,
  37374             .Slice => null,
  37375         },
  37376         .opt_type => |opt_child| switch (mod.intern_pool.indexToKey(opt_child)) {
  37377             .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  37378                 .Slice, .C => null,
  37379                 .Many, .One => {
  37380                     if (ptr_type.flags.is_allowzero) return null;
  37381 
  37382                     // optionals of zero sized types behave like bools, not pointers
  37383                     const payload_ty = Type.fromInterned(opt_child);
  37384                     if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) {
  37385                         return null;
  37386                     }
  37387 
  37388                     return payload_ty;
  37389                 },
  37390             },
  37391             else => null,
  37392         },
  37393         else => null,
  37394     };
  37395 }
  37396 
  37397 /// `generic_poison` will return false.
  37398 /// May return false negatives when structs and unions are having their field types resolved.
  37399 pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
  37400     return ty.comptimeOnlyAdvanced(sema.mod, sema);
  37401 }
  37402 
  37403 pub fn typeHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {
  37404     const mod = sema.mod;
  37405     return ty.hasRuntimeBitsAdvanced(mod, false, .{ .sema = sema }) catch |err| switch (err) {
  37406         error.NeedLazy => unreachable,
  37407         else => |e| return e,
  37408     };
  37409 }
  37410 
  37411 fn typeAbiSize(sema: *Sema, ty: Type) !u64 {
  37412     try sema.resolveTypeLayout(ty);
  37413     return ty.abiSize(sema.mod);
  37414 }
  37415 
  37416 fn typeAbiAlignment(sema: *Sema, ty: Type) CompileError!Alignment {
  37417     return (try ty.abiAlignmentAdvanced(sema.mod, .{ .sema = sema })).scalar;
  37418 }
  37419 
  37420 /// Not valid to call for packed unions.
  37421 /// Keep implementation in sync with `Module.unionFieldNormalAlignment`.
  37422 fn unionFieldAlignment(sema: *Sema, u: InternPool.UnionType, field_index: u32) !Alignment {
  37423     const mod = sema.mod;
  37424     const ip = &mod.intern_pool;
  37425     const field_align = u.fieldAlign(ip, field_index);
  37426     if (field_align != .none) return field_align;
  37427     const field_ty = Type.fromInterned(u.field_types.get(ip)[field_index]);
  37428     if (field_ty.isNoReturn(sema.mod)) return .none;
  37429     return sema.typeAbiAlignment(field_ty);
  37430 }
  37431 
  37432 /// Keep implementation in sync with `Module.structFieldAlignment`.
  37433 fn structFieldAlignment(
  37434     sema: *Sema,
  37435     explicit_alignment: InternPool.Alignment,
  37436     field_ty: Type,
  37437     layout: std.builtin.Type.ContainerLayout,
  37438 ) !Alignment {
  37439     if (explicit_alignment != .none)
  37440         return explicit_alignment;
  37441     const mod = sema.mod;
  37442     switch (layout) {
  37443         .Packed => return .none,
  37444         .Auto => if (mod.getTarget().ofmt != .c) return sema.typeAbiAlignment(field_ty),
  37445         .Extern => {},
  37446     }
  37447     // extern
  37448     const ty_abi_align = try sema.typeAbiAlignment(field_ty);
  37449     if (field_ty.isAbiInt(mod) and field_ty.intInfo(mod).bits >= 128) {
  37450         return ty_abi_align.maxStrict(.@"16");
  37451     }
  37452     return ty_abi_align;
  37453 }
  37454 
  37455 pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {
  37456     return ty.fnHasRuntimeBitsAdvanced(sema.mod, sema);
  37457 }
  37458 
  37459 fn unionFieldIndex(
  37460     sema: *Sema,
  37461     block: *Block,
  37462     union_ty: Type,
  37463     field_name: InternPool.NullTerminatedString,
  37464     field_src: LazySrcLoc,
  37465 ) !u32 {
  37466     const mod = sema.mod;
  37467     const ip = &mod.intern_pool;
  37468     try sema.resolveTypeFields(union_ty);
  37469     const union_obj = mod.typeToUnion(union_ty).?;
  37470     const field_index = union_obj.nameIndex(ip, field_name) orelse
  37471         return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
  37472     return @intCast(field_index);
  37473 }
  37474 
  37475 fn structFieldIndex(
  37476     sema: *Sema,
  37477     block: *Block,
  37478     struct_ty: Type,
  37479     field_name: InternPool.NullTerminatedString,
  37480     field_src: LazySrcLoc,
  37481 ) !u32 {
  37482     const mod = sema.mod;
  37483     const ip = &mod.intern_pool;
  37484     try sema.resolveTypeFields(struct_ty);
  37485     if (struct_ty.isAnonStruct(mod)) {
  37486         return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src);
  37487     } else {
  37488         const struct_type = mod.typeToStruct(struct_ty).?;
  37489         return struct_type.nameIndex(ip, field_name) orelse
  37490             return sema.failWithBadStructFieldAccess(block, struct_type, field_src, field_name);
  37491     }
  37492 }
  37493 
  37494 fn anonStructFieldIndex(
  37495     sema: *Sema,
  37496     block: *Block,
  37497     struct_ty: Type,
  37498     field_name: InternPool.NullTerminatedString,
  37499     field_src: LazySrcLoc,
  37500 ) !u32 {
  37501     const mod = sema.mod;
  37502     const ip = &mod.intern_pool;
  37503     switch (ip.indexToKey(struct_ty.toIntern())) {
  37504         .anon_struct_type => |anon_struct_type| for (anon_struct_type.names.get(ip), 0..) |name, i| {
  37505             if (name == field_name) return @intCast(i);
  37506         },
  37507         .struct_type => |struct_type| if (struct_type.nameIndex(ip, field_name)) |i| return i,
  37508         else => unreachable,
  37509     }
  37510     return sema.fail(block, field_src, "no field named '{}' in anonymous struct '{}'", .{
  37511         field_name.fmt(ip), struct_ty.fmt(sema.mod),
  37512     });
  37513 }
  37514 
  37515 fn queueFullTypeResolution(sema: *Sema, ty: Type) !void {
  37516     try sema.types_to_resolve.put(sema.gpa, ty.toIntern(), {});
  37517 }
  37518 
  37519 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
  37520 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
  37521 fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value {
  37522     var overflow: usize = undefined;
  37523     return sema.intAddInner(lhs, rhs, ty, &overflow) catch |err| switch (err) {
  37524         error.Overflow => {
  37525             const is_vec = ty.isVector(sema.mod);
  37526             overflow_idx.* = if (is_vec) overflow else 0;
  37527             const safe_ty = if (is_vec) try sema.mod.vectorType(.{
  37528                 .len = ty.vectorLen(sema.mod),
  37529                 .child = .comptime_int_type,
  37530             }) else Type.comptime_int;
  37531             return sema.intAddInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) {
  37532                 error.Overflow => unreachable,
  37533                 else => |e| return e,
  37534             };
  37535         },
  37536         else => |e| return e,
  37537     };
  37538 }
  37539 
  37540 fn intAddInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value {
  37541     const mod = sema.mod;
  37542     if (ty.zigTypeTag(mod) == .Vector) {
  37543         const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
  37544         const scalar_ty = ty.scalarType(mod);
  37545         for (result_data, 0..) |*scalar, i| {
  37546             const lhs_elem = try lhs.elemValue(mod, i);
  37547             const rhs_elem = try rhs.elemValue(mod, i);
  37548             const val = sema.intAddScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) {
  37549                 error.Overflow => {
  37550                     overflow_idx.* = i;
  37551                     return error.Overflow;
  37552                 },
  37553                 else => |e| return e,
  37554             };
  37555             scalar.* = try val.intern(scalar_ty, mod);
  37556         }
  37557         return Value.fromInterned((try mod.intern(.{ .aggregate = .{
  37558             .ty = ty.toIntern(),
  37559             .storage = .{ .elems = result_data },
  37560         } })));
  37561     }
  37562     return sema.intAddScalar(lhs, rhs, ty);
  37563 }
  37564 
  37565 fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
  37566     const mod = sema.mod;
  37567     if (scalar_ty.toIntern() != .comptime_int_type) {
  37568         const res = try sema.intAddWithOverflowScalar(lhs, rhs, scalar_ty);
  37569         if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
  37570         return res.wrapped_result;
  37571     }
  37572     // TODO is this a performance issue? maybe we should try the operation without
  37573     // resorting to BigInt first.
  37574     var lhs_space: Value.BigIntSpace = undefined;
  37575     var rhs_space: Value.BigIntSpace = undefined;
  37576     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  37577     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  37578     const limbs = try sema.arena.alloc(
  37579         std.math.big.Limb,
  37580         @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
  37581     );
  37582     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  37583     result_bigint.add(lhs_bigint, rhs_bigint);
  37584     return mod.intValue_big(scalar_ty, result_bigint.toConst());
  37585 }
  37586 
  37587 /// Supports both floats and ints; handles undefined.
  37588 fn numberAddWrapScalar(
  37589     sema: *Sema,
  37590     lhs: Value,
  37591     rhs: Value,
  37592     ty: Type,
  37593 ) !Value {
  37594     const mod = sema.mod;
  37595     if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
  37596 
  37597     if (ty.zigTypeTag(mod) == .ComptimeInt) {
  37598         return sema.intAdd(lhs, rhs, ty, undefined);
  37599     }
  37600 
  37601     if (ty.isAnyFloat()) {
  37602         return Value.floatAdd(lhs, rhs, ty, sema.arena, mod);
  37603     }
  37604 
  37605     const overflow_result = try sema.intAddWithOverflow(lhs, rhs, ty);
  37606     return overflow_result.wrapped_result;
  37607 }
  37608 
  37609 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
  37610 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
  37611 fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value {
  37612     var overflow: usize = undefined;
  37613     return sema.intSubInner(lhs, rhs, ty, &overflow) catch |err| switch (err) {
  37614         error.Overflow => {
  37615             const is_vec = ty.isVector(sema.mod);
  37616             overflow_idx.* = if (is_vec) overflow else 0;
  37617             const safe_ty = if (is_vec) try sema.mod.vectorType(.{
  37618                 .len = ty.vectorLen(sema.mod),
  37619                 .child = .comptime_int_type,
  37620             }) else Type.comptime_int;
  37621             return sema.intSubInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) {
  37622                 error.Overflow => unreachable,
  37623                 else => |e| return e,
  37624             };
  37625         },
  37626         else => |e| return e,
  37627     };
  37628 }
  37629 
  37630 fn intSubInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value {
  37631     const mod = sema.mod;
  37632     if (ty.zigTypeTag(mod) == .Vector) {
  37633         const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
  37634         const scalar_ty = ty.scalarType(mod);
  37635         for (result_data, 0..) |*scalar, i| {
  37636             const lhs_elem = try lhs.elemValue(sema.mod, i);
  37637             const rhs_elem = try rhs.elemValue(sema.mod, i);
  37638             const val = sema.intSubScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) {
  37639                 error.Overflow => {
  37640                     overflow_idx.* = i;
  37641                     return error.Overflow;
  37642                 },
  37643                 else => |e| return e,
  37644             };
  37645             scalar.* = try val.intern(scalar_ty, mod);
  37646         }
  37647         return Value.fromInterned((try mod.intern(.{ .aggregate = .{
  37648             .ty = ty.toIntern(),
  37649             .storage = .{ .elems = result_data },
  37650         } })));
  37651     }
  37652     return sema.intSubScalar(lhs, rhs, ty);
  37653 }
  37654 
  37655 fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
  37656     const mod = sema.mod;
  37657     if (scalar_ty.toIntern() != .comptime_int_type) {
  37658         const res = try sema.intSubWithOverflowScalar(lhs, rhs, scalar_ty);
  37659         if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
  37660         return res.wrapped_result;
  37661     }
  37662     // TODO is this a performance issue? maybe we should try the operation without
  37663     // resorting to BigInt first.
  37664     var lhs_space: Value.BigIntSpace = undefined;
  37665     var rhs_space: Value.BigIntSpace = undefined;
  37666     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  37667     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  37668     const limbs = try sema.arena.alloc(
  37669         std.math.big.Limb,
  37670         @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
  37671     );
  37672     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  37673     result_bigint.sub(lhs_bigint, rhs_bigint);
  37674     return mod.intValue_big(scalar_ty, result_bigint.toConst());
  37675 }
  37676 
  37677 /// Supports both floats and ints; handles undefined.
  37678 fn numberSubWrapScalar(
  37679     sema: *Sema,
  37680     lhs: Value,
  37681     rhs: Value,
  37682     ty: Type,
  37683 ) !Value {
  37684     const mod = sema.mod;
  37685     if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
  37686 
  37687     if (ty.zigTypeTag(mod) == .ComptimeInt) {
  37688         return sema.intSub(lhs, rhs, ty, undefined);
  37689     }
  37690 
  37691     if (ty.isAnyFloat()) {
  37692         return Value.floatSub(lhs, rhs, ty, sema.arena, mod);
  37693     }
  37694 
  37695     const overflow_result = try sema.intSubWithOverflow(lhs, rhs, ty);
  37696     return overflow_result.wrapped_result;
  37697 }
  37698 
  37699 fn intSubWithOverflow(
  37700     sema: *Sema,
  37701     lhs: Value,
  37702     rhs: Value,
  37703     ty: Type,
  37704 ) !Value.OverflowArithmeticResult {
  37705     const mod = sema.mod;
  37706     if (ty.zigTypeTag(mod) == .Vector) {
  37707         const vec_len = ty.vectorLen(mod);
  37708         const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len);
  37709         const result_data = try sema.arena.alloc(InternPool.Index, vec_len);
  37710         const scalar_ty = ty.scalarType(mod);
  37711         for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
  37712             const lhs_elem = try lhs.elemValue(sema.mod, i);
  37713             const rhs_elem = try rhs.elemValue(sema.mod, i);
  37714             const of_math_result = try sema.intSubWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty);
  37715             of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
  37716             scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
  37717         }
  37718         return Value.OverflowArithmeticResult{
  37719             .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
  37720                 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
  37721                 .storage = .{ .elems = overflowed_data },
  37722             } }))),
  37723             .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
  37724                 .ty = ty.toIntern(),
  37725                 .storage = .{ .elems = result_data },
  37726             } }))),
  37727         };
  37728     }
  37729     return sema.intSubWithOverflowScalar(lhs, rhs, ty);
  37730 }
  37731 
  37732 fn intSubWithOverflowScalar(
  37733     sema: *Sema,
  37734     lhs: Value,
  37735     rhs: Value,
  37736     ty: Type,
  37737 ) !Value.OverflowArithmeticResult {
  37738     const mod = sema.mod;
  37739     const info = ty.intInfo(mod);
  37740 
  37741     var lhs_space: Value.BigIntSpace = undefined;
  37742     var rhs_space: Value.BigIntSpace = undefined;
  37743     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  37744     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  37745     const limbs = try sema.arena.alloc(
  37746         std.math.big.Limb,
  37747         std.math.big.int.calcTwosCompLimbCount(info.bits),
  37748     );
  37749     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  37750     const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
  37751     const wrapped_result = try mod.intValue_big(ty, result_bigint.toConst());
  37752     return Value.OverflowArithmeticResult{
  37753         .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
  37754         .wrapped_result = wrapped_result,
  37755     };
  37756 }
  37757 
  37758 const IntFromFloatMode = enum { exact, truncate };
  37759 
  37760 fn intFromFloat(
  37761     sema: *Sema,
  37762     block: *Block,
  37763     src: LazySrcLoc,
  37764     val: Value,
  37765     float_ty: Type,
  37766     int_ty: Type,
  37767     mode: IntFromFloatMode,
  37768 ) CompileError!Value {
  37769     const mod = sema.mod;
  37770     if (float_ty.zigTypeTag(mod) == .Vector) {
  37771         const elem_ty = float_ty.scalarType(mod);
  37772         const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(mod));
  37773         const scalar_ty = int_ty.scalarType(mod);
  37774         for (result_data, 0..) |*scalar, i| {
  37775             const elem_val = try val.elemValue(sema.mod, i);
  37776             scalar.* = try (try sema.intFromFloatScalar(block, src, elem_val, elem_ty, int_ty.scalarType(mod), mode)).intern(scalar_ty, mod);
  37777         }
  37778         return Value.fromInterned((try mod.intern(.{ .aggregate = .{
  37779             .ty = int_ty.toIntern(),
  37780             .storage = .{ .elems = result_data },
  37781         } })));
  37782     }
  37783     return sema.intFromFloatScalar(block, src, val, float_ty, int_ty, mode);
  37784 }
  37785 
  37786 // float is expected to be finite and non-NaN
  37787 fn float128IntPartToBigInt(
  37788     arena: Allocator,
  37789     float: f128,
  37790 ) !std.math.big.int.Managed {
  37791     const is_negative = std.math.signbit(float);
  37792     const floored = @floor(@abs(float));
  37793 
  37794     var rational = try std.math.big.Rational.init(arena);
  37795     defer rational.q.deinit();
  37796     rational.setFloat(f128, floored) catch |err| switch (err) {
  37797         error.NonFiniteFloat => unreachable,
  37798         error.OutOfMemory => return error.OutOfMemory,
  37799     };
  37800 
  37801     // The float is reduced in rational.setFloat, so we assert that denominator is equal to one
  37802     const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
  37803     assert(rational.q.toConst().eqlAbs(big_one));
  37804 
  37805     if (is_negative) {
  37806         rational.negate();
  37807     }
  37808     return rational.p;
  37809 }
  37810 
  37811 fn intFromFloatScalar(
  37812     sema: *Sema,
  37813     block: *Block,
  37814     src: LazySrcLoc,
  37815     val: Value,
  37816     float_ty: Type,
  37817     int_ty: Type,
  37818     mode: IntFromFloatMode,
  37819 ) CompileError!Value {
  37820     const mod = sema.mod;
  37821 
  37822     if (val.isUndef(mod)) return sema.failWithUseOfUndef(block, src);
  37823 
  37824     if (mode == .exact and val.floatHasFraction(mod)) return sema.fail(
  37825         block,
  37826         src,
  37827         "fractional component prevents float value '{}' from coercion to type '{}'",
  37828         .{ val.fmtValue(float_ty, mod), int_ty.fmt(mod) },
  37829     );
  37830 
  37831     const float = val.toFloat(f128, mod);
  37832     if (std.math.isNan(float)) {
  37833         return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{
  37834             int_ty.fmt(sema.mod),
  37835         });
  37836     }
  37837     if (std.math.isInf(float)) {
  37838         return sema.fail(block, src, "float value Inf cannot be stored in integer type '{}'", .{
  37839             int_ty.fmt(sema.mod),
  37840         });
  37841     }
  37842 
  37843     var big_int = try float128IntPartToBigInt(sema.arena, float);
  37844     defer big_int.deinit();
  37845 
  37846     const cti_result = try mod.intValue_big(Type.comptime_int, big_int.toConst());
  37847 
  37848     if (!(try sema.intFitsInType(cti_result, int_ty, null))) {
  37849         return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{
  37850             val.fmtValue(float_ty, sema.mod), int_ty.fmt(sema.mod),
  37851         });
  37852     }
  37853     return mod.getCoerced(cti_result, int_ty);
  37854 }
  37855 
  37856 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
  37857 /// Vectors are also accepted. Vector results are reduced with AND.
  37858 ///
  37859 /// If provided, `vector_index` reports the first element that failed the range check.
  37860 fn intFitsInType(
  37861     sema: *Sema,
  37862     val: Value,
  37863     ty: Type,
  37864     vector_index: ?*usize,
  37865 ) CompileError!bool {
  37866     const mod = sema.mod;
  37867     if (ty.toIntern() == .comptime_int_type) return true;
  37868     const info = ty.intInfo(mod);
  37869     switch (val.toIntern()) {
  37870         .zero_usize, .zero_u8 => return true,
  37871         else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
  37872             .undef => return true,
  37873             .variable, .extern_func, .func, .ptr => {
  37874                 const target = mod.getTarget();
  37875                 const ptr_bits = target.ptrBitWidth();
  37876                 return switch (info.signedness) {
  37877                     .signed => info.bits > ptr_bits,
  37878                     .unsigned => info.bits >= ptr_bits,
  37879                 };
  37880             },
  37881             .int => |int| switch (int.storage) {
  37882                 .u64, .i64, .big_int => {
  37883                     var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
  37884                     const big_int = int.storage.toBigInt(&buffer);
  37885                     return big_int.fitsInTwosComp(info.signedness, info.bits);
  37886                 },
  37887                 .lazy_align => |lazy_ty| {
  37888                     const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed);
  37889                     // If it is u16 or bigger we know the alignment fits without resolving it.
  37890                     if (info.bits >= max_needed_bits) return true;
  37891                     const x = try sema.typeAbiAlignment(Type.fromInterned(lazy_ty));
  37892                     if (x == .none) return true;
  37893                     const actual_needed_bits = @as(usize, x.toLog2Units()) + 1 + @intFromBool(info.signedness == .signed);
  37894                     return info.bits >= actual_needed_bits;
  37895                 },
  37896                 .lazy_size => |lazy_ty| {
  37897                     const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed);
  37898                     // If it is u64 or bigger we know the size fits without resolving it.
  37899                     if (info.bits >= max_needed_bits) return true;
  37900                     const x = try sema.typeAbiSize(Type.fromInterned(lazy_ty));
  37901                     if (x == 0) return true;
  37902                     const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed);
  37903                     return info.bits >= actual_needed_bits;
  37904                 },
  37905             },
  37906             .aggregate => |aggregate| {
  37907                 assert(ty.zigTypeTag(mod) == .Vector);
  37908                 return switch (aggregate.storage) {
  37909                     .bytes => |bytes| for (bytes, 0..) |byte, i| {
  37910                         if (byte == 0) continue;
  37911                         const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed);
  37912                         if (info.bits >= actual_needed_bits) continue;
  37913                         if (vector_index) |vi| vi.* = i;
  37914                         break false;
  37915                     } else true,
  37916                     .elems, .repeated_elem => for (switch (aggregate.storage) {
  37917                         .bytes => unreachable,
  37918                         .elems => |elems| elems,
  37919                         .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem),
  37920                     }, 0..) |elem, i| {
  37921                         if (try sema.intFitsInType(Value.fromInterned(elem), ty.scalarType(mod), null)) continue;
  37922                         if (vector_index) |vi| vi.* = i;
  37923                         break false;
  37924                     } else true,
  37925                 };
  37926             },
  37927             else => unreachable,
  37928         },
  37929     }
  37930 }
  37931 
  37932 fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool {
  37933     const mod = sema.mod;
  37934     if (!(try int_val.compareAllWithZeroAdvanced(.gte, sema))) return false;
  37935     const end_val = try mod.intValue(tag_ty, end);
  37936     if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false;
  37937     return true;
  37938 }
  37939 
  37940 /// Asserts the type is an enum.
  37941 fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
  37942     const mod = sema.mod;
  37943     const enum_type = mod.intern_pool.indexToKey(ty.toIntern()).enum_type;
  37944     assert(enum_type.tag_mode != .nonexhaustive);
  37945     // The `tagValueIndex` function call below relies on the type being the integer tag type.
  37946     // `getCoerced` assumes the value will fit the new type.
  37947     if (!(try sema.intFitsInType(int, Type.fromInterned(enum_type.tag_ty), null))) return false;
  37948     const int_coerced = try mod.getCoerced(int, Type.fromInterned(enum_type.tag_ty));
  37949 
  37950     return enum_type.tagValueIndex(&mod.intern_pool, int_coerced.toIntern()) != null;
  37951 }
  37952 
  37953 fn intAddWithOverflow(
  37954     sema: *Sema,
  37955     lhs: Value,
  37956     rhs: Value,
  37957     ty: Type,
  37958 ) !Value.OverflowArithmeticResult {
  37959     const mod = sema.mod;
  37960     if (ty.zigTypeTag(mod) == .Vector) {
  37961         const vec_len = ty.vectorLen(mod);
  37962         const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len);
  37963         const result_data = try sema.arena.alloc(InternPool.Index, vec_len);
  37964         const scalar_ty = ty.scalarType(mod);
  37965         for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
  37966             const lhs_elem = try lhs.elemValue(sema.mod, i);
  37967             const rhs_elem = try rhs.elemValue(sema.mod, i);
  37968             const of_math_result = try sema.intAddWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty);
  37969             of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
  37970             scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
  37971         }
  37972         return Value.OverflowArithmeticResult{
  37973             .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
  37974                 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
  37975                 .storage = .{ .elems = overflowed_data },
  37976             } }))),
  37977             .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
  37978                 .ty = ty.toIntern(),
  37979                 .storage = .{ .elems = result_data },
  37980             } }))),
  37981         };
  37982     }
  37983     return sema.intAddWithOverflowScalar(lhs, rhs, ty);
  37984 }
  37985 
  37986 fn intAddWithOverflowScalar(
  37987     sema: *Sema,
  37988     lhs: Value,
  37989     rhs: Value,
  37990     ty: Type,
  37991 ) !Value.OverflowArithmeticResult {
  37992     const mod = sema.mod;
  37993     const info = ty.intInfo(mod);
  37994 
  37995     var lhs_space: Value.BigIntSpace = undefined;
  37996     var rhs_space: Value.BigIntSpace = undefined;
  37997     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  37998     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  37999     const limbs = try sema.arena.alloc(
  38000         std.math.big.Limb,
  38001         std.math.big.int.calcTwosCompLimbCount(info.bits),
  38002     );
  38003     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  38004     const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
  38005     const result = try mod.intValue_big(ty, result_bigint.toConst());
  38006     return Value.OverflowArithmeticResult{
  38007         .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
  38008         .wrapped_result = result,
  38009     };
  38010 }
  38011 
  38012 /// Asserts the values are comparable. Both operands have type `ty`.
  38013 /// For vectors, returns true if the comparison is true for ALL elements.
  38014 ///
  38015 /// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)`
  38016 fn compareAll(
  38017     sema: *Sema,
  38018     lhs: Value,
  38019     op: std.math.CompareOperator,
  38020     rhs: Value,
  38021     ty: Type,
  38022 ) CompileError!bool {
  38023     const mod = sema.mod;
  38024     if (ty.zigTypeTag(mod) == .Vector) {
  38025         var i: usize = 0;
  38026         while (i < ty.vectorLen(mod)) : (i += 1) {
  38027             const lhs_elem = try lhs.elemValue(sema.mod, i);
  38028             const rhs_elem = try rhs.elemValue(sema.mod, i);
  38029             if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)))) {
  38030                 return false;
  38031             }
  38032         }
  38033         return true;
  38034     }
  38035     return sema.compareScalar(lhs, op, rhs, ty);
  38036 }
  38037 
  38038 /// Asserts the values are comparable. Both operands have type `ty`.
  38039 fn compareScalar(
  38040     sema: *Sema,
  38041     lhs: Value,
  38042     op: std.math.CompareOperator,
  38043     rhs: Value,
  38044     ty: Type,
  38045 ) CompileError!bool {
  38046     const mod = sema.mod;
  38047     const coerced_lhs = try mod.getCoerced(lhs, ty);
  38048     const coerced_rhs = try mod.getCoerced(rhs, ty);
  38049     switch (op) {
  38050         .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty),
  38051         .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)),
  38052         else => return Value.compareHeteroAdvanced(coerced_lhs, op, coerced_rhs, mod, sema),
  38053     }
  38054 }
  38055 
  38056 fn valuesEqual(
  38057     sema: *Sema,
  38058     lhs: Value,
  38059     rhs: Value,
  38060     ty: Type,
  38061 ) CompileError!bool {
  38062     return lhs.eql(rhs, ty, sema.mod);
  38063 }
  38064 
  38065 /// Asserts the values are comparable vectors of type `ty`.
  38066 fn compareVector(
  38067     sema: *Sema,
  38068     lhs: Value,
  38069     op: std.math.CompareOperator,
  38070     rhs: Value,
  38071     ty: Type,
  38072 ) !Value {
  38073     const mod = sema.mod;
  38074     assert(ty.zigTypeTag(mod) == .Vector);
  38075     const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
  38076     for (result_data, 0..) |*scalar, i| {
  38077         const lhs_elem = try lhs.elemValue(sema.mod, i);
  38078         const rhs_elem = try rhs.elemValue(sema.mod, i);
  38079         const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod));
  38080         scalar.* = try Value.makeBool(res_bool).intern(Type.bool, mod);
  38081     }
  38082     return Value.fromInterned((try mod.intern(.{ .aggregate = .{
  38083         .ty = (try mod.vectorType(.{ .len = ty.vectorLen(mod), .child = .bool_type })).toIntern(),
  38084         .storage = .{ .elems = result_data },
  38085     } })));
  38086 }
  38087 
  38088 /// Returns the type of a pointer to an element.
  38089 /// Asserts that the type is a pointer, and that the element type is indexable.
  38090 /// For *[N]T, return *T
  38091 /// For [*]T, returns *T
  38092 /// For []T, returns *T
  38093 /// Handles const-ness and address spaces in particular.
  38094 /// This code is duplicated in `analyzePtrArithmetic`.
  38095 fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type {
  38096     const mod = sema.mod;
  38097     const ptr_info = ptr_ty.ptrInfo(mod);
  38098     const elem_ty = ptr_ty.elemType2(mod);
  38099     const is_allowzero = ptr_info.flags.is_allowzero and (offset orelse 0) == 0;
  38100     const parent_ty = ptr_ty.childType(mod);
  38101 
  38102     const VI = InternPool.Key.PtrType.VectorIndex;
  38103 
  38104     const vector_info: struct {
  38105         host_size: u16 = 0,
  38106         alignment: Alignment = .none,
  38107         vector_index: VI = .none,
  38108     } = if (parent_ty.isVector(mod) and ptr_info.flags.size == .One) blk: {
  38109         const elem_bits = elem_ty.bitSize(mod);
  38110         if (elem_bits == 0) break :blk .{};
  38111         const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits);
  38112         if (!is_packed) break :blk .{};
  38113 
  38114         break :blk .{
  38115             .host_size = @intCast(parent_ty.arrayLen(mod)),
  38116             .alignment = parent_ty.abiAlignment(mod),
  38117             .vector_index = if (offset) |some| @enumFromInt(some) else .runtime,
  38118         };
  38119     } else .{};
  38120 
  38121     const alignment: Alignment = a: {
  38122         // Calculate the new pointer alignment.
  38123         if (ptr_info.flags.alignment == .none) {
  38124             // In case of an ABI-aligned pointer, any pointer arithmetic
  38125             // maintains the same ABI-alignedness.
  38126             break :a vector_info.alignment;
  38127         }
  38128         // If the addend is not a comptime-known value we can still count on
  38129         // it being a multiple of the type size.
  38130         const elem_size = try sema.typeAbiSize(elem_ty);
  38131         const addend = if (offset) |off| elem_size * off else elem_size;
  38132 
  38133         // The resulting pointer is aligned to the lcd between the offset (an
  38134         // arbitrary number) and the alignment factor (always a power of two,
  38135         // non zero).
  38136         const new_align: Alignment = @enumFromInt(@min(
  38137             @ctz(addend),
  38138             ptr_info.flags.alignment.toLog2Units(),
  38139         ));
  38140         assert(new_align != .none);
  38141         break :a new_align;
  38142     };
  38143     return sema.ptrType(.{
  38144         .child = elem_ty.toIntern(),
  38145         .flags = .{
  38146             .alignment = alignment,
  38147             .is_const = ptr_info.flags.is_const,
  38148             .is_volatile = ptr_info.flags.is_volatile,
  38149             .is_allowzero = is_allowzero,
  38150             .address_space = ptr_info.flags.address_space,
  38151             .vector_index = vector_info.vector_index,
  38152         },
  38153         .packed_offset = .{
  38154             .host_size = vector_info.host_size,
  38155             .bit_offset = 0,
  38156         },
  38157     });
  38158 }
  38159 
  38160 /// Merge lhs with rhs.
  38161 /// Asserts that lhs and rhs are both error sets and are resolved.
  38162 fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type {
  38163     const mod = sema.mod;
  38164     const arena = sema.arena;
  38165     const lhs_names = lhs.errorSetNames(mod);
  38166     const rhs_names = rhs.errorSetNames(mod);
  38167     var names: InferredErrorSet.NameMap = .{};
  38168     try names.ensureUnusedCapacity(arena, lhs_names.len);
  38169 
  38170     for (lhs_names) |name| {
  38171         names.putAssumeCapacityNoClobber(name, {});
  38172     }
  38173     for (rhs_names) |name| {
  38174         try names.put(arena, name, {});
  38175     }
  38176 
  38177     return mod.errorSetFromUnsortedNames(names.keys());
  38178 }
  38179 
  38180 /// Avoids crashing the compiler when asking if inferred allocations are noreturn.
  38181 fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool {
  38182     if (ref == .unreachable_value) return true;
  38183     if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) {
  38184         .inferred_alloc, .inferred_alloc_comptime => return false,
  38185         else => {},
  38186     };
  38187     return sema.typeOf(ref).isNoReturn(sema.mod);
  38188 }
  38189 
  38190 /// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type.
  38191 fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool {
  38192     if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) {
  38193         .inferred_alloc, .inferred_alloc_comptime => return false,
  38194         else => {},
  38195     };
  38196     return sema.typeOf(ref).zigTypeTag(sema.mod) == tag;
  38197 }
  38198 
  38199 fn ptrType(sema: *Sema, info: InternPool.Key.PtrType) CompileError!Type {
  38200     if (info.flags.alignment != .none) {
  38201         _ = try sema.typeAbiAlignment(Type.fromInterned(info.child));
  38202     }
  38203     return sema.mod.ptrType(info);
  38204 }