zig

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

blob 9f2c577a (1566975B) - 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: Decl.Index,
     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 owner_func: ?*Module.Fn,
     27 owner_func_index: Module.Fn.OptionalIndex,
     28 /// The function this ZIR code is the body of, according to the source code.
     29 /// This starts out the same as `owner_func` and then diverges in the case of
     30 /// an inline or comptime function call.
     31 func: ?*Module.Fn,
     32 func_index: Module.Fn.OptionalIndex,
     33 /// Used to restore the error return trace when returning a non-error from a function.
     34 error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none,
     35 /// When semantic analysis needs to know the return type of the function whose body
     36 /// is being analyzed, this `Type` should be used instead of going through `func`.
     37 /// This will correctly handle the case of a comptime/inline function call of a
     38 /// generic function which uses a type expression for the return type.
     39 /// The type will be `void` in the case that `func` is `null`.
     40 fn_ret_ty: Type,
     41 branch_quota: u32 = default_branch_quota,
     42 branch_count: u32 = 0,
     43 /// Populated when returning `error.ComptimeBreak`. Used to communicate the
     44 /// break instruction up the stack to find the corresponding Block.
     45 comptime_break_inst: Zir.Inst.Index = undefined,
     46 /// This field is updated when a new source location becomes active, so that
     47 /// instructions which do not have explicitly mapped source locations still have
     48 /// access to the source location set by the previous instruction which did
     49 /// contain a mapped source location.
     50 src: LazySrcLoc = .{ .token_offset = 0 },
     51 decl_val_table: std.AutoHashMapUnmanaged(Decl.Index, Air.Inst.Ref) = .{},
     52 /// When doing a generic function instantiation, this array collects a
     53 /// `Value` object for each parameter that is comptime-known and thus elided
     54 /// from the generated function. This memory is allocated by a parent `Sema` and
     55 /// owned by the values arena of the Sema owner_decl.
     56 comptime_args: []TypedValue = &.{},
     57 /// Marks the function instruction that `comptime_args` applies to so that we
     58 /// don't accidentally apply it to a function prototype which is used in the
     59 /// type expression of a generic function parameter.
     60 comptime_args_fn_inst: Zir.Inst.Index = 0,
     61 /// When `comptime_args` is provided, this field is also provided. It was used as
     62 /// the key in the `monomorphed_funcs` set. The `func` instruction is supposed
     63 /// to use this instead of allocating a fresh one. This avoids an unnecessary
     64 /// extra hash table lookup in the `monomorphed_funcs` set.
     65 /// Sema will set this to null when it takes ownership.
     66 preallocated_new_func: Module.Fn.OptionalIndex = .none,
     67 /// The key is types that must be fully resolved prior to machine code
     68 /// generation pass. Types are added to this set when resolving them
     69 /// immediately could cause a dependency loop, but they do need to be resolved
     70 /// before machine code generation passes process the AIR.
     71 /// It would work fine if this were an array list instead of an array hash map.
     72 /// I chose array hash map with the intention to save time by omitting
     73 /// duplicates.
     74 types_to_resolve: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},
     75 /// These are lazily created runtime blocks from block_inline instructions.
     76 /// They are created when an break_inline passes through a runtime condition, because
     77 /// Sema must convert comptime control flow to runtime control flow, which means
     78 /// breaking from a block.
     79 post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{},
     80 /// Populated with the last compile error created.
     81 err: ?*Module.ErrorMsg = null,
     82 /// True when analyzing a generic instantiation. Used to suppress some errors.
     83 is_generic_instantiation: bool = false,
     84 /// Set to true when analyzing a func type instruction so that nested generic
     85 /// function types will emit generic poison instead of a partial type.
     86 no_partial_func_ty: bool = false,
     87 
     88 /// The temporary arena is used for the memory of the `InferredAlloc` values
     89 /// here so the values can be dropped without any cleanup.
     90 unresolved_inferred_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, InferredAlloc) = .{},
     91 
     92 /// Indices of comptime-mutable decls created by this Sema. These decls' values
     93 /// should be interned after analysis completes, as they may refer to memory in
     94 /// the Sema arena.
     95 /// TODO: this is a workaround for memory bugs triggered by the removal of
     96 /// Decl.value_arena. A better solution needs to be found. Probably this will
     97 /// involve transitioning comptime-mutable memory away from using Decls at all.
     98 comptime_mutable_decls: *std.ArrayList(Decl.Index),
     99 
    100 const std = @import("std");
    101 const math = std.math;
    102 const mem = std.mem;
    103 const Allocator = mem.Allocator;
    104 const assert = std.debug.assert;
    105 const log = std.log.scoped(.sema);
    106 
    107 const Sema = @This();
    108 const Value = @import("value.zig").Value;
    109 const Type = @import("type.zig").Type;
    110 const TypedValue = @import("TypedValue.zig");
    111 const Air = @import("Air.zig");
    112 const Zir = @import("Zir.zig");
    113 const Module = @import("Module.zig");
    114 const trace = @import("tracy.zig").trace;
    115 const Namespace = Module.Namespace;
    116 const CompileError = Module.CompileError;
    117 const SemaError = Module.SemaError;
    118 const Decl = Module.Decl;
    119 const CaptureScope = Module.CaptureScope;
    120 const WipCaptureScope = Module.WipCaptureScope;
    121 const LazySrcLoc = Module.LazySrcLoc;
    122 const RangeSet = @import("RangeSet.zig");
    123 const target_util = @import("target.zig");
    124 const Package = @import("Package.zig");
    125 const crash_report = @import("crash_report.zig");
    126 const build_options = @import("build_options");
    127 const Compilation = @import("Compilation.zig");
    128 const InternPool = @import("InternPool.zig");
    129 const Alignment = InternPool.Alignment;
    130 
    131 pub const default_branch_quota = 1000;
    132 pub const default_reference_trace_len = 2;
    133 
    134 /// Stores the mapping from `Zir.Inst.Index -> Air.Inst.Ref`, which is used by sema to resolve
    135 /// instructions during analysis.
    136 /// Instead of a hash table approach, InstMap is simply a slice that is indexed into using the
    137 /// zir instruction index and a start offset. An index is not pressent in the map if the value
    138 /// at the index is `Air.Inst.Ref.none`.
    139 /// `ensureSpaceForInstructions` can be called to force InstMap to have a mapped range that
    140 /// includes all instructions in a slice. After calling this function, `putAssumeCapacity*` can
    141 /// be called safely for any of the instructions passed in.
    142 pub const InstMap = struct {
    143     items: []Air.Inst.Ref = &[_]Air.Inst.Ref{},
    144     start: Zir.Inst.Index = 0,
    145 
    146     pub fn deinit(map: InstMap, allocator: mem.Allocator) void {
    147         allocator.free(map.items);
    148     }
    149 
    150     pub fn get(map: InstMap, key: Zir.Inst.Index) ?Air.Inst.Ref {
    151         if (!map.contains(key)) return null;
    152         return map.items[key - map.start];
    153     }
    154 
    155     pub fn putAssumeCapacity(
    156         map: *InstMap,
    157         key: Zir.Inst.Index,
    158         ref: Air.Inst.Ref,
    159     ) void {
    160         map.items[key - map.start] = ref;
    161     }
    162 
    163     pub fn putAssumeCapacityNoClobber(
    164         map: *InstMap,
    165         key: Zir.Inst.Index,
    166         ref: Air.Inst.Ref,
    167     ) void {
    168         assert(!map.contains(key));
    169         map.putAssumeCapacity(key, ref);
    170     }
    171 
    172     pub const GetOrPutResult = struct {
    173         value_ptr: *Air.Inst.Ref,
    174         found_existing: bool,
    175     };
    176 
    177     pub fn getOrPutAssumeCapacity(
    178         map: *InstMap,
    179         key: Zir.Inst.Index,
    180     ) GetOrPutResult {
    181         const index = key - map.start;
    182         return GetOrPutResult{
    183             .value_ptr = &map.items[index],
    184             .found_existing = map.items[index] != .none,
    185         };
    186     }
    187 
    188     pub fn remove(map: InstMap, key: Zir.Inst.Index) bool {
    189         if (!map.contains(key)) return false;
    190         map.items[key - map.start] = .none;
    191         return true;
    192     }
    193 
    194     pub fn contains(map: InstMap, key: Zir.Inst.Index) bool {
    195         return map.items[key - map.start] != .none;
    196     }
    197 
    198     pub fn ensureSpaceForInstructions(
    199         map: *InstMap,
    200         allocator: mem.Allocator,
    201         insts: []const Zir.Inst.Index,
    202     ) !void {
    203         const min_max = mem.minMax(Zir.Inst.Index, insts);
    204         const start = min_max.min;
    205         const end = min_max.max;
    206         if (map.start <= start and end < map.items.len + map.start)
    207             return;
    208 
    209         const old_start = if (map.items.len == 0) start else map.start;
    210         var better_capacity = map.items.len;
    211         var better_start = old_start;
    212         while (true) {
    213             const extra_capacity = better_capacity / 2 + 16;
    214             better_capacity += extra_capacity;
    215             better_start -|= @as(Zir.Inst.Index, @intCast(extra_capacity / 2));
    216             if (better_start <= start and end < better_capacity + better_start)
    217                 break;
    218         }
    219 
    220         const start_diff = old_start - better_start;
    221         const new_items = try allocator.alloc(Air.Inst.Ref, better_capacity);
    222         @memset(new_items[0..start_diff], .none);
    223         @memcpy(new_items[start_diff..][0..map.items.len], map.items);
    224         @memset(new_items[start_diff + map.items.len ..], .none);
    225 
    226         allocator.free(map.items);
    227         map.items = new_items;
    228         map.start = @as(Zir.Inst.Index, @intCast(better_start));
    229     }
    230 };
    231 
    232 /// This is the context needed to semantically analyze ZIR instructions and
    233 /// produce AIR instructions.
    234 /// This is a temporary structure stored on the stack; references to it are valid only
    235 /// during semantic analysis of the block.
    236 pub const Block = struct {
    237     parent: ?*Block,
    238     /// Shared among all child blocks.
    239     sema: *Sema,
    240     /// The namespace to use for lookups from this source block
    241     /// When analyzing fields, this is different from src_decl.src_namespace.
    242     namespace: Namespace.Index,
    243     /// The AIR instructions generated for this block.
    244     instructions: std.ArrayListUnmanaged(Air.Inst.Index),
    245     // `param` instructions are collected here to be used by the `func` instruction.
    246     params: std.ArrayListUnmanaged(Param) = .{},
    247 
    248     wip_capture_scope: *CaptureScope,
    249 
    250     label: ?*Label = null,
    251     inlining: ?*Inlining,
    252     /// If runtime_index is not 0 then one of these is guaranteed to be non null.
    253     runtime_cond: ?LazySrcLoc = null,
    254     runtime_loop: ?LazySrcLoc = null,
    255     /// This Decl is the Decl according to the Zig source code corresponding to this Block.
    256     /// This can vary during inline or comptime function calls. See `Sema.owner_decl`
    257     /// for the one that will be the same for all Block instances.
    258     src_decl: Decl.Index,
    259     /// Non zero if a non-inline loop or a runtime conditional have been encountered.
    260     /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index.
    261     runtime_index: Value.RuntimeIndex = .zero,
    262     inline_block: Zir.Inst.Index = 0,
    263 
    264     comptime_reason: ?*const ComptimeReason = null,
    265     // TODO is_comptime and comptime_reason should probably be merged together.
    266     is_comptime: bool,
    267     is_typeof: bool = false,
    268 
    269     /// Keep track of the active error return trace index around blocks so that we can correctly
    270     /// pop the error trace upon block exit.
    271     error_return_trace_index: Air.Inst.Ref = .none,
    272 
    273     /// when null, it is determined by build mode, changed by @setRuntimeSafety
    274     want_safety: ?bool = null,
    275 
    276     /// What mode to generate float operations in, set by @setFloatMode
    277     float_mode: std.builtin.FloatMode = .Strict,
    278 
    279     c_import_buf: ?*std.ArrayList(u8) = null,
    280 
    281     const ComptimeReason = union(enum) {
    282         c_import: struct {
    283             block: *Block,
    284             src: LazySrcLoc,
    285         },
    286         comptime_ret_ty: struct {
    287             block: *Block,
    288             func: Air.Inst.Ref,
    289             func_src: LazySrcLoc,
    290             return_ty: Type,
    291         },
    292 
    293         fn explain(cr: ComptimeReason, sema: *Sema, msg: ?*Module.ErrorMsg) !void {
    294             const parent = msg orelse return;
    295             const mod = sema.mod;
    296             const prefix = "expression is evaluated at comptime because ";
    297             switch (cr) {
    298                 .c_import => |ci| {
    299                     try sema.errNote(ci.block, ci.src, parent, prefix ++ "it is inside a @cImport", .{});
    300                 },
    301                 .comptime_ret_ty => |rt| {
    302                     const src_loc = if (try sema.funcDeclSrc(rt.func)) |fn_decl| blk: {
    303                         var src_loc = fn_decl.srcLoc(mod);
    304                         src_loc.lazy = .{ .node_offset_fn_type_ret_ty = 0 };
    305                         break :blk src_loc;
    306                     } else blk: {
    307                         const src_decl = mod.declPtr(rt.block.src_decl);
    308                         break :blk rt.func_src.toSrcLoc(src_decl, mod);
    309                     };
    310                     if (rt.return_ty.isGenericPoison()) {
    311                         return mod.errNoteNonLazy(src_loc, parent, prefix ++ "the generic function was instantiated with a comptime-only return type", .{});
    312                     }
    313                     try mod.errNoteNonLazy(
    314                         src_loc,
    315                         parent,
    316                         prefix ++ "the function returns a comptime-only type '{}'",
    317                         .{rt.return_ty.fmt(mod)},
    318                     );
    319                     try sema.explainWhyTypeIsComptime(parent, src_loc, rt.return_ty);
    320                 },
    321             }
    322         }
    323     };
    324 
    325     const Param = struct {
    326         /// `noreturn` means `anytype`.
    327         ty: Type,
    328         is_comptime: bool,
    329         name: []const u8,
    330     };
    331 
    332     /// This `Block` maps a block ZIR instruction to the corresponding
    333     /// AIR instruction for break instruction analysis.
    334     pub const Label = struct {
    335         zir_block: Zir.Inst.Index,
    336         merges: Merges,
    337     };
    338 
    339     /// This `Block` indicates that an inline function call is happening
    340     /// and return instructions should be analyzed as a break instruction
    341     /// to this AIR block instruction.
    342     /// It is shared among all the blocks in an inline or comptime called
    343     /// function.
    344     pub const Inlining = struct {
    345         func: ?*Module.Fn,
    346         comptime_result: Air.Inst.Ref,
    347         merges: Merges,
    348     };
    349 
    350     pub const Merges = struct {
    351         block_inst: Air.Inst.Index,
    352         /// Separate array list from break_inst_list so that it can be passed directly
    353         /// to resolvePeerTypes.
    354         results: std.ArrayListUnmanaged(Air.Inst.Ref),
    355         /// Keeps track of the break instructions so that the operand can be replaced
    356         /// if we need to add type coercion at the end of block analysis.
    357         /// Same indexes, capacity, length as `results`.
    358         br_list: std.ArrayListUnmanaged(Air.Inst.Index),
    359         /// Keeps the source location of the rhs operand of the break instruction,
    360         /// to enable more precise compile errors.
    361         /// Same indexes, capacity, length as `results`.
    362         src_locs: std.ArrayListUnmanaged(?LazySrcLoc),
    363 
    364         pub fn deinit(merges: *@This(), allocator: mem.Allocator) void {
    365             merges.results.deinit(allocator);
    366             merges.br_list.deinit(allocator);
    367             merges.src_locs.deinit(allocator);
    368         }
    369     };
    370 
    371     /// For debugging purposes.
    372     pub fn dump(block: *Block, mod: Module) void {
    373         Zir.dumpBlock(mod, block);
    374     }
    375 
    376     pub fn makeSubBlock(parent: *Block) Block {
    377         return .{
    378             .parent = parent,
    379             .sema = parent.sema,
    380             .src_decl = parent.src_decl,
    381             .namespace = parent.namespace,
    382             .instructions = .{},
    383             .wip_capture_scope = parent.wip_capture_scope,
    384             .label = null,
    385             .inlining = parent.inlining,
    386             .is_comptime = parent.is_comptime,
    387             .comptime_reason = parent.comptime_reason,
    388             .is_typeof = parent.is_typeof,
    389             .runtime_cond = parent.runtime_cond,
    390             .runtime_loop = parent.runtime_loop,
    391             .runtime_index = parent.runtime_index,
    392             .want_safety = parent.want_safety,
    393             .float_mode = parent.float_mode,
    394             .c_import_buf = parent.c_import_buf,
    395             .error_return_trace_index = parent.error_return_trace_index,
    396         };
    397     }
    398 
    399     pub fn wantSafety(block: *const Block) bool {
    400         return block.want_safety orelse switch (block.sema.mod.optimizeMode()) {
    401             .Debug => true,
    402             .ReleaseSafe => true,
    403             .ReleaseFast => false,
    404             .ReleaseSmall => false,
    405         };
    406     }
    407 
    408     pub fn getFileScope(block: *Block, mod: *Module) *Module.File {
    409         return mod.namespacePtr(block.namespace).file_scope;
    410     }
    411 
    412     fn addTy(
    413         block: *Block,
    414         tag: Air.Inst.Tag,
    415         ty: Type,
    416     ) error{OutOfMemory}!Air.Inst.Ref {
    417         return block.addInst(.{
    418             .tag = tag,
    419             .data = .{ .ty = ty },
    420         });
    421     }
    422 
    423     fn addTyOp(
    424         block: *Block,
    425         tag: Air.Inst.Tag,
    426         ty: Type,
    427         operand: Air.Inst.Ref,
    428     ) error{OutOfMemory}!Air.Inst.Ref {
    429         return block.addInst(.{
    430             .tag = tag,
    431             .data = .{ .ty_op = .{
    432                 .ty = try block.sema.addType(ty),
    433                 .operand = operand,
    434             } },
    435         });
    436     }
    437 
    438     fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref {
    439         return block.addInst(.{
    440             .tag = .bitcast,
    441             .data = .{ .ty_op = .{
    442                 .ty = try block.sema.addType(ty),
    443                 .operand = operand,
    444             } },
    445         });
    446     }
    447 
    448     fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref {
    449         return block.addInst(.{
    450             .tag = tag,
    451             .data = .{ .no_op = {} },
    452         });
    453     }
    454 
    455     fn addUnOp(
    456         block: *Block,
    457         tag: Air.Inst.Tag,
    458         operand: Air.Inst.Ref,
    459     ) error{OutOfMemory}!Air.Inst.Ref {
    460         return block.addInst(.{
    461             .tag = tag,
    462             .data = .{ .un_op = operand },
    463         });
    464     }
    465 
    466     fn addBr(
    467         block: *Block,
    468         target_block: Air.Inst.Index,
    469         operand: Air.Inst.Ref,
    470     ) error{OutOfMemory}!Air.Inst.Ref {
    471         return block.addInst(.{
    472             .tag = .br,
    473             .data = .{ .br = .{
    474                 .block_inst = target_block,
    475                 .operand = operand,
    476             } },
    477         });
    478     }
    479 
    480     fn addBinOp(
    481         block: *Block,
    482         tag: Air.Inst.Tag,
    483         lhs: Air.Inst.Ref,
    484         rhs: Air.Inst.Ref,
    485     ) error{OutOfMemory}!Air.Inst.Ref {
    486         return block.addInst(.{
    487             .tag = tag,
    488             .data = .{ .bin_op = .{
    489                 .lhs = lhs,
    490                 .rhs = rhs,
    491             } },
    492         });
    493     }
    494 
    495     fn addStructFieldPtr(
    496         block: *Block,
    497         struct_ptr: Air.Inst.Ref,
    498         field_index: u32,
    499         ptr_field_ty: Type,
    500     ) !Air.Inst.Ref {
    501         const ty = try block.sema.addType(ptr_field_ty);
    502         const tag: Air.Inst.Tag = switch (field_index) {
    503             0 => .struct_field_ptr_index_0,
    504             1 => .struct_field_ptr_index_1,
    505             2 => .struct_field_ptr_index_2,
    506             3 => .struct_field_ptr_index_3,
    507             else => {
    508                 return block.addInst(.{
    509                     .tag = .struct_field_ptr,
    510                     .data = .{ .ty_pl = .{
    511                         .ty = ty,
    512                         .payload = try block.sema.addExtra(Air.StructField{
    513                             .struct_operand = struct_ptr,
    514                             .field_index = field_index,
    515                         }),
    516                     } },
    517                 });
    518             },
    519         };
    520         return block.addInst(.{
    521             .tag = tag,
    522             .data = .{ .ty_op = .{
    523                 .ty = ty,
    524                 .operand = struct_ptr,
    525             } },
    526         });
    527     }
    528 
    529     fn addStructFieldVal(
    530         block: *Block,
    531         struct_val: Air.Inst.Ref,
    532         field_index: u32,
    533         field_ty: Type,
    534     ) !Air.Inst.Ref {
    535         return block.addInst(.{
    536             .tag = .struct_field_val,
    537             .data = .{ .ty_pl = .{
    538                 .ty = try block.sema.addType(field_ty),
    539                 .payload = try block.sema.addExtra(Air.StructField{
    540                     .struct_operand = struct_val,
    541                     .field_index = field_index,
    542                 }),
    543             } },
    544         });
    545     }
    546 
    547     fn addSliceElemPtr(
    548         block: *Block,
    549         slice: Air.Inst.Ref,
    550         elem_index: Air.Inst.Ref,
    551         elem_ptr_ty: Type,
    552     ) !Air.Inst.Ref {
    553         return block.addInst(.{
    554             .tag = .slice_elem_ptr,
    555             .data = .{ .ty_pl = .{
    556                 .ty = try block.sema.addType(elem_ptr_ty),
    557                 .payload = try block.sema.addExtra(Air.Bin{
    558                     .lhs = slice,
    559                     .rhs = elem_index,
    560                 }),
    561             } },
    562         });
    563     }
    564 
    565     fn addPtrElemPtr(
    566         block: *Block,
    567         array_ptr: Air.Inst.Ref,
    568         elem_index: Air.Inst.Ref,
    569         elem_ptr_ty: Type,
    570     ) !Air.Inst.Ref {
    571         const ty_ref = try block.sema.addType(elem_ptr_ty);
    572         return block.addPtrElemPtrTypeRef(array_ptr, elem_index, ty_ref);
    573     }
    574 
    575     fn addPtrElemPtrTypeRef(
    576         block: *Block,
    577         array_ptr: Air.Inst.Ref,
    578         elem_index: Air.Inst.Ref,
    579         elem_ptr_ty: Air.Inst.Ref,
    580     ) !Air.Inst.Ref {
    581         return block.addInst(.{
    582             .tag = .ptr_elem_ptr,
    583             .data = .{ .ty_pl = .{
    584                 .ty = elem_ptr_ty,
    585                 .payload = try block.sema.addExtra(Air.Bin{
    586                     .lhs = array_ptr,
    587                     .rhs = elem_index,
    588                 }),
    589             } },
    590         });
    591     }
    592 
    593     fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref {
    594         const sema = block.sema;
    595         const mod = sema.mod;
    596         return block.addInst(.{
    597             .tag = if (block.float_mode == .Optimized) .cmp_vector_optimized else .cmp_vector,
    598             .data = .{ .ty_pl = .{
    599                 .ty = try sema.addType(
    600                     try mod.vectorType(.{
    601                         .len = sema.typeOf(lhs).vectorLen(mod),
    602                         .child = .bool_type,
    603                     }),
    604                 ),
    605                 .payload = try sema.addExtra(Air.VectorCmp{
    606                     .lhs = lhs,
    607                     .rhs = rhs,
    608                     .op = Air.VectorCmp.encodeOp(cmp_op),
    609                 }),
    610             } },
    611         });
    612     }
    613 
    614     fn addAggregateInit(
    615         block: *Block,
    616         aggregate_ty: Type,
    617         elements: []const Air.Inst.Ref,
    618     ) !Air.Inst.Ref {
    619         const sema = block.sema;
    620         const ty_ref = try sema.addType(aggregate_ty);
    621         try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len);
    622         const extra_index = @as(u32, @intCast(sema.air_extra.items.len));
    623         sema.appendRefsAssumeCapacity(elements);
    624 
    625         return block.addInst(.{
    626             .tag = .aggregate_init,
    627             .data = .{ .ty_pl = .{
    628                 .ty = ty_ref,
    629                 .payload = extra_index,
    630             } },
    631         });
    632     }
    633 
    634     fn addUnionInit(
    635         block: *Block,
    636         union_ty: Type,
    637         field_index: u32,
    638         init: Air.Inst.Ref,
    639     ) !Air.Inst.Ref {
    640         return block.addInst(.{
    641             .tag = .union_init,
    642             .data = .{ .ty_pl = .{
    643                 .ty = try block.sema.addType(union_ty),
    644                 .payload = try block.sema.addExtra(Air.UnionInit{
    645                     .field_index = field_index,
    646                     .init = init,
    647                 }),
    648             } },
    649         });
    650     }
    651 
    652     pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
    653         return Air.indexToRef(try block.addInstAsIndex(inst));
    654     }
    655 
    656     pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index {
    657         const sema = block.sema;
    658         const gpa = sema.gpa;
    659 
    660         try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
    661         try block.instructions.ensureUnusedCapacity(gpa, 1);
    662 
    663         const result_index = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
    664         sema.air_instructions.appendAssumeCapacity(inst);
    665         block.instructions.appendAssumeCapacity(result_index);
    666         return result_index;
    667     }
    668 
    669     /// Insert an instruction into the block at `index`. Moves all following
    670     /// instructions forward in the block to make room. Operation is O(N).
    671     pub fn insertInst(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
    672         return Air.indexToRef(try block.insertInstAsIndex(index, inst));
    673     }
    674 
    675     pub fn insertInstAsIndex(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index {
    676         const sema = block.sema;
    677         const gpa = sema.gpa;
    678 
    679         try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
    680 
    681         const result_index = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
    682         sema.air_instructions.appendAssumeCapacity(inst);
    683 
    684         try block.instructions.insert(gpa, index, result_index);
    685         return result_index;
    686     }
    687 
    688     fn addUnreachable(block: *Block, safety_check: bool) !void {
    689         if (safety_check and block.wantSafety()) {
    690             try block.sema.safetyPanic(block, .unreach);
    691         } else {
    692             _ = try block.addNoOp(.unreach);
    693         }
    694     }
    695 
    696     pub fn startAnonDecl(block: *Block) !WipAnonDecl {
    697         return WipAnonDecl{
    698             .block = block,
    699             .finished = false,
    700         };
    701     }
    702 
    703     pub const WipAnonDecl = struct {
    704         block: *Block,
    705         finished: bool,
    706 
    707         pub fn deinit(wad: *WipAnonDecl) void {
    708             wad.* = undefined;
    709         }
    710 
    711         /// `alignment` value of 0 means to use ABI alignment.
    712         pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: Alignment) !Decl.Index {
    713             const sema = wad.block.sema;
    714             // Do this ahead of time because `createAnonymousDecl` depends on calling
    715             // `type.hasRuntimeBits()`.
    716             _ = try sema.typeHasRuntimeBits(ty);
    717             const new_decl_index = try sema.mod.createAnonymousDecl(wad.block, .{
    718                 .ty = ty,
    719                 .val = val,
    720             });
    721             const new_decl = sema.mod.declPtr(new_decl_index);
    722             new_decl.alignment = alignment;
    723             errdefer sema.mod.abortAnonDecl(new_decl_index);
    724             wad.finished = true;
    725             try sema.mod.finalizeAnonDecl(new_decl_index);
    726             return new_decl_index;
    727         }
    728     };
    729 };
    730 
    731 const LabeledBlock = struct {
    732     block: Block,
    733     label: Block.Label,
    734 
    735     fn destroy(lb: *LabeledBlock, gpa: Allocator) void {
    736         lb.block.instructions.deinit(gpa);
    737         lb.label.merges.deinit(gpa);
    738         gpa.destroy(lb);
    739     }
    740 };
    741 
    742 /// The value stored in the inferred allocation. This will go into
    743 /// peer type resolution. This is stored in a separate list so that
    744 /// the items are contiguous in memory and thus can be passed to
    745 /// `Module.resolvePeerTypes`.
    746 const InferredAlloc = struct {
    747     prongs: std.MultiArrayList(struct {
    748         /// The dummy instruction used as a peer to resolve the type.
    749         /// Although this has a redundant type with placeholder, this is
    750         /// needed in addition because it may be a constant value, which
    751         /// affects peer type resolution.
    752         stored_inst: Air.Inst.Ref,
    753         /// The bitcast instruction used as a placeholder when the
    754         /// new result pointer type is not yet known.
    755         placeholder: Air.Inst.Index,
    756     }) = .{},
    757 };
    758 
    759 pub fn deinit(sema: *Sema) void {
    760     const gpa = sema.gpa;
    761     sema.air_instructions.deinit(gpa);
    762     sema.air_extra.deinit(gpa);
    763     sema.inst_map.deinit(gpa);
    764     sema.decl_val_table.deinit(gpa);
    765     sema.types_to_resolve.deinit(gpa);
    766     {
    767         var it = sema.post_hoc_blocks.iterator();
    768         while (it.next()) |entry| {
    769             const labeled_block = entry.value_ptr.*;
    770             labeled_block.destroy(gpa);
    771         }
    772         sema.post_hoc_blocks.deinit(gpa);
    773     }
    774     sema.unresolved_inferred_allocs.deinit(gpa);
    775     sema.* = undefined;
    776 }
    777 
    778 /// Returns only the result from the body that is specified.
    779 /// Only appropriate to call when it is determined at comptime that this body
    780 /// has no peers.
    781 fn resolveBody(
    782     sema: *Sema,
    783     block: *Block,
    784     body: []const Zir.Inst.Index,
    785     /// This is the instruction that a break instruction within `body` can
    786     /// use to return from the body.
    787     body_inst: Zir.Inst.Index,
    788 ) CompileError!Air.Inst.Ref {
    789     const break_data = (try sema.analyzeBodyBreak(block, body)) orelse
    790         return Air.Inst.Ref.unreachable_value;
    791     // For comptime control flow, we need to detect when `analyzeBody` reports
    792     // that we need to break from an outer block. In such case we
    793     // use Zig's error mechanism to send control flow up the stack until
    794     // we find the corresponding block to this break.
    795     if (block.is_comptime and break_data.block_inst != body_inst) {
    796         sema.comptime_break_inst = break_data.inst;
    797         return error.ComptimeBreak;
    798     }
    799     return try sema.resolveInst(break_data.operand);
    800 }
    801 
    802 fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !void {
    803     _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
    804         error.ComptimeBreak => {
    805             const zir_datas = sema.code.instructions.items(.data);
    806             const break_data = zir_datas[sema.comptime_break_inst].@"break";
    807             const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
    808             try sema.addRuntimeBreak(block, .{
    809                 .block_inst = extra.block_inst,
    810                 .operand = break_data.operand,
    811                 .inst = sema.comptime_break_inst,
    812             });
    813         },
    814         else => |e| return e,
    815     };
    816 }
    817 
    818 pub fn analyzeBody(
    819     sema: *Sema,
    820     block: *Block,
    821     body: []const Zir.Inst.Index,
    822 ) !void {
    823     _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
    824         error.ComptimeBreak => unreachable, // unexpected comptime control flow
    825         else => |e| return e,
    826     };
    827 }
    828 
    829 const BreakData = struct {
    830     block_inst: Zir.Inst.Index,
    831     operand: Zir.Inst.Ref,
    832     inst: Zir.Inst.Index,
    833 };
    834 
    835 pub fn analyzeBodyBreak(
    836     sema: *Sema,
    837     block: *Block,
    838     body: []const Zir.Inst.Index,
    839 ) CompileError!?BreakData {
    840     const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
    841         error.ComptimeBreak => sema.comptime_break_inst,
    842         else => |e| return e,
    843     };
    844     if (block.instructions.items.len != 0 and
    845         sema.isNoReturn(Air.indexToRef(block.instructions.items[block.instructions.items.len - 1])))
    846         return null;
    847     const break_data = sema.code.instructions.items(.data)[break_inst].@"break";
    848     const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
    849     return BreakData{
    850         .block_inst = extra.block_inst,
    851         .operand = break_data.operand,
    852         .inst = break_inst,
    853     };
    854 }
    855 
    856 /// ZIR instructions which are always `noreturn` return this. This matches the
    857 /// return type of `analyzeBody` so that we can tail call them.
    858 /// Only appropriate to return when the instruction is known to be NoReturn
    859 /// solely based on the ZIR tag.
    860 const always_noreturn: CompileError!Zir.Inst.Index = @as(Zir.Inst.Index, undefined);
    861 
    862 /// This function is the main loop of `Sema` and it can be used in two different ways:
    863 /// * The traditional way where there are N breaks out of the block and peer type
    864 ///   resolution is done on the break operands. In this case, the `Zir.Inst.Index`
    865 ///   part of the return value will be `undefined`, and callsites should ignore it,
    866 ///   finding the block result value via the block scope.
    867 /// * The "flat" way. There is only 1 break out of the block, and it is with a `break_inline`
    868 ///   instruction. In this case, the `Zir.Inst.Index` part of the return value will be
    869 ///   the break instruction. This communicates both which block the break applies to, as
    870 ///   well as the operand. No block scope needs to be created for this strategy.
    871 fn analyzeBodyInner(
    872     sema: *Sema,
    873     block: *Block,
    874     body: []const Zir.Inst.Index,
    875 ) CompileError!Zir.Inst.Index {
    876     // No tracy calls here, to avoid interfering with the tail call mechanism.
    877 
    878     try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body);
    879 
    880     // Most of the time, we don't need to construct a new capture scope for a
    881     // block. However, successive iterations of comptime loops can capture
    882     // different values for the same Zir.Inst.Index, so in those cases, we will
    883     // have to create nested capture scopes; see the `.repeat` case below.
    884     const parent_capture_scope = block.wip_capture_scope;
    885     parent_capture_scope.incRef();
    886     var wip_captures: WipCaptureScope = .{
    887         .scope = parent_capture_scope,
    888         .gpa = sema.gpa,
    889         .finalized = true, // don't finalize the parent scope
    890     };
    891     defer wip_captures.deinit();
    892 
    893     const mod = sema.mod;
    894     const map = &sema.inst_map;
    895     const tags = sema.code.instructions.items(.tag);
    896     const datas = sema.code.instructions.items(.data);
    897 
    898     var orig_captures: usize = parent_capture_scope.captures.count();
    899 
    900     var crash_info = crash_report.prepAnalyzeBody(sema, block, body);
    901     crash_info.push();
    902     defer crash_info.pop();
    903 
    904     var dbg_block_begins: u32 = 0;
    905 
    906     // We use a while (true) loop here to avoid a redundant way of breaking out of
    907     // the loop. The only way to break out of the loop is with a `noreturn`
    908     // instruction.
    909     var i: usize = 0;
    910     const result = while (true) {
    911         crash_info.setBodyIndex(i);
    912         const inst = body[i];
    913         std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{
    914             mod.namespacePtr(mod.declPtr(block.src_decl).src_namespace).file_scope.sub_file_path, inst,
    915         });
    916         const air_inst: Air.Inst.Ref = switch (tags[inst]) {
    917             // zig fmt: off
    918             .alloc                        => try sema.zirAlloc(block, inst),
    919             .alloc_inferred               => try sema.zirAllocInferred(block, inst, true),
    920             .alloc_inferred_mut           => try sema.zirAllocInferred(block, inst, false),
    921             .alloc_inferred_comptime      => try sema.zirAllocInferredComptime(inst, true),
    922             .alloc_inferred_comptime_mut  => try sema.zirAllocInferredComptime(inst, false),
    923             .alloc_mut                    => try sema.zirAllocMut(block, inst),
    924             .alloc_comptime_mut           => try sema.zirAllocComptime(block, inst),
    925             .make_ptr_const               => try sema.zirMakePtrConst(block, inst),
    926             .anyframe_type                => try sema.zirAnyframeType(block, inst),
    927             .array_cat                    => try sema.zirArrayCat(block, inst),
    928             .array_mul                    => try sema.zirArrayMul(block, inst),
    929             .array_type                   => try sema.zirArrayType(block, inst),
    930             .array_type_sentinel          => try sema.zirArrayTypeSentinel(block, inst),
    931             .vector_type                  => try sema.zirVectorType(block, inst),
    932             .as                           => try sema.zirAs(block, inst),
    933             .as_node                      => try sema.zirAsNode(block, inst),
    934             .as_shift_operand             => try sema.zirAsShiftOperand(block, inst),
    935             .bit_and                      => try sema.zirBitwise(block, inst, .bit_and),
    936             .bit_not                      => try sema.zirBitNot(block, inst),
    937             .bit_or                       => try sema.zirBitwise(block, inst, .bit_or),
    938             .bitcast                      => try sema.zirBitcast(block, inst),
    939             .suspend_block                => try sema.zirSuspendBlock(block, inst),
    940             .bool_not                     => try sema.zirBoolNot(block, inst),
    941             .bool_br_and                  => try sema.zirBoolBr(block, inst, false),
    942             .bool_br_or                   => try sema.zirBoolBr(block, inst, true),
    943             .c_import                     => try sema.zirCImport(block, inst),
    944             .call                         => try sema.zirCall(block, inst, .direct),
    945             .field_call                   => try sema.zirCall(block, inst, .field),
    946             .closure_get                  => try sema.zirClosureGet(block, inst),
    947             .cmp_lt                       => try sema.zirCmp(block, inst, .lt),
    948             .cmp_lte                      => try sema.zirCmp(block, inst, .lte),
    949             .cmp_eq                       => try sema.zirCmpEq(block, inst, .eq, Air.Inst.Tag.fromCmpOp(.eq, block.float_mode == .Optimized)),
    950             .cmp_gte                      => try sema.zirCmp(block, inst, .gte),
    951             .cmp_gt                       => try sema.zirCmp(block, inst, .gt),
    952             .cmp_neq                      => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .Optimized)),
    953             .coerce_result_ptr            => try sema.zirCoerceResultPtr(block, inst),
    954             .decl_ref                     => try sema.zirDeclRef(block, inst),
    955             .decl_val                     => try sema.zirDeclVal(block, inst),
    956             .load                         => try sema.zirLoad(block, inst),
    957             .elem_ptr                     => try sema.zirElemPtr(block, inst),
    958             .elem_ptr_node                => try sema.zirElemPtrNode(block, inst),
    959             .elem_ptr_imm                 => try sema.zirElemPtrImm(block, inst),
    960             .elem_val                     => try sema.zirElemVal(block, inst),
    961             .elem_val_node                => try sema.zirElemValNode(block, inst),
    962             .elem_type_index              => try sema.zirElemTypeIndex(block, inst),
    963             .elem_type                    => try sema.zirElemType(block, inst),
    964             .enum_literal                 => try sema.zirEnumLiteral(block, inst),
    965             .int_from_enum                  => try sema.zirIntFromEnum(block, inst),
    966             .enum_from_int                  => try sema.zirEnumFromInt(block, inst),
    967             .err_union_code               => try sema.zirErrUnionCode(block, inst),
    968             .err_union_code_ptr           => try sema.zirErrUnionCodePtr(block, inst),
    969             .err_union_payload_unsafe     => try sema.zirErrUnionPayload(block, inst),
    970             .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst),
    971             .error_union_type             => try sema.zirErrorUnionType(block, inst),
    972             .error_value                  => try sema.zirErrorValue(block, inst),
    973             .field_ptr                    => try sema.zirFieldPtr(block, inst, false),
    974             .field_ptr_init               => try sema.zirFieldPtr(block, inst, true),
    975             .field_ptr_named              => try sema.zirFieldPtrNamed(block, inst),
    976             .field_val                    => try sema.zirFieldVal(block, inst),
    977             .field_val_named              => try sema.zirFieldValNamed(block, inst),
    978             .func                         => try sema.zirFunc(block, inst, false),
    979             .func_inferred                => try sema.zirFunc(block, inst, true),
    980             .func_fancy                   => try sema.zirFuncFancy(block, inst),
    981             .import                       => try sema.zirImport(block, inst),
    982             .indexable_ptr_len            => try sema.zirIndexablePtrLen(block, inst),
    983             .int                          => try sema.zirInt(block, inst),
    984             .int_big                      => try sema.zirIntBig(block, inst),
    985             .float                        => try sema.zirFloat(block, inst),
    986             .float128                     => try sema.zirFloat128(block, inst),
    987             .int_type                     => try sema.zirIntType(inst),
    988             .is_non_err                   => try sema.zirIsNonErr(block, inst),
    989             .is_non_err_ptr               => try sema.zirIsNonErrPtr(block, inst),
    990             .ret_is_non_err               => try sema.zirRetIsNonErr(block, inst),
    991             .is_non_null                  => try sema.zirIsNonNull(block, inst),
    992             .is_non_null_ptr              => try sema.zirIsNonNullPtr(block, inst),
    993             .merge_error_sets             => try sema.zirMergeErrorSets(block, inst),
    994             .negate                       => try sema.zirNegate(block, inst),
    995             .negate_wrap                  => try sema.zirNegateWrap(block, inst),
    996             .optional_payload_safe        => try sema.zirOptionalPayload(block, inst, true),
    997             .optional_payload_safe_ptr    => try sema.zirOptionalPayloadPtr(block, inst, true),
    998             .optional_payload_unsafe      => try sema.zirOptionalPayload(block, inst, false),
    999             .optional_payload_unsafe_ptr  => try sema.zirOptionalPayloadPtr(block, inst, false),
   1000             .optional_type                => try sema.zirOptionalType(block, inst),
   1001             .ptr_type                     => try sema.zirPtrType(block, inst),
   1002             .ref                          => try sema.zirRef(block, inst),
   1003             .ret_err_value_code           => try sema.zirRetErrValueCode(inst),
   1004             .shr                          => try sema.zirShr(block, inst, .shr),
   1005             .shr_exact                    => try sema.zirShr(block, inst, .shr_exact),
   1006             .slice_end                    => try sema.zirSliceEnd(block, inst),
   1007             .slice_sentinel               => try sema.zirSliceSentinel(block, inst),
   1008             .slice_start                  => try sema.zirSliceStart(block, inst),
   1009             .slice_length                 => try sema.zirSliceLength(block, inst),
   1010             .str                          => try sema.zirStr(block, inst),
   1011             .switch_block                 => try sema.zirSwitchBlock(block, inst, false),
   1012             .switch_block_ref             => try sema.zirSwitchBlock(block, inst, true),
   1013             .type_info                    => try sema.zirTypeInfo(block, inst),
   1014             .size_of                      => try sema.zirSizeOf(block, inst),
   1015             .bit_size_of                  => try sema.zirBitSizeOf(block, inst),
   1016             .typeof                       => try sema.zirTypeof(block, inst),
   1017             .typeof_builtin               => try sema.zirTypeofBuiltin(block, inst),
   1018             .typeof_log2_int_type         => try sema.zirTypeofLog2IntType(block, inst),
   1019             .xor                          => try sema.zirBitwise(block, inst, .xor),
   1020             .struct_init_empty            => try sema.zirStructInitEmpty(block, inst),
   1021             .struct_init                  => try sema.zirStructInit(block, inst, false),
   1022             .struct_init_ref              => try sema.zirStructInit(block, inst, true),
   1023             .struct_init_anon             => try sema.zirStructInitAnon(block, inst, false),
   1024             .struct_init_anon_ref         => try sema.zirStructInitAnon(block, inst, true),
   1025             .array_init                   => try sema.zirArrayInit(block, inst, false),
   1026             .array_init_ref               => try sema.zirArrayInit(block, inst, true),
   1027             .array_init_anon              => try sema.zirArrayInitAnon(block, inst, false),
   1028             .array_init_anon_ref          => try sema.zirArrayInitAnon(block, inst, true),
   1029             .union_init                   => try sema.zirUnionInit(block, inst),
   1030             .field_type                   => try sema.zirFieldType(block, inst),
   1031             .field_type_ref               => try sema.zirFieldTypeRef(block, inst),
   1032             .int_from_ptr                   => try sema.zirIntFromPtr(block, inst),
   1033             .align_of                     => try sema.zirAlignOf(block, inst),
   1034             .int_from_bool                  => try sema.zirIntFromBool(block, inst),
   1035             .embed_file                   => try sema.zirEmbedFile(block, inst),
   1036             .error_name                   => try sema.zirErrorName(block, inst),
   1037             .tag_name                     => try sema.zirTagName(block, inst),
   1038             .type_name                    => try sema.zirTypeName(block, inst),
   1039             .frame_type                   => try sema.zirFrameType(block, inst),
   1040             .frame_size                   => try sema.zirFrameSize(block, inst),
   1041             .int_from_float                 => try sema.zirIntFromFloat(block, inst),
   1042             .float_from_int                 => try sema.zirFloatFromInt(block, inst),
   1043             .ptr_from_int                   => try sema.zirPtrFromInt(block, inst),
   1044             .float_cast                   => try sema.zirFloatCast(block, inst),
   1045             .int_cast                     => try sema.zirIntCast(block, inst),
   1046             .ptr_cast                     => try sema.zirPtrCast(block, inst),
   1047             .truncate                     => try sema.zirTruncate(block, inst),
   1048             .has_decl                     => try sema.zirHasDecl(block, inst),
   1049             .has_field                    => try sema.zirHasField(block, inst),
   1050             .byte_swap                    => try sema.zirByteSwap(block, inst),
   1051             .bit_reverse                  => try sema.zirBitReverse(block, inst),
   1052             .bit_offset_of                => try sema.zirBitOffsetOf(block, inst),
   1053             .offset_of                    => try sema.zirOffsetOf(block, inst),
   1054             .splat                        => try sema.zirSplat(block, inst),
   1055             .reduce                       => try sema.zirReduce(block, inst),
   1056             .shuffle                      => try sema.zirShuffle(block, inst),
   1057             .atomic_load                  => try sema.zirAtomicLoad(block, inst),
   1058             .atomic_rmw                   => try sema.zirAtomicRmw(block, inst),
   1059             .mul_add                      => try sema.zirMulAdd(block, inst),
   1060             .builtin_call                 => try sema.zirBuiltinCall(block, inst),
   1061             .field_parent_ptr             => try sema.zirFieldParentPtr(block, inst),
   1062             .@"resume"                    => try sema.zirResume(block, inst),
   1063             .@"await"                     => try sema.zirAwait(block, inst),
   1064             .array_base_ptr               => try sema.zirArrayBasePtr(block, inst),
   1065             .field_base_ptr               => try sema.zirFieldBasePtr(block, inst),
   1066             .for_len                      => try sema.zirForLen(block, inst),
   1067 
   1068             .clz       => try sema.zirBitCount(block, inst, .clz,      Value.clz),
   1069             .ctz       => try sema.zirBitCount(block, inst, .ctz,      Value.ctz),
   1070             .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount),
   1071 
   1072             .sqrt  => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt),
   1073             .sin   => try sema.zirUnaryMath(block, inst, .sin, Value.sin),
   1074             .cos   => try sema.zirUnaryMath(block, inst, .cos, Value.cos),
   1075             .tan   => try sema.zirUnaryMath(block, inst, .tan, Value.tan),
   1076             .exp   => try sema.zirUnaryMath(block, inst, .exp, Value.exp),
   1077             .exp2  => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2),
   1078             .log   => try sema.zirUnaryMath(block, inst, .log, Value.log),
   1079             .log2  => try sema.zirUnaryMath(block, inst, .log2, Value.log2),
   1080             .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10),
   1081             .fabs  => try sema.zirUnaryMath(block, inst, .fabs, Value.fabs),
   1082             .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor),
   1083             .ceil  => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil),
   1084             .round => try sema.zirUnaryMath(block, inst, .round, Value.round),
   1085             .trunc => try sema.zirUnaryMath(block, inst, .trunc_float, Value.trunc),
   1086 
   1087             .error_set_decl      => try sema.zirErrorSetDecl(block, inst, .parent),
   1088             .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon),
   1089             .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func),
   1090 
   1091             .add       => try sema.zirArithmetic(block, inst, .add,        true),
   1092             .addwrap   => try sema.zirArithmetic(block, inst, .addwrap,    true),
   1093             .add_sat   => try sema.zirArithmetic(block, inst, .add_sat,    true),
   1094             .add_unsafe=> try sema.zirArithmetic(block, inst, .add_unsafe, false),
   1095             .mul       => try sema.zirArithmetic(block, inst, .mul,        true),
   1096             .mulwrap   => try sema.zirArithmetic(block, inst, .mulwrap,    true),
   1097             .mul_sat   => try sema.zirArithmetic(block, inst, .mul_sat,    true),
   1098             .sub       => try sema.zirArithmetic(block, inst, .sub,        true),
   1099             .subwrap   => try sema.zirArithmetic(block, inst, .subwrap,    true),
   1100             .sub_sat   => try sema.zirArithmetic(block, inst, .sub_sat,    true),
   1101 
   1102             .div       => try sema.zirDiv(block, inst),
   1103             .div_exact => try sema.zirDivExact(block, inst),
   1104             .div_floor => try sema.zirDivFloor(block, inst),
   1105             .div_trunc => try sema.zirDivTrunc(block, inst),
   1106 
   1107             .mod_rem   => try sema.zirModRem(block, inst),
   1108             .mod       => try sema.zirMod(block, inst),
   1109             .rem       => try sema.zirRem(block, inst),
   1110 
   1111             .max => try sema.zirMinMax(block, inst, .max),
   1112             .min => try sema.zirMinMax(block, inst, .min),
   1113 
   1114             .shl       => try sema.zirShl(block, inst, .shl),
   1115             .shl_exact => try sema.zirShl(block, inst, .shl_exact),
   1116             .shl_sat   => try sema.zirShl(block, inst, .shl_sat),
   1117 
   1118             .ret_ptr  => try sema.zirRetPtr(block),
   1119             .ret_type => try sema.addType(sema.fn_ret_ty),
   1120 
   1121             // Instructions that we know to *always* be noreturn based solely on their tag.
   1122             // These functions match the return type of analyzeBody so that we can
   1123             // tail call them here.
   1124             .compile_error  => break sema.zirCompileError(block, inst),
   1125             .ret_implicit   => break sema.zirRetImplicit(block, inst),
   1126             .ret_node       => break sema.zirRetNode(block, inst),
   1127             .ret_load       => break sema.zirRetLoad(block, inst),
   1128             .ret_err_value  => break sema.zirRetErrValue(block, inst),
   1129             .@"unreachable" => break sema.zirUnreachable(block, inst),
   1130             .panic          => break sema.zirPanic(block, inst),
   1131             .trap           => break sema.zirTrap(block, inst),
   1132             // zig fmt: on
   1133 
   1134             .extended => ext: {
   1135                 const extended = datas[inst].extended;
   1136                 break :ext switch (extended.opcode) {
   1137                     // zig fmt: off
   1138                     .variable              => try sema.zirVarExtended(       block, extended),
   1139                     .struct_decl           => try sema.zirStructDecl(        block, extended, inst),
   1140                     .enum_decl             => try sema.zirEnumDecl(          block, extended, inst),
   1141                     .union_decl            => try sema.zirUnionDecl(         block, extended, inst),
   1142                     .opaque_decl           => try sema.zirOpaqueDecl(        block, extended, inst),
   1143                     .this                  => try sema.zirThis(              block, extended),
   1144                     .ret_addr              => try sema.zirRetAddr(           block, extended),
   1145                     .builtin_src           => try sema.zirBuiltinSrc(        block, extended),
   1146                     .error_return_trace    => try sema.zirErrorReturnTrace(  block),
   1147                     .frame                 => try sema.zirFrame(             block, extended),
   1148                     .frame_address         => try sema.zirFrameAddress(      block, extended),
   1149                     .alloc                 => try sema.zirAllocExtended(     block, extended),
   1150                     .builtin_extern        => try sema.zirBuiltinExtern(     block, extended),
   1151                     .@"asm"                => try sema.zirAsm(               block, extended, false),
   1152                     .asm_expr              => try sema.zirAsm(               block, extended, true),
   1153                     .typeof_peer           => try sema.zirTypeofPeer(        block, extended),
   1154                     .compile_log           => try sema.zirCompileLog(               extended),
   1155                     .min_multi             => try sema.zirMinMaxMulti(       block, extended, .min),
   1156                     .max_multi             => try sema.zirMinMaxMulti(       block, extended, .max),
   1157                     .add_with_overflow     => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1158                     .sub_with_overflow     => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1159                     .mul_with_overflow     => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1160                     .shl_with_overflow     => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1161                     .c_undef               => try sema.zirCUndef(            block, extended),
   1162                     .c_include             => try sema.zirCInclude(          block, extended),
   1163                     .c_define              => try sema.zirCDefine(           block, extended),
   1164                     .wasm_memory_size      => try sema.zirWasmMemorySize(    block, extended),
   1165                     .wasm_memory_grow      => try sema.zirWasmMemoryGrow(    block, extended),
   1166                     .prefetch              => try sema.zirPrefetch(          block, extended),
   1167                     .err_set_cast          => try sema.zirErrSetCast(        block, extended),
   1168                     .await_nosuspend       => try sema.zirAwaitNosuspend(    block, extended),
   1169                     .select                => try sema.zirSelect(            block, extended),
   1170                     .int_from_error          => try sema.zirIntFromError(        block, extended),
   1171                     .error_from_int          => try sema.zirErrorFromInt(        block, extended),
   1172                     .reify                 => try sema.zirReify(             block, extended, inst),
   1173                     .builtin_async_call    => try sema.zirBuiltinAsyncCall(  block, extended),
   1174                     .cmpxchg               => try sema.zirCmpxchg(           block, extended),
   1175                     .c_va_arg              => try sema.zirCVaArg(            block, extended),
   1176                     .c_va_copy             => try sema.zirCVaCopy(           block, extended),
   1177                     .c_va_end              => try sema.zirCVaEnd(            block, extended),
   1178                     .c_va_start            => try sema.zirCVaStart(          block, extended),
   1179                     .ptr_cast_full         => try sema.zirPtrCastFull(       block, extended),
   1180                     .ptr_cast_no_dest      => try sema.zirPtrCastNoDest(       block, extended),
   1181                     .work_item_id          => try sema.zirWorkItem(          block, extended, extended.opcode),
   1182                     .work_group_size       => try sema.zirWorkItem(          block, extended, extended.opcode),
   1183                     .work_group_id         => try sema.zirWorkItem(          block, extended, extended.opcode),
   1184                     .in_comptime           => try sema.zirInComptime(        block),
   1185                     // zig fmt: on
   1186 
   1187                     .fence => {
   1188                         try sema.zirFence(block, extended);
   1189                         i += 1;
   1190                         continue;
   1191                     },
   1192                     .set_float_mode => {
   1193                         try sema.zirSetFloatMode(block, extended);
   1194                         i += 1;
   1195                         continue;
   1196                     },
   1197                     .set_align_stack => {
   1198                         try sema.zirSetAlignStack(block, extended);
   1199                         i += 1;
   1200                         continue;
   1201                     },
   1202                     .set_cold => {
   1203                         try sema.zirSetCold(block, extended);
   1204                         i += 1;
   1205                         continue;
   1206                     },
   1207                     .breakpoint => {
   1208                         if (!block.is_comptime) {
   1209                             _ = try block.addNoOp(.breakpoint);
   1210                         }
   1211                         i += 1;
   1212                         continue;
   1213                     },
   1214                     .value_placeholder => unreachable, // never appears in a body
   1215                 };
   1216             },
   1217 
   1218             // Instructions that we know can *never* be noreturn based solely on
   1219             // their tag. We avoid needlessly checking if they are noreturn and
   1220             // continue the loop.
   1221             // We also know that they cannot be referenced later, so we avoid
   1222             // putting them into the map.
   1223             .dbg_stmt => {
   1224                 try sema.zirDbgStmt(block, inst);
   1225                 i += 1;
   1226                 continue;
   1227             },
   1228             .dbg_var_ptr => {
   1229                 try sema.zirDbgVar(block, inst, .dbg_var_ptr);
   1230                 i += 1;
   1231                 continue;
   1232             },
   1233             .dbg_var_val => {
   1234                 try sema.zirDbgVar(block, inst, .dbg_var_val);
   1235                 i += 1;
   1236                 continue;
   1237             },
   1238             .dbg_block_begin => {
   1239                 dbg_block_begins += 1;
   1240                 try sema.zirDbgBlockBegin(block);
   1241                 i += 1;
   1242                 continue;
   1243             },
   1244             .dbg_block_end => {
   1245                 dbg_block_begins -= 1;
   1246                 try sema.zirDbgBlockEnd(block);
   1247                 i += 1;
   1248                 continue;
   1249             },
   1250             .ensure_err_union_payload_void => {
   1251                 try sema.zirEnsureErrUnionPayloadVoid(block, inst);
   1252                 i += 1;
   1253                 continue;
   1254             },
   1255             .ensure_result_non_error => {
   1256                 try sema.zirEnsureResultNonError(block, inst);
   1257                 i += 1;
   1258                 continue;
   1259             },
   1260             .ensure_result_used => {
   1261                 try sema.zirEnsureResultUsed(block, inst);
   1262                 i += 1;
   1263                 continue;
   1264             },
   1265             .set_eval_branch_quota => {
   1266                 try sema.zirSetEvalBranchQuota(block, inst);
   1267                 i += 1;
   1268                 continue;
   1269             },
   1270             .atomic_store => {
   1271                 try sema.zirAtomicStore(block, inst);
   1272                 i += 1;
   1273                 continue;
   1274             },
   1275             .store => {
   1276                 try sema.zirStore(block, inst);
   1277                 i += 1;
   1278                 continue;
   1279             },
   1280             .store_node => {
   1281                 try sema.zirStoreNode(block, inst);
   1282                 i += 1;
   1283                 continue;
   1284             },
   1285             .store_to_block_ptr => {
   1286                 try sema.zirStoreToBlockPtr(block, inst);
   1287                 i += 1;
   1288                 continue;
   1289             },
   1290             .store_to_inferred_ptr => {
   1291                 try sema.zirStoreToInferredPtr(block, inst);
   1292                 i += 1;
   1293                 continue;
   1294             },
   1295             .resolve_inferred_alloc => {
   1296                 try sema.zirResolveInferredAlloc(block, inst);
   1297                 i += 1;
   1298                 continue;
   1299             },
   1300             .validate_array_init_ty => {
   1301                 try sema.validateArrayInitTy(block, inst);
   1302                 i += 1;
   1303                 continue;
   1304             },
   1305             .validate_struct_init_ty => {
   1306                 try sema.validateStructInitTy(block, inst);
   1307                 i += 1;
   1308                 continue;
   1309             },
   1310             .validate_struct_init => {
   1311                 try sema.zirValidateStructInit(block, inst);
   1312                 i += 1;
   1313                 continue;
   1314             },
   1315             .validate_array_init => {
   1316                 try sema.zirValidateArrayInit(block, inst);
   1317                 i += 1;
   1318                 continue;
   1319             },
   1320             .validate_deref => {
   1321                 try sema.zirValidateDeref(block, inst);
   1322                 i += 1;
   1323                 continue;
   1324             },
   1325             .@"export" => {
   1326                 try sema.zirExport(block, inst);
   1327                 i += 1;
   1328                 continue;
   1329             },
   1330             .export_value => {
   1331                 try sema.zirExportValue(block, inst);
   1332                 i += 1;
   1333                 continue;
   1334             },
   1335             .set_runtime_safety => {
   1336                 try sema.zirSetRuntimeSafety(block, inst);
   1337                 i += 1;
   1338                 continue;
   1339             },
   1340             .param => {
   1341                 try sema.zirParam(block, inst, false);
   1342                 i += 1;
   1343                 continue;
   1344             },
   1345             .param_comptime => {
   1346                 try sema.zirParam(block, inst, true);
   1347                 i += 1;
   1348                 continue;
   1349             },
   1350             .param_anytype => {
   1351                 try sema.zirParamAnytype(block, inst, false);
   1352                 i += 1;
   1353                 continue;
   1354             },
   1355             .param_anytype_comptime => {
   1356                 try sema.zirParamAnytype(block, inst, true);
   1357                 i += 1;
   1358                 continue;
   1359             },
   1360             .closure_capture => {
   1361                 try sema.zirClosureCapture(block, inst);
   1362                 i += 1;
   1363                 continue;
   1364             },
   1365             .memcpy => {
   1366                 try sema.zirMemcpy(block, inst);
   1367                 i += 1;
   1368                 continue;
   1369             },
   1370             .memset => {
   1371                 try sema.zirMemset(block, inst);
   1372                 i += 1;
   1373                 continue;
   1374             },
   1375             .check_comptime_control_flow => {
   1376                 if (!block.is_comptime) {
   1377                     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   1378                     const src = inst_data.src();
   1379                     const inline_block = Zir.refToIndex(inst_data.operand).?;
   1380 
   1381                     var check_block = block;
   1382                     const target_runtime_index = while (true) {
   1383                         if (check_block.inline_block == inline_block) {
   1384                             break check_block.runtime_index;
   1385                         }
   1386                         check_block = check_block.parent.?;
   1387                     };
   1388 
   1389                     if (@intFromEnum(target_runtime_index) < @intFromEnum(block.runtime_index)) {
   1390                         const runtime_src = block.runtime_cond orelse block.runtime_loop.?;
   1391                         const msg = msg: {
   1392                             const msg = try sema.errMsg(block, src, "comptime control flow inside runtime block", .{});
   1393                             errdefer msg.destroy(sema.gpa);
   1394 
   1395                             try sema.errNote(block, runtime_src, msg, "runtime control flow here", .{});
   1396                             break :msg msg;
   1397                         };
   1398                         return sema.failWithOwnedErrorMsg(msg);
   1399                     }
   1400                 }
   1401                 i += 1;
   1402                 continue;
   1403             },
   1404             .save_err_ret_index => {
   1405                 try sema.zirSaveErrRetIndex(block, inst);
   1406                 i += 1;
   1407                 continue;
   1408             },
   1409             .restore_err_ret_index => {
   1410                 try sema.zirRestoreErrRetIndex(block, inst);
   1411                 i += 1;
   1412                 continue;
   1413             },
   1414 
   1415             // Special case instructions to handle comptime control flow.
   1416             .@"break" => {
   1417                 if (block.is_comptime) {
   1418                     break inst; // same as break_inline
   1419                 } else {
   1420                     break sema.zirBreak(block, inst);
   1421                 }
   1422             },
   1423             .break_inline => {
   1424                 if (block.is_comptime) {
   1425                     break inst;
   1426                 } else {
   1427                     sema.comptime_break_inst = inst;
   1428                     return error.ComptimeBreak;
   1429                 }
   1430             },
   1431             .repeat => {
   1432                 if (block.is_comptime) {
   1433                     // Send comptime control flow back to the beginning of this block.
   1434                     const src = LazySrcLoc.nodeOffset(datas[inst].node);
   1435                     try sema.emitBackwardBranch(block, src);
   1436                     if (wip_captures.scope.captures.count() != orig_captures) {
   1437                         // We need to construct new capture scopes for the next loop iteration so it
   1438                         // can capture values without clobbering the earlier iteration's captures.
   1439                         // At first, we reused the parent capture scope as an optimization, but for
   1440                         // successive scopes we have to create new ones as children of the parent
   1441                         // scope.
   1442                         try wip_captures.reset(parent_capture_scope);
   1443                         block.wip_capture_scope = wip_captures.scope;
   1444                         orig_captures = 0;
   1445                     }
   1446                     i = 0;
   1447                     continue;
   1448                 } else {
   1449                     break always_noreturn;
   1450                 }
   1451             },
   1452             .repeat_inline => {
   1453                 // Send comptime control flow back to the beginning of this block.
   1454                 const src = LazySrcLoc.nodeOffset(datas[inst].node);
   1455                 try sema.emitBackwardBranch(block, src);
   1456                 if (wip_captures.scope.captures.count() != orig_captures) {
   1457                     // We need to construct new capture scopes for the next loop iteration so it
   1458                     // can capture values without clobbering the earlier iteration's captures.
   1459                     // At first, we reused the parent capture scope as an optimization, but for
   1460                     // successive scopes we have to create new ones as children of the parent
   1461                     // scope.
   1462                     try wip_captures.reset(parent_capture_scope);
   1463                     block.wip_capture_scope = wip_captures.scope;
   1464                     orig_captures = 0;
   1465                 }
   1466                 i = 0;
   1467                 continue;
   1468             },
   1469             .loop => blk: {
   1470                 if (!block.is_comptime) break :blk try sema.zirLoop(block, inst);
   1471                 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220
   1472                 const inst_data = datas[inst].pl_node;
   1473                 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   1474                 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
   1475                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1476                     break always_noreturn;
   1477                 if (inst == break_data.block_inst) {
   1478                     break :blk try sema.resolveInst(break_data.operand);
   1479                 } else {
   1480                     break break_data.inst;
   1481                 }
   1482             },
   1483             .block, .block_comptime => blk: {
   1484                 if (!block.is_comptime) {
   1485                     break :blk try sema.zirBlock(block, inst, tags[inst] == .block_comptime);
   1486                 }
   1487                 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220
   1488                 const inst_data = datas[inst].pl_node;
   1489                 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   1490                 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
   1491                 // If this block contains a function prototype, we need to reset the
   1492                 // current list of parameters and restore it later.
   1493                 // Note: this probably needs to be resolved in a more general manner.
   1494                 const prev_params = block.params;
   1495                 block.params = .{};
   1496                 defer {
   1497                     block.params.deinit(sema.gpa);
   1498                     block.params = prev_params;
   1499                 }
   1500                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1501                     break always_noreturn;
   1502                 if (inst == break_data.block_inst) {
   1503                     break :blk try sema.resolveInst(break_data.operand);
   1504                 } else {
   1505                     break break_data.inst;
   1506                 }
   1507             },
   1508             .block_inline => blk: {
   1509                 // Directly analyze the block body without introducing a new block.
   1510                 // However, in the case of a corresponding break_inline which reaches
   1511                 // through a runtime conditional branch, we must retroactively emit
   1512                 // a block, so we remember the block index here just in case.
   1513                 const block_index = block.instructions.items.len;
   1514                 const inst_data = datas[inst].pl_node;
   1515                 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   1516                 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
   1517                 const gpa = sema.gpa;
   1518 
   1519                 const opt_break_data = b: {
   1520                     // Create a temporary child block so that this inline block is properly
   1521                     // labeled for any .restore_err_ret_index instructions
   1522                     var child_block = block.makeSubBlock();
   1523 
   1524                     // If this block contains a function prototype, we need to reset the
   1525                     // current list of parameters and restore it later.
   1526                     // Note: this probably needs to be resolved in a more general manner.
   1527                     child_block.inline_block =
   1528                         if (tags[inline_body[inline_body.len - 1]] == .repeat_inline) inline_body[0] else inst;
   1529 
   1530                     var label: Block.Label = .{
   1531                         .zir_block = inst,
   1532                         .merges = undefined,
   1533                     };
   1534                     child_block.label = &label;
   1535                     defer child_block.params.deinit(gpa);
   1536 
   1537                     // Write these instructions directly into the parent block
   1538                     child_block.instructions = block.instructions;
   1539                     defer block.instructions = child_block.instructions;
   1540 
   1541                     break :b try sema.analyzeBodyBreak(&child_block, inline_body);
   1542                 };
   1543 
   1544                 // A runtime conditional branch that needs a post-hoc block to be
   1545                 // emitted communicates this by mapping the block index into the inst map.
   1546                 if (map.get(inst)) |new_block_ref| ph: {
   1547                     // Comptime control flow populates the map, so we don't actually know
   1548                     // if this is a post-hoc runtime block until we check the
   1549                     // post_hoc_block map.
   1550                     const new_block_inst = Air.refToIndex(new_block_ref) orelse break :ph;
   1551                     const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse
   1552                         break :ph;
   1553 
   1554                     // In this case we need to move all the instructions starting at
   1555                     // block_index from the current block into this new one.
   1556 
   1557                     if (opt_break_data) |break_data| {
   1558                         // This is a comptime break which we now change to a runtime break
   1559                         // since it crosses a runtime branch.
   1560                         // It may pass through our currently being analyzed block_inline or it
   1561                         // may point directly to it. In the latter case, this modifies the
   1562                         // block that we are about to look up in the post_hoc_blocks map below.
   1563                         try sema.addRuntimeBreak(block, break_data);
   1564                     } else {
   1565                         // Here the comptime control flow ends with noreturn; however
   1566                         // we have runtime control flow continuing after this block.
   1567                         // This branch is therefore handled by the `i += 1; continue;`
   1568                         // logic below.
   1569                     }
   1570 
   1571                     try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]);
   1572                     block.instructions.items.len = block_index;
   1573 
   1574                     const block_result = try sema.analyzeBlockBody(block, inst_data.src(), &labeled_block.block, &labeled_block.label.merges);
   1575                     {
   1576                         // Destroy the ad-hoc block entry so that it does not interfere with
   1577                         // the next iteration of comptime control flow, if any.
   1578                         labeled_block.destroy(gpa);
   1579                         assert(sema.post_hoc_blocks.remove(new_block_inst));
   1580                     }
   1581                     map.putAssumeCapacity(inst, block_result);
   1582                     i += 1;
   1583                     continue;
   1584                 }
   1585 
   1586                 const break_data = opt_break_data orelse break always_noreturn;
   1587                 if (inst == break_data.block_inst) {
   1588                     break :blk try sema.resolveInst(break_data.operand);
   1589                 } else {
   1590                     break break_data.inst;
   1591                 }
   1592             },
   1593             .condbr => blk: {
   1594                 if (!block.is_comptime) break sema.zirCondbr(block, inst);
   1595                 // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220
   1596                 const inst_data = datas[inst].pl_node;
   1597                 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
   1598                 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
   1599                 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
   1600                 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
   1601                 const cond = sema.resolveInstConst(block, cond_src, extra.data.condition, "condition in comptime branch must be comptime-known") catch |err| {
   1602                     if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err);
   1603                     return err;
   1604                 };
   1605                 const inline_body = if (cond.val.toBool()) then_body else else_body;
   1606 
   1607                 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src);
   1608                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1609                     break always_noreturn;
   1610                 if (inst == break_data.block_inst) {
   1611                     break :blk try sema.resolveInst(break_data.operand);
   1612                 } else {
   1613                     break break_data.inst;
   1614                 }
   1615             },
   1616             .condbr_inline => blk: {
   1617                 const inst_data = datas[inst].pl_node;
   1618                 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
   1619                 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
   1620                 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
   1621                 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
   1622                 const cond = sema.resolveInstConst(block, cond_src, extra.data.condition, "condition in comptime branch must be comptime-known") catch |err| {
   1623                     if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err);
   1624                     return err;
   1625                 };
   1626                 const inline_body = if (cond.val.toBool()) then_body else else_body;
   1627 
   1628                 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src);
   1629                 const old_runtime_index = block.runtime_index;
   1630                 defer block.runtime_index = old_runtime_index;
   1631                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1632                     break always_noreturn;
   1633                 if (inst == break_data.block_inst) {
   1634                     break :blk try sema.resolveInst(break_data.operand);
   1635                 } else {
   1636                     break break_data.inst;
   1637                 }
   1638             },
   1639             .@"try" => blk: {
   1640                 if (!block.is_comptime) break :blk try sema.zirTry(block, inst);
   1641                 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   1642                 const src = inst_data.src();
   1643                 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
   1644                 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
   1645                 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
   1646                 const err_union = try sema.resolveInst(extra.data.operand);
   1647                 const err_union_ty = sema.typeOf(err_union);
   1648                 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
   1649                     return sema.fail(block, operand_src, "expected error union type, found '{}'", .{
   1650                         err_union_ty.fmt(mod),
   1651                     });
   1652                 }
   1653                 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
   1654                 assert(is_non_err != .none);
   1655                 const is_non_err_val = sema.resolveConstValue(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| {
   1656                     if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err);
   1657                     return err;
   1658                 };
   1659                 if (is_non_err_val.toBool()) {
   1660                     break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
   1661                 }
   1662                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1663                     break always_noreturn;
   1664                 if (inst == break_data.block_inst) {
   1665                     break :blk try sema.resolveInst(break_data.operand);
   1666                 } else {
   1667                     break break_data.inst;
   1668                 }
   1669             },
   1670             .try_ptr => blk: {
   1671                 if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst);
   1672                 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   1673                 const src = inst_data.src();
   1674                 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
   1675                 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
   1676                 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
   1677                 const operand = try sema.resolveInst(extra.data.operand);
   1678                 const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
   1679                 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
   1680                 assert(is_non_err != .none);
   1681                 const is_non_err_val = sema.resolveConstValue(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| {
   1682                     if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err);
   1683                     return err;
   1684                 };
   1685                 if (is_non_err_val.toBool()) {
   1686                     break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
   1687                 }
   1688                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
   1689                     break always_noreturn;
   1690                 if (inst == break_data.block_inst) {
   1691                     break :blk try sema.resolveInst(break_data.operand);
   1692                 } else {
   1693                     break break_data.inst;
   1694                 }
   1695             },
   1696             .@"defer" => blk: {
   1697                 const inst_data = sema.code.instructions.items(.data)[inst].@"defer";
   1698                 const defer_body = sema.code.extra[inst_data.index..][0..inst_data.len];
   1699                 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
   1700                     error.ComptimeBreak => sema.comptime_break_inst,
   1701                     else => |e| return e,
   1702                 };
   1703                 if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn;
   1704                 break :blk Air.Inst.Ref.void_value;
   1705             },
   1706             .defer_err_code => blk: {
   1707                 const inst_data = sema.code.instructions.items(.data)[inst].defer_err_code;
   1708                 const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data;
   1709                 const defer_body = sema.code.extra[extra.index..][0..extra.len];
   1710                 const err_code = try sema.resolveInst(inst_data.err_code);
   1711                 map.putAssumeCapacity(extra.remapped_err_code, err_code);
   1712                 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
   1713                     error.ComptimeBreak => sema.comptime_break_inst,
   1714                     else => |e| return e,
   1715                 };
   1716                 if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn;
   1717                 break :blk Air.Inst.Ref.void_value;
   1718             },
   1719         };
   1720         if (sema.isNoReturn(air_inst)) {
   1721             // We're going to assume that the body itself is noreturn, so let's ensure that now
   1722             assert(block.instructions.items.len > 0);
   1723             assert(sema.isNoReturn(Air.indexToRef(block.instructions.items[block.instructions.items.len - 1])));
   1724             break always_noreturn;
   1725         }
   1726         map.putAssumeCapacity(inst, air_inst);
   1727         i += 1;
   1728     };
   1729 
   1730     // balance out dbg_block_begins in case of early noreturn
   1731     const noreturn_inst = block.instructions.popOrNull();
   1732     while (dbg_block_begins > 0) {
   1733         dbg_block_begins -= 1;
   1734         if (block.is_comptime or mod.comp.bin_file.options.strip) continue;
   1735 
   1736         _ = try block.addInst(.{
   1737             .tag = .dbg_block_end,
   1738             .data = undefined,
   1739         });
   1740     }
   1741     if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some);
   1742 
   1743     if (!wip_captures.finalized) {
   1744         // We've updated the capture scope due to a `repeat` instruction where
   1745         // the body had a capture; finalize our child scope and reset
   1746         try wip_captures.finalize();
   1747         block.wip_capture_scope = parent_capture_scope;
   1748     }
   1749 
   1750     return result;
   1751 }
   1752 
   1753 pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
   1754     if (zir_ref == .none) {
   1755         return .none;
   1756     } else {
   1757         return resolveInst(sema, zir_ref);
   1758     }
   1759 }
   1760 
   1761 pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
   1762     assert(zir_ref != .none);
   1763     const i = @intFromEnum(zir_ref);
   1764     // First section of indexes correspond to a set number of constant values.
   1765     // We intentionally map the same indexes to the same values between ZIR and AIR.
   1766     if (i < InternPool.static_len) return @as(Air.Inst.Ref, @enumFromInt(i));
   1767     // The last section of indexes refers to the map of ZIR => AIR.
   1768     const inst = sema.inst_map.get(i - InternPool.static_len).?;
   1769     if (inst == .generic_poison) return error.GenericPoison;
   1770     return inst;
   1771 }
   1772 
   1773 fn resolveConstBool(
   1774     sema: *Sema,
   1775     block: *Block,
   1776     src: LazySrcLoc,
   1777     zir_ref: Zir.Inst.Ref,
   1778     reason: []const u8,
   1779 ) !bool {
   1780     const air_inst = try sema.resolveInst(zir_ref);
   1781     const wanted_type = Type.bool;
   1782     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   1783     const val = try sema.resolveConstValue(block, src, coerced_inst, reason);
   1784     return val.toBool();
   1785 }
   1786 
   1787 pub fn resolveConstString(
   1788     sema: *Sema,
   1789     block: *Block,
   1790     src: LazySrcLoc,
   1791     zir_ref: Zir.Inst.Ref,
   1792     reason: []const u8,
   1793 ) ![]u8 {
   1794     const air_inst = try sema.resolveInst(zir_ref);
   1795     const wanted_type = Type.slice_const_u8;
   1796     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   1797     const val = try sema.resolveConstValue(block, src, coerced_inst, reason);
   1798     return val.toAllocatedBytes(wanted_type, sema.arena, sema.mod);
   1799 }
   1800 
   1801 pub fn resolveConstStringIntern(
   1802     sema: *Sema,
   1803     block: *Block,
   1804     src: LazySrcLoc,
   1805     zir_ref: Zir.Inst.Ref,
   1806     reason: []const u8,
   1807 ) !InternPool.NullTerminatedString {
   1808     const air_inst = try sema.resolveInst(zir_ref);
   1809     const wanted_type = Type.slice_const_u8;
   1810     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   1811     const val = try sema.resolveConstValue(block, src, coerced_inst, reason);
   1812     return val.toIpString(wanted_type, sema.mod);
   1813 }
   1814 
   1815 pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
   1816     const air_inst = try sema.resolveInst(zir_ref);
   1817     assert(air_inst != .var_args_param_type);
   1818     const ty = try sema.analyzeAsType(block, src, air_inst);
   1819     if (ty.isGenericPoison()) return error.GenericPoison;
   1820     return ty;
   1821 }
   1822 
   1823 fn resolveCastDestType(
   1824     sema: *Sema,
   1825     block: *Block,
   1826     src: LazySrcLoc,
   1827     zir_ref: Zir.Inst.Ref,
   1828     strat: enum { remove_eu_opt, remove_eu, remove_opt },
   1829     builtin_name: []const u8,
   1830 ) !Type {
   1831     const mod = sema.mod;
   1832     const remove_eu = switch (strat) {
   1833         .remove_eu_opt, .remove_eu => true,
   1834         .remove_opt => false,
   1835     };
   1836     const remove_opt = switch (strat) {
   1837         .remove_eu_opt, .remove_opt => true,
   1838         .remove_eu => false,
   1839     };
   1840 
   1841     const raw_ty = sema.resolveType(block, src, zir_ref) catch |err| switch (err) {
   1842         error.GenericPoison => {
   1843             // Cast builtins use their result type as the destination type, but
   1844             // it could be an anytype argument, which we can't catch in AstGen.
   1845             const msg = msg: {
   1846                 const msg = try sema.errMsg(block, src, "{s} must have a known result type", .{builtin_name});
   1847                 errdefer msg.destroy(sema.gpa);
   1848                 try sema.errNote(block, src, msg, "result type is unknown due to anytype parameter", .{});
   1849                 try sema.errNote(block, src, msg, "use @as to provide explicit result type", .{});
   1850                 break :msg msg;
   1851             };
   1852             return sema.failWithOwnedErrorMsg(msg);
   1853         },
   1854         else => |e| return e,
   1855     };
   1856 
   1857     if (remove_eu and raw_ty.zigTypeTag(mod) == .ErrorUnion) {
   1858         const eu_child = raw_ty.errorUnionPayload(mod);
   1859         if (remove_opt and eu_child.zigTypeTag(mod) == .Optional) {
   1860             return eu_child.childType(mod);
   1861         }
   1862         return eu_child;
   1863     }
   1864     if (remove_opt and raw_ty.zigTypeTag(mod) == .Optional) {
   1865         return raw_ty.childType(mod);
   1866     }
   1867     return raw_ty;
   1868 }
   1869 
   1870 fn analyzeAsType(
   1871     sema: *Sema,
   1872     block: *Block,
   1873     src: LazySrcLoc,
   1874     air_inst: Air.Inst.Ref,
   1875 ) !Type {
   1876     const wanted_type = Type.type;
   1877     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   1878     const val = try sema.resolveConstValue(block, src, coerced_inst, "types must be comptime-known");
   1879     return val.toType();
   1880 }
   1881 
   1882 pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void {
   1883     const mod = sema.mod;
   1884     const gpa = sema.gpa;
   1885     const ip = &mod.intern_pool;
   1886     if (!mod.backendSupportsFeature(.error_return_trace)) return;
   1887 
   1888     assert(!block.is_comptime);
   1889     var err_trace_block = block.makeSubBlock();
   1890     defer err_trace_block.instructions.deinit(gpa);
   1891 
   1892     const src: LazySrcLoc = .unneeded;
   1893 
   1894     // var addrs: [err_return_trace_addr_count]usize = undefined;
   1895     const err_return_trace_addr_count = 32;
   1896     const addr_arr_ty = try mod.arrayType(.{
   1897         .len = err_return_trace_addr_count,
   1898         .child = .usize_type,
   1899     });
   1900     const addrs_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(addr_arr_ty));
   1901 
   1902     // var st: StackTrace = undefined;
   1903     const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
   1904     const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
   1905     const st_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(stack_trace_ty));
   1906 
   1907     // st.instruction_addresses = &addrs;
   1908     const instruction_addresses_field_name = try ip.getOrPutString(gpa, "instruction_addresses");
   1909     const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true);
   1910     try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
   1911 
   1912     // st.index = 0;
   1913     const index_field_name = try ip.getOrPutString(gpa, "index");
   1914     const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true);
   1915     try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store);
   1916 
   1917     // @errorReturnTrace() = &st;
   1918     _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr);
   1919 
   1920     try block.instructions.insertSlice(gpa, last_arg_index, err_trace_block.instructions.items);
   1921 }
   1922 
   1923 /// May return Value Tags: `variable`, `undef`.
   1924 /// See `resolveConstValue` for an alternative.
   1925 /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
   1926 fn resolveValue(
   1927     sema: *Sema,
   1928     block: *Block,
   1929     src: LazySrcLoc,
   1930     air_ref: Air.Inst.Ref,
   1931     reason: []const u8,
   1932 ) CompileError!Value {
   1933     if (try sema.resolveMaybeUndefValAllowVariables(air_ref)) |val| {
   1934         if (val.isGenericPoison()) return error.GenericPoison;
   1935         return val;
   1936     }
   1937     return sema.failWithNeededComptime(block, src, reason);
   1938 }
   1939 
   1940 /// Value Tag `variable` will cause a compile error.
   1941 /// Value Tag `undef` may be returned.
   1942 fn resolveConstMaybeUndefVal(
   1943     sema: *Sema,
   1944     block: *Block,
   1945     src: LazySrcLoc,
   1946     inst: Air.Inst.Ref,
   1947     reason: []const u8,
   1948 ) CompileError!Value {
   1949     if (try sema.resolveMaybeUndefValAllowVariables(inst)) |val| {
   1950         if (val.isGenericPoison()) return error.GenericPoison;
   1951         if (sema.mod.intern_pool.isVariable(val.toIntern()))
   1952             return sema.failWithNeededComptime(block, src, reason);
   1953         return val;
   1954     }
   1955     return sema.failWithNeededComptime(block, src, reason);
   1956 }
   1957 
   1958 /// Will not return Value Tags: `variable`, `undef`. Instead they will emit compile errors.
   1959 /// See `resolveValue` for an alternative.
   1960 fn resolveConstValue(
   1961     sema: *Sema,
   1962     block: *Block,
   1963     src: LazySrcLoc,
   1964     air_ref: Air.Inst.Ref,
   1965     reason: []const u8,
   1966 ) CompileError!Value {
   1967     if (try sema.resolveMaybeUndefValAllowVariables(air_ref)) |val| {
   1968         if (val.isGenericPoison()) return error.GenericPoison;
   1969         if (val.isUndef(sema.mod)) return sema.failWithUseOfUndef(block, src);
   1970         if (sema.mod.intern_pool.isVariable(val.toIntern()))
   1971             return sema.failWithNeededComptime(block, src, reason);
   1972         return val;
   1973     }
   1974     return sema.failWithNeededComptime(block, src, reason);
   1975 }
   1976 
   1977 /// Will not return Value Tags: `variable`, `undef`. Instead they will emit compile errors.
   1978 /// Lazy values are recursively resolved.
   1979 fn resolveConstLazyValue(
   1980     sema: *Sema,
   1981     block: *Block,
   1982     src: LazySrcLoc,
   1983     air_ref: Air.Inst.Ref,
   1984     reason: []const u8,
   1985 ) CompileError!Value {
   1986     return sema.resolveLazyValue(try sema.resolveConstValue(block, src, air_ref, reason));
   1987 }
   1988 
   1989 /// Value Tag `variable` causes this function to return `null`.
   1990 /// Value Tag `undef` causes this function to return a compile error.
   1991 fn resolveDefinedValue(
   1992     sema: *Sema,
   1993     block: *Block,
   1994     src: LazySrcLoc,
   1995     air_ref: Air.Inst.Ref,
   1996 ) CompileError!?Value {
   1997     const mod = sema.mod;
   1998     if (try sema.resolveMaybeUndefVal(air_ref)) |val| {
   1999         if (val.isUndef(mod)) {
   2000             if (block.is_typeof) return null;
   2001             return sema.failWithUseOfUndef(block, src);
   2002         }
   2003         return val;
   2004     }
   2005     return null;
   2006 }
   2007 
   2008 /// Value Tag `variable` causes this function to return `null`.
   2009 /// Value Tag `undef` causes this function to return the Value.
   2010 /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
   2011 fn resolveMaybeUndefVal(
   2012     sema: *Sema,
   2013     inst: Air.Inst.Ref,
   2014 ) CompileError!?Value {
   2015     const val = (try sema.resolveMaybeUndefValAllowVariables(inst)) orelse return null;
   2016     if (val.isGenericPoison()) return error.GenericPoison;
   2017     if (val.ip_index != .none and sema.mod.intern_pool.isVariable(val.toIntern())) return null;
   2018     return val;
   2019 }
   2020 
   2021 /// Value Tag `variable` causes this function to return `null`.
   2022 /// Value Tag `undef` causes this function to return the Value.
   2023 /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
   2024 /// Lazy values are recursively resolved.
   2025 fn resolveMaybeUndefLazyVal(
   2026     sema: *Sema,
   2027     inst: Air.Inst.Ref,
   2028 ) CompileError!?Value {
   2029     return try sema.resolveLazyValue((try sema.resolveMaybeUndefVal(inst)) orelse return null);
   2030 }
   2031 
   2032 /// Value Tag `variable` results in `null`.
   2033 /// Value Tag `undef` results in the Value.
   2034 /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
   2035 /// Value Tag `decl_ref` and `decl_ref_mut` or any nested such value results in `null`.
   2036 /// Lazy values are recursively resolved.
   2037 fn resolveMaybeUndefValIntable(
   2038     sema: *Sema,
   2039     inst: Air.Inst.Ref,
   2040 ) CompileError!?Value {
   2041     const val = (try sema.resolveMaybeUndefValAllowVariables(inst)) orelse return null;
   2042     if (val.isGenericPoison()) return error.GenericPoison;
   2043     if (val.ip_index == .none) return val;
   2044     if (sema.mod.intern_pool.isVariable(val.toIntern())) return null;
   2045     if (sema.mod.intern_pool.getBackingAddrTag(val.toIntern())) |addr| switch (addr) {
   2046         .decl, .mut_decl, .comptime_field => return null,
   2047         .int => {},
   2048         .eu_payload, .opt_payload, .elem, .field => unreachable,
   2049     };
   2050     return try sema.resolveLazyValue(val);
   2051 }
   2052 
   2053 /// Returns all Value tags including `variable` and `undef`.
   2054 fn resolveMaybeUndefValAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
   2055     var make_runtime = false;
   2056     if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(inst, &make_runtime)) |val| {
   2057         if (make_runtime) return null;
   2058         return val;
   2059     }
   2060     return null;
   2061 }
   2062 
   2063 /// Returns all Value tags including `variable`, `undef` and `runtime_value`.
   2064 fn resolveMaybeUndefValAllowVariablesMaybeRuntime(
   2065     sema: *Sema,
   2066     inst: Air.Inst.Ref,
   2067     make_runtime: *bool,
   2068 ) CompileError!?Value {
   2069     assert(inst != .none);
   2070     // First section of indexes correspond to a set number of constant values.
   2071     if (@intFromEnum(inst) < InternPool.static_len) {
   2072         return @as(InternPool.Index, @enumFromInt(@intFromEnum(inst))).toValue();
   2073     }
   2074 
   2075     const air_tags = sema.air_instructions.items(.tag);
   2076     if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| {
   2077         if (Air.refToInterned(inst)) |ip_index| {
   2078             const val = ip_index.toValue();
   2079             if (val.getVariable(sema.mod) != null) return val;
   2080         }
   2081         return opv;
   2082     }
   2083     const ip_index = Air.refToInterned(inst) orelse {
   2084         switch (air_tags[Air.refToIndex(inst).?]) {
   2085             .inferred_alloc => unreachable,
   2086             .inferred_alloc_comptime => unreachable,
   2087             else => return null,
   2088         }
   2089     };
   2090     const val = ip_index.toValue();
   2091     if (val.isRuntimeValue(sema.mod)) make_runtime.* = true;
   2092     if (val.isPtrToThreadLocal(sema.mod)) make_runtime.* = true;
   2093     return val;
   2094 }
   2095 
   2096 fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: []const u8) CompileError {
   2097     const msg = msg: {
   2098         const msg = try sema.errMsg(block, src, "unable to resolve comptime value", .{});
   2099         errdefer msg.destroy(sema.gpa);
   2100 
   2101         try sema.errNote(block, src, msg, "{s}", .{reason});
   2102         break :msg msg;
   2103     };
   2104     return sema.failWithOwnedErrorMsg(msg);
   2105 }
   2106 
   2107 fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
   2108     return sema.fail(block, src, "use of undefined value here causes undefined behavior", .{});
   2109 }
   2110 
   2111 fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
   2112     return sema.fail(block, src, "division by zero here causes undefined behavior", .{});
   2113 }
   2114 
   2115 fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError {
   2116     return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{
   2117         lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod),
   2118     });
   2119 }
   2120 
   2121 fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, optional_ty: Type) CompileError {
   2122     return sema.fail(block, src, "expected optional type, found '{}'", .{optional_ty.fmt(sema.mod)});
   2123 }
   2124 
   2125 fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
   2126     const mod = sema.mod;
   2127     const msg = msg: {
   2128         const msg = try sema.errMsg(block, src, "type '{}' does not support array initialization syntax", .{
   2129             ty.fmt(mod),
   2130         });
   2131         errdefer msg.destroy(sema.gpa);
   2132         if (ty.isSlice(mod)) {
   2133             try sema.errNote(block, src, msg, "inferred array length is specified with an underscore: '[_]{}'", .{ty.elemType2(mod).fmt(mod)});
   2134         }
   2135         break :msg msg;
   2136     };
   2137     return sema.failWithOwnedErrorMsg(msg);
   2138 }
   2139 
   2140 fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
   2141     return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{
   2142         ty.fmt(sema.mod),
   2143     });
   2144 }
   2145 
   2146 fn failWithErrorSetCodeMissing(
   2147     sema: *Sema,
   2148     block: *Block,
   2149     src: LazySrcLoc,
   2150     dest_err_set_ty: Type,
   2151     src_err_set_ty: Type,
   2152 ) CompileError {
   2153     return sema.fail(block, src, "expected type '{}', found type '{}'", .{
   2154         dest_err_set_ty.fmt(sema.mod), src_err_set_ty.fmt(sema.mod),
   2155     });
   2156 }
   2157 
   2158 fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: usize) CompileError {
   2159     const mod = sema.mod;
   2160     if (int_ty.zigTypeTag(mod) == .Vector) {
   2161         const msg = msg: {
   2162             const msg = try sema.errMsg(block, src, "overflow of vector type '{}' with value '{}'", .{
   2163                 int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod),
   2164             });
   2165             errdefer msg.destroy(sema.gpa);
   2166             try sema.errNote(block, src, msg, "when computing vector element at index '{d}'", .{vector_index});
   2167             break :msg msg;
   2168         };
   2169         return sema.failWithOwnedErrorMsg(msg);
   2170     }
   2171     return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{
   2172         int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod),
   2173     });
   2174 }
   2175 
   2176 fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazySrcLoc, container_ty: Type, field_index: usize) CompileError {
   2177     const mod = sema.mod;
   2178     const msg = msg: {
   2179         const msg = try sema.errMsg(block, init_src, "value stored in comptime field does not match the default value of the field", .{});
   2180         errdefer msg.destroy(sema.gpa);
   2181 
   2182         const struct_ty = mod.typeToStruct(container_ty) orelse break :msg msg;
   2183         const default_value_src = mod.fieldSrcLoc(struct_ty.owner_decl, .{
   2184             .index = field_index,
   2185             .range = .value,
   2186         });
   2187         try mod.errNoteNonLazy(default_value_src, msg, "default value set here", .{});
   2188         break :msg msg;
   2189     };
   2190     return sema.failWithOwnedErrorMsg(msg);
   2191 }
   2192 
   2193 fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
   2194     const msg = msg: {
   2195         const msg = try sema.errMsg(block, src, "async has not been implemented in the self-hosted compiler yet", .{});
   2196         errdefer msg.destroy(sema.gpa);
   2197         break :msg msg;
   2198     };
   2199     return sema.failWithOwnedErrorMsg(msg);
   2200 }
   2201 
   2202 fn failWithInvalidFieldAccess(
   2203     sema: *Sema,
   2204     block: *Block,
   2205     src: LazySrcLoc,
   2206     object_ty: Type,
   2207     field_name: InternPool.NullTerminatedString,
   2208 ) CompileError {
   2209     const mod = sema.mod;
   2210     const inner_ty = if (object_ty.isSinglePointer(mod)) object_ty.childType(mod) else object_ty;
   2211 
   2212     if (inner_ty.zigTypeTag(mod) == .Optional) opt: {
   2213         const child_ty = inner_ty.optionalChild(mod);
   2214         if (!typeSupportsFieldAccess(mod, child_ty, field_name)) break :opt;
   2215         const msg = msg: {
   2216             const msg = try sema.errMsg(block, src, "optional type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
   2217             errdefer msg.destroy(sema.gpa);
   2218             try sema.errNote(block, src, msg, "consider using '.?', 'orelse', or 'if'", .{});
   2219             break :msg msg;
   2220         };
   2221         return sema.failWithOwnedErrorMsg(msg);
   2222     } else if (inner_ty.zigTypeTag(mod) == .ErrorUnion) err: {
   2223         const child_ty = inner_ty.errorUnionPayload(mod);
   2224         if (!typeSupportsFieldAccess(mod, child_ty, field_name)) break :err;
   2225         const msg = msg: {
   2226             const msg = try sema.errMsg(block, src, "error union type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
   2227             errdefer msg.destroy(sema.gpa);
   2228             try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{});
   2229             break :msg msg;
   2230         };
   2231         return sema.failWithOwnedErrorMsg(msg);
   2232     }
   2233     return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
   2234 }
   2235 
   2236 fn typeSupportsFieldAccess(mod: *const Module, ty: Type, field_name: InternPool.NullTerminatedString) bool {
   2237     const ip = &mod.intern_pool;
   2238     switch (ty.zigTypeTag(mod)) {
   2239         .Array => return ip.stringEqlSlice(field_name, "len"),
   2240         .Pointer => {
   2241             const ptr_info = ty.ptrInfo(mod);
   2242             if (ptr_info.flags.size == .Slice) {
   2243                 return ip.stringEqlSlice(field_name, "ptr") or ip.stringEqlSlice(field_name, "len");
   2244             } else if (ptr_info.child.toType().zigTypeTag(mod) == .Array) {
   2245                 return ip.stringEqlSlice(field_name, "len");
   2246             } else return false;
   2247         },
   2248         .Type, .Struct, .Union => return true,
   2249         else => return false,
   2250     }
   2251 }
   2252 
   2253 /// We don't return a pointer to the new error note because the pointer
   2254 /// becomes invalid when you add another one.
   2255 fn errNote(
   2256     sema: *Sema,
   2257     block: *Block,
   2258     src: LazySrcLoc,
   2259     parent: *Module.ErrorMsg,
   2260     comptime format: []const u8,
   2261     args: anytype,
   2262 ) error{OutOfMemory}!void {
   2263     const mod = sema.mod;
   2264     const src_decl = mod.declPtr(block.src_decl);
   2265     return mod.errNoteNonLazy(src.toSrcLoc(src_decl, mod), parent, format, args);
   2266 }
   2267 
   2268 fn addFieldErrNote(
   2269     sema: *Sema,
   2270     container_ty: Type,
   2271     field_index: usize,
   2272     parent: *Module.ErrorMsg,
   2273     comptime format: []const u8,
   2274     args: anytype,
   2275 ) !void {
   2276     @setCold(true);
   2277     const mod = sema.mod;
   2278     const decl_index = container_ty.getOwnerDecl(mod);
   2279     const decl = mod.declPtr(decl_index);
   2280 
   2281     const field_src = blk: {
   2282         const tree = decl.getFileScope(mod).getTree(sema.gpa) catch |err| {
   2283             log.err("unable to load AST to report compile error: {s}", .{@errorName(err)});
   2284             break :blk decl.srcLoc(mod);
   2285         };
   2286 
   2287         const container_node = decl.relativeToNodeIndex(0);
   2288         const node_tags = tree.nodes.items(.tag);
   2289         var buf: [2]std.zig.Ast.Node.Index = undefined;
   2290         const container_decl = tree.fullContainerDecl(&buf, container_node) orelse break :blk decl.srcLoc(mod);
   2291 
   2292         var it_index: usize = 0;
   2293         for (container_decl.ast.members) |member_node| {
   2294             switch (node_tags[member_node]) {
   2295                 .container_field_init,
   2296                 .container_field_align,
   2297                 .container_field,
   2298                 => {
   2299                     if (it_index == field_index) {
   2300                         break :blk decl.nodeOffsetSrcLoc(decl.nodeIndexToRelative(member_node), mod);
   2301                     }
   2302                     it_index += 1;
   2303                 },
   2304                 else => continue,
   2305             }
   2306         }
   2307         unreachable;
   2308     };
   2309     try mod.errNoteNonLazy(field_src, parent, format, args);
   2310 }
   2311 
   2312 fn errMsg(
   2313     sema: *Sema,
   2314     block: *Block,
   2315     src: LazySrcLoc,
   2316     comptime format: []const u8,
   2317     args: anytype,
   2318 ) error{OutOfMemory}!*Module.ErrorMsg {
   2319     const mod = sema.mod;
   2320     const src_decl = mod.declPtr(block.src_decl);
   2321     return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl, mod), format, args);
   2322 }
   2323 
   2324 pub fn fail(
   2325     sema: *Sema,
   2326     block: *Block,
   2327     src: LazySrcLoc,
   2328     comptime format: []const u8,
   2329     args: anytype,
   2330 ) CompileError {
   2331     const err_msg = try sema.errMsg(block, src, format, args);
   2332     return sema.failWithOwnedErrorMsg(err_msg);
   2333 }
   2334 
   2335 fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
   2336     @setCold(true);
   2337     const gpa = sema.gpa;
   2338     const mod = sema.mod;
   2339 
   2340     if (crash_report.is_enabled and mod.comp.debug_compile_errors) {
   2341         if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation;
   2342         var wip_errors: std.zig.ErrorBundle.Wip = undefined;
   2343         wip_errors.init(gpa) catch unreachable;
   2344         Compilation.addModuleErrorMsg(mod, &wip_errors, err_msg.*) catch unreachable;
   2345         std.debug.print("compile error during Sema:\n", .{});
   2346         var error_bundle = wip_errors.toOwnedBundle("") catch unreachable;
   2347         error_bundle.renderToStdErr(.{ .ttyconf = .no_color });
   2348         crash_report.compilerPanic("unexpected compile error occurred", null, null);
   2349     }
   2350 
   2351     ref: {
   2352         errdefer err_msg.destroy(gpa);
   2353         if (err_msg.src_loc.lazy == .unneeded) {
   2354             return error.NeededSourceLocation;
   2355         }
   2356         try mod.failed_decls.ensureUnusedCapacity(gpa, 1);
   2357         try mod.failed_files.ensureUnusedCapacity(gpa, 1);
   2358 
   2359         const max_references = blk: {
   2360             if (mod.comp.reference_trace) |num| break :blk num;
   2361             // Do not add multiple traces without explicit request.
   2362             if (mod.failed_decls.count() != 0) break :ref;
   2363             break :blk default_reference_trace_len;
   2364         };
   2365 
   2366         var referenced_by = if (sema.func) |some| some.owner_decl else sema.owner_decl_index;
   2367         var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(gpa);
   2368         defer reference_stack.deinit();
   2369 
   2370         // Avoid infinite loops.
   2371         var seen = std.AutoHashMap(Decl.Index, void).init(gpa);
   2372         defer seen.deinit();
   2373 
   2374         var cur_reference_trace: u32 = 0;
   2375         while (sema.mod.reference_table.get(referenced_by)) |ref| : (cur_reference_trace += 1) {
   2376             const gop = try seen.getOrPut(ref.referencer);
   2377             if (gop.found_existing) break;
   2378             if (cur_reference_trace < max_references) {
   2379                 const decl = sema.mod.declPtr(ref.referencer);
   2380                 try reference_stack.append(.{
   2381                     .decl = decl.name.toOptional(),
   2382                     .src_loc = ref.src.toSrcLoc(decl, mod),
   2383                 });
   2384             }
   2385             referenced_by = ref.referencer;
   2386         }
   2387         if (sema.mod.comp.reference_trace == null and cur_reference_trace > 0) {
   2388             try reference_stack.append(.{
   2389                 .decl = .none,
   2390                 .src_loc = undefined,
   2391                 .hidden = 0,
   2392             });
   2393         } else if (cur_reference_trace > max_references) {
   2394             try reference_stack.append(.{
   2395                 .decl = undefined,
   2396                 .src_loc = undefined,
   2397                 .hidden = cur_reference_trace - max_references,
   2398             });
   2399         }
   2400         err_msg.reference_trace = try reference_stack.toOwnedSlice();
   2401     }
   2402     if (sema.owner_func) |func| {
   2403         func.state = .sema_failure;
   2404     } else {
   2405         sema.owner_decl.analysis = .sema_failure;
   2406         sema.owner_decl.generation = mod.generation;
   2407     }
   2408     if (sema.func) |func| {
   2409         func.state = .sema_failure;
   2410     }
   2411     const gop = mod.failed_decls.getOrPutAssumeCapacity(sema.owner_decl_index);
   2412     if (gop.found_existing) {
   2413         // If there are multiple errors for the same Decl, prefer the first one added.
   2414         sema.err = null;
   2415         err_msg.destroy(gpa);
   2416     } else {
   2417         sema.err = err_msg;
   2418         gop.value_ptr.* = err_msg;
   2419     }
   2420     return error.AnalysisFail;
   2421 }
   2422 
   2423 /// Given an ErrorMsg, modify its message and source location to the given values, turning the
   2424 /// original message into a note. Notes on the original message are preserved as further notes.
   2425 /// Reference trace is preserved.
   2426 fn reparentOwnedErrorMsg(
   2427     sema: *Sema,
   2428     block: *Block,
   2429     src: LazySrcLoc,
   2430     msg: *Module.ErrorMsg,
   2431     comptime format: []const u8,
   2432     args: anytype,
   2433 ) !void {
   2434     const mod = sema.mod;
   2435     const src_decl = mod.declPtr(block.src_decl);
   2436     const resolved_src = src.toSrcLoc(src_decl, mod);
   2437     const msg_str = try std.fmt.allocPrint(mod.gpa, format, args);
   2438 
   2439     const orig_notes = msg.notes.len;
   2440     msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1);
   2441     std.mem.copyBackwards(Module.ErrorMsg, msg.notes[1..], msg.notes[0..orig_notes]);
   2442     msg.notes[0] = .{
   2443         .src_loc = msg.src_loc,
   2444         .msg = msg.msg,
   2445     };
   2446 
   2447     msg.src_loc = resolved_src;
   2448     msg.msg = msg_str;
   2449 }
   2450 
   2451 const align_ty = Type.u29;
   2452 
   2453 fn analyzeAsAlign(
   2454     sema: *Sema,
   2455     block: *Block,
   2456     src: LazySrcLoc,
   2457     air_ref: Air.Inst.Ref,
   2458 ) !Alignment {
   2459     const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty, "alignment must be comptime-known");
   2460     const alignment = @as(u32, @intCast(alignment_big)); // We coerce to u29 in the prev line.
   2461     try sema.validateAlign(block, src, alignment);
   2462     return Alignment.fromNonzeroByteUnits(alignment);
   2463 }
   2464 
   2465 fn validateAlign(
   2466     sema: *Sema,
   2467     block: *Block,
   2468     src: LazySrcLoc,
   2469     alignment: u32,
   2470 ) !void {
   2471     if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{});
   2472     if (!std.math.isPowerOfTwo(alignment)) {
   2473         return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{
   2474             alignment,
   2475         });
   2476     }
   2477 }
   2478 
   2479 pub fn resolveAlign(
   2480     sema: *Sema,
   2481     block: *Block,
   2482     src: LazySrcLoc,
   2483     zir_ref: Zir.Inst.Ref,
   2484 ) !Alignment {
   2485     const air_ref = try sema.resolveInst(zir_ref);
   2486     return sema.analyzeAsAlign(block, src, air_ref);
   2487 }
   2488 
   2489 fn resolveInt(
   2490     sema: *Sema,
   2491     block: *Block,
   2492     src: LazySrcLoc,
   2493     zir_ref: Zir.Inst.Ref,
   2494     dest_ty: Type,
   2495     reason: []const u8,
   2496 ) !u64 {
   2497     const air_ref = try sema.resolveInst(zir_ref);
   2498     return sema.analyzeAsInt(block, src, air_ref, dest_ty, reason);
   2499 }
   2500 
   2501 fn analyzeAsInt(
   2502     sema: *Sema,
   2503     block: *Block,
   2504     src: LazySrcLoc,
   2505     air_ref: Air.Inst.Ref,
   2506     dest_ty: Type,
   2507     reason: []const u8,
   2508 ) !u64 {
   2509     const mod = sema.mod;
   2510     const coerced = try sema.coerce(block, dest_ty, air_ref, src);
   2511     const val = try sema.resolveConstValue(block, src, coerced, reason);
   2512     return (try val.getUnsignedIntAdvanced(mod, sema)).?;
   2513 }
   2514 
   2515 // Returns a compile error if the value has tag `variable`. See `resolveInstValue` for
   2516 // a function that does not.
   2517 pub fn resolveInstConst(
   2518     sema: *Sema,
   2519     block: *Block,
   2520     src: LazySrcLoc,
   2521     zir_ref: Zir.Inst.Ref,
   2522     reason: []const u8,
   2523 ) CompileError!TypedValue {
   2524     const air_ref = try sema.resolveInst(zir_ref);
   2525     const val = try sema.resolveConstValue(block, src, air_ref, reason);
   2526     return TypedValue{
   2527         .ty = sema.typeOf(air_ref),
   2528         .val = val,
   2529     };
   2530 }
   2531 
   2532 // Value Tag may be `undef` or `variable`.
   2533 // See `resolveInstConst` for an alternative.
   2534 pub fn resolveInstValue(
   2535     sema: *Sema,
   2536     block: *Block,
   2537     src: LazySrcLoc,
   2538     zir_ref: Zir.Inst.Ref,
   2539     reason: []const u8,
   2540 ) CompileError!TypedValue {
   2541     const air_ref = try sema.resolveInst(zir_ref);
   2542     const val = try sema.resolveValue(block, src, air_ref, reason);
   2543     return TypedValue{
   2544         .ty = sema.typeOf(air_ref),
   2545         .val = val,
   2546     };
   2547 }
   2548 
   2549 fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   2550     const tracy = trace(@src());
   2551     defer tracy.end();
   2552 
   2553     const mod = sema.mod;
   2554     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   2555     const src = inst_data.src();
   2556     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   2557     const pointee_ty = try sema.resolveType(block, src, extra.lhs);
   2558     const ptr = try sema.resolveInst(extra.rhs);
   2559     const target = mod.getTarget();
   2560     const addr_space = target_util.defaultAddressSpace(target, .local);
   2561 
   2562     if (Air.refToIndex(ptr)) |ptr_inst| {
   2563         switch (sema.air_instructions.items(.tag)[ptr_inst]) {
   2564             .inferred_alloc => {
   2565                 const ia1 = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc;
   2566                 const ia2 = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?;
   2567                 // Add the stored instruction to the set we will use to resolve peer types
   2568                 // for the inferred allocation.
   2569                 // This instruction will not make it to codegen; it is only to participate
   2570                 // in the `stored_inst_list` of the `inferred_alloc`.
   2571                 var trash_block = block.makeSubBlock();
   2572                 defer trash_block.instructions.deinit(sema.gpa);
   2573                 const operand = try trash_block.addBitCast(pointee_ty, .void_value);
   2574 
   2575                 const ptr_ty = try mod.ptrType(.{
   2576                     .child = pointee_ty.toIntern(),
   2577                     .flags = .{
   2578                         .alignment = ia1.alignment,
   2579                         .address_space = addr_space,
   2580                     },
   2581                 });
   2582                 const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);
   2583 
   2584                 try ia2.prongs.append(sema.arena, .{
   2585                     .stored_inst = operand,
   2586                     .placeholder = Air.refToIndex(bitcasted_ptr).?,
   2587                 });
   2588 
   2589                 return bitcasted_ptr;
   2590             },
   2591             .inferred_alloc_comptime => {
   2592                 const alignment = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime.alignment;
   2593                 // There will be only one coerce_result_ptr because we are running at comptime.
   2594                 // The alloc will turn into a Decl.
   2595                 var anon_decl = try block.startAnonDecl();
   2596                 defer anon_decl.deinit();
   2597                 const decl_index = try anon_decl.finish(
   2598                     pointee_ty,
   2599                     (try mod.intern(.{ .undef = pointee_ty.toIntern() })).toValue(),
   2600                     alignment,
   2601                 );
   2602                 sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime.decl_index = decl_index;
   2603                 if (alignment != .none) {
   2604                     try sema.resolveTypeLayout(pointee_ty);
   2605                 }
   2606                 const ptr_ty = try mod.ptrType(.{
   2607                     .child = pointee_ty.toIntern(),
   2608                     .flags = .{
   2609                         .alignment = alignment,
   2610                         .address_space = addr_space,
   2611                     },
   2612                 });
   2613                 try sema.maybeQueueFuncBodyAnalysis(decl_index);
   2614                 try sema.comptime_mutable_decls.append(decl_index);
   2615                 return sema.addConstant((try mod.intern(.{ .ptr = .{
   2616                     .ty = ptr_ty.toIntern(),
   2617                     .addr = .{ .mut_decl = .{
   2618                         .decl = decl_index,
   2619                         .runtime_index = block.runtime_index,
   2620                     } },
   2621                 } })).toValue());
   2622             },
   2623             else => {},
   2624         }
   2625     }
   2626 
   2627     // Make a dummy store through the pointer to test the coercion.
   2628     // We will then use the generated instructions to decide what
   2629     // kind of transformations to make on the result pointer.
   2630     var trash_block = block.makeSubBlock();
   2631     trash_block.is_comptime = false;
   2632     defer trash_block.instructions.deinit(sema.gpa);
   2633 
   2634     const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr));
   2635     const dummy_operand = try trash_block.addBitCast(pointee_ty, .void_value);
   2636     return sema.coerceResultPtr(block, src, ptr, dummy_ptr, dummy_operand, &trash_block);
   2637 }
   2638 
   2639 fn coerceResultPtr(
   2640     sema: *Sema,
   2641     block: *Block,
   2642     src: LazySrcLoc,
   2643     ptr: Air.Inst.Ref,
   2644     dummy_ptr: Air.Inst.Ref,
   2645     dummy_operand: Air.Inst.Ref,
   2646     trash_block: *Block,
   2647 ) CompileError!Air.Inst.Ref {
   2648     const mod = sema.mod;
   2649     const target = sema.mod.getTarget();
   2650     const addr_space = target_util.defaultAddressSpace(target, .local);
   2651     const pointee_ty = sema.typeOf(dummy_operand);
   2652     const prev_trash_len = trash_block.instructions.items.len;
   2653 
   2654     try sema.storePtr2(trash_block, src, dummy_ptr, src, dummy_operand, src, .bitcast);
   2655 
   2656     {
   2657         const air_tags = sema.air_instructions.items(.tag);
   2658 
   2659         //std.debug.print("dummy storePtr instructions:\n", .{});
   2660         //for (trash_block.instructions.items) |item| {
   2661         //    std.debug.print("  {s}\n", .{@tagName(air_tags[item])});
   2662         //}
   2663 
   2664         // The last one is always `store`.
   2665         const trash_inst = trash_block.instructions.items[trash_block.instructions.items.len - 1];
   2666         if (air_tags[trash_inst] != .store and air_tags[trash_inst] != .store_safe) {
   2667             // no store instruction is generated for zero sized types
   2668             assert((try sema.typeHasOnePossibleValue(pointee_ty)) != null);
   2669         } else {
   2670             trash_block.instructions.items.len -= 1;
   2671             assert(trash_inst == sema.air_instructions.len - 1);
   2672             sema.air_instructions.len -= 1;
   2673         }
   2674     }
   2675 
   2676     const ptr_ty = try mod.ptrType(.{
   2677         .child = pointee_ty.toIntern(),
   2678         .flags = .{ .address_space = addr_space },
   2679     });
   2680 
   2681     var new_ptr = ptr;
   2682 
   2683     while (true) {
   2684         const air_tags = sema.air_instructions.items(.tag);
   2685         const air_datas = sema.air_instructions.items(.data);
   2686 
   2687         if (trash_block.instructions.items.len == prev_trash_len) {
   2688             if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| {
   2689                 return sema.addConstant(ptr_val);
   2690             }
   2691             if (pointee_ty.eql(Type.null, sema.mod)) {
   2692                 const null_inst = try sema.addConstant(Value.null);
   2693                 _ = try block.addBinOp(.store, new_ptr, null_inst);
   2694                 return Air.Inst.Ref.void_value;
   2695             }
   2696             return sema.bitCast(block, ptr_ty, new_ptr, src, null);
   2697         }
   2698 
   2699         const trash_inst = trash_block.instructions.pop();
   2700 
   2701         switch (air_tags[trash_inst]) {
   2702             // Array coerced to Vector where element size is not equal but coercible.
   2703             .aggregate_init => {
   2704                 const ty_pl = air_datas[trash_inst].ty_pl;
   2705                 const ptr_operand_ty = try mod.ptrType(.{
   2706                     .child = (try sema.analyzeAsType(block, src, ty_pl.ty)).toIntern(),
   2707                     .flags = .{ .address_space = addr_space },
   2708                 });
   2709 
   2710                 if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| {
   2711                     return sema.addConstant(ptr_val);
   2712                 } else {
   2713                     return sema.bitCast(block, ptr_operand_ty, new_ptr, src, null);
   2714                 }
   2715             },
   2716             .bitcast => {
   2717                 const ty_op = air_datas[trash_inst].ty_op;
   2718                 const operand_ty = sema.typeOf(ty_op.operand);
   2719                 const ptr_operand_ty = try mod.ptrType(.{
   2720                     .child = operand_ty.toIntern(),
   2721                     .flags = .{ .address_space = addr_space },
   2722                 });
   2723                 if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| {
   2724                     new_ptr = try sema.addConstant(try mod.getCoerced(ptr_val, ptr_operand_ty));
   2725                 } else {
   2726                     new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src, null);
   2727                 }
   2728             },
   2729             .wrap_optional => {
   2730                 new_ptr = try sema.analyzeOptionalPayloadPtr(block, src, new_ptr, false, true);
   2731             },
   2732             .wrap_errunion_err => {
   2733                 return sema.fail(block, src, "TODO coerce_result_ptr wrap_errunion_err", .{});
   2734             },
   2735             .wrap_errunion_payload => {
   2736                 new_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, new_ptr, false, true);
   2737             },
   2738             .array_to_slice => {
   2739                 return sema.fail(block, src, "TODO coerce_result_ptr array_to_slice", .{});
   2740             },
   2741             .get_union_tag => {
   2742                 return sema.fail(block, src, "TODO coerce_result_ptr get_union_tag", .{});
   2743             },
   2744             else => {
   2745                 if (std.debug.runtime_safety) {
   2746                     std.debug.panic("unexpected AIR tag for coerce_result_ptr: {}", .{
   2747                         air_tags[trash_inst],
   2748                     });
   2749                 } else {
   2750                     unreachable;
   2751                 }
   2752             },
   2753         }
   2754     }
   2755 }
   2756 
   2757 pub fn analyzeStructDecl(
   2758     sema: *Sema,
   2759     new_decl: *Decl,
   2760     inst: Zir.Inst.Index,
   2761     struct_index: Module.Struct.Index,
   2762 ) SemaError!void {
   2763     const mod = sema.mod;
   2764     const struct_obj = mod.structPtr(struct_index);
   2765     const extended = sema.code.instructions.items(.data)[inst].extended;
   2766     assert(extended.opcode == .struct_decl);
   2767     const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small));
   2768 
   2769     struct_obj.known_non_opv = small.known_non_opv;
   2770     if (small.known_comptime_only) {
   2771         struct_obj.requires_comptime = .yes;
   2772     }
   2773 
   2774     var extra_index: usize = extended.operand;
   2775     extra_index += @intFromBool(small.has_src_node);
   2776     extra_index += @intFromBool(small.has_fields_len);
   2777     const decls_len = if (small.has_decls_len) blk: {
   2778         const decls_len = sema.code.extra[extra_index];
   2779         extra_index += 1;
   2780         break :blk decls_len;
   2781     } else 0;
   2782 
   2783     if (small.has_backing_int) {
   2784         const backing_int_body_len = sema.code.extra[extra_index];
   2785         extra_index += 1; // backing_int_body_len
   2786         if (backing_int_body_len == 0) {
   2787             extra_index += 1; // backing_int_ref
   2788         } else {
   2789             extra_index += backing_int_body_len; // backing_int_body_inst
   2790         }
   2791     }
   2792 
   2793     _ = try mod.scanNamespace(struct_obj.namespace, extra_index, decls_len, new_decl);
   2794 }
   2795 
   2796 fn zirStructDecl(
   2797     sema: *Sema,
   2798     block: *Block,
   2799     extended: Zir.Inst.Extended.InstData,
   2800     inst: Zir.Inst.Index,
   2801 ) CompileError!Air.Inst.Ref {
   2802     const mod = sema.mod;
   2803     const gpa = sema.gpa;
   2804     const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small));
   2805     const src: LazySrcLoc = if (small.has_src_node) blk: {
   2806         const node_offset = @as(i32, @bitCast(sema.code.extra[extended.operand]));
   2807         break :blk LazySrcLoc.nodeOffset(node_offset);
   2808     } else sema.src;
   2809 
   2810     // Because these three things each reference each other, `undefined`
   2811     // placeholders are used before being set after the struct type gains an
   2812     // InternPool index.
   2813 
   2814     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
   2815         .ty = Type.noreturn,
   2816         .val = Value.@"unreachable",
   2817     }, small.name_strategy, "struct", inst);
   2818     const new_decl = mod.declPtr(new_decl_index);
   2819     new_decl.owns_tv = true;
   2820     errdefer mod.abortAnonDecl(new_decl_index);
   2821 
   2822     const new_namespace_index = try mod.createNamespace(.{
   2823         .parent = block.namespace.toOptional(),
   2824         .ty = undefined,
   2825         .file_scope = block.getFileScope(mod),
   2826     });
   2827     const new_namespace = mod.namespacePtr(new_namespace_index);
   2828     errdefer mod.destroyNamespace(new_namespace_index);
   2829 
   2830     const struct_index = try mod.createStruct(.{
   2831         .owner_decl = new_decl_index,
   2832         .fields = .{},
   2833         .zir_index = inst,
   2834         .layout = small.layout,
   2835         .status = .none,
   2836         .known_non_opv = undefined,
   2837         .is_tuple = small.is_tuple,
   2838         .namespace = new_namespace_index,
   2839     });
   2840     errdefer mod.destroyStruct(struct_index);
   2841 
   2842     const struct_ty = try mod.intern_pool.get(gpa, .{ .struct_type = .{
   2843         .index = struct_index.toOptional(),
   2844         .namespace = new_namespace_index.toOptional(),
   2845     } });
   2846     // TODO: figure out InternPool removals for incremental compilation
   2847     //errdefer mod.intern_pool.remove(struct_ty);
   2848 
   2849     new_decl.ty = Type.type;
   2850     new_decl.val = struct_ty.toValue();
   2851     new_namespace.ty = struct_ty.toType();
   2852 
   2853     try sema.analyzeStructDecl(new_decl, inst, struct_index);
   2854     const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
   2855     try mod.finalizeAnonDecl(new_decl_index);
   2856     return decl_val;
   2857 }
   2858 
   2859 fn createAnonymousDeclTypeNamed(
   2860     sema: *Sema,
   2861     block: *Block,
   2862     src: LazySrcLoc,
   2863     typed_value: TypedValue,
   2864     name_strategy: Zir.Inst.NameStrategy,
   2865     anon_prefix: []const u8,
   2866     inst: ?Zir.Inst.Index,
   2867 ) !Decl.Index {
   2868     const mod = sema.mod;
   2869     const gpa = sema.gpa;
   2870     const namespace = block.namespace;
   2871     const src_scope = block.wip_capture_scope;
   2872     const src_decl = mod.declPtr(block.src_decl);
   2873     const src_node = src_decl.relativeToNodeIndex(src.node_offset.x);
   2874     const new_decl_index = try mod.allocateNewDecl(namespace, src_node, src_scope);
   2875     errdefer mod.destroyDecl(new_decl_index);
   2876 
   2877     switch (name_strategy) {
   2878         .anon => {
   2879             // It would be neat to have "struct:line:column" but this name has
   2880             // to survive incremental updates, where it may have been shifted down
   2881             // or up to a different line, but unchanged, and thus not unnecessarily
   2882             // semantically analyzed.
   2883             // This name is also used as the key in the parent namespace so it cannot be
   2884             // renamed.
   2885 
   2886             const name = mod.intern_pool.getOrPutStringFmt(gpa, "{}__{s}_{d}", .{
   2887                 src_decl.name.fmt(&mod.intern_pool), anon_prefix, @intFromEnum(new_decl_index),
   2888             }) catch unreachable;
   2889             try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name);
   2890             return new_decl_index;
   2891         },
   2892         .parent => {
   2893             const name = mod.declPtr(block.src_decl).name;
   2894             try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name);
   2895             return new_decl_index;
   2896         },
   2897         .func => {
   2898             const fn_info = sema.code.getFnInfo(sema.func.?.zir_body_inst);
   2899             const zir_tags = sema.code.instructions.items(.tag);
   2900 
   2901             var buf = std.ArrayList(u8).init(gpa);
   2902             defer buf.deinit();
   2903 
   2904             const writer = buf.writer();
   2905             try writer.print("{}(", .{mod.declPtr(block.src_decl).name.fmt(&mod.intern_pool)});
   2906 
   2907             var arg_i: usize = 0;
   2908             for (fn_info.param_body) |zir_inst| switch (zir_tags[zir_inst]) {
   2909                 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
   2910                     const arg = sema.inst_map.get(zir_inst).?;
   2911                     // If this is being called in a generic function then analyzeCall will
   2912                     // have already resolved the args and this will work.
   2913                     // If not then this is a struct type being returned from a non-generic
   2914                     // function and the name doesn't matter since it will later
   2915                     // result in a compile error.
   2916                     const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg, "") catch
   2917                         return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null);
   2918 
   2919                     if (arg_i != 0) try writer.writeByte(',');
   2920                     try writer.print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)});
   2921 
   2922                     arg_i += 1;
   2923                     continue;
   2924                 },
   2925                 else => continue,
   2926             };
   2927 
   2928             try writer.writeByte(')');
   2929             const name = try mod.intern_pool.getOrPutString(gpa, buf.items);
   2930             try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name);
   2931             return new_decl_index;
   2932         },
   2933         .dbg_var => {
   2934             const ref = Zir.indexToRef(inst.?);
   2935             const zir_tags = sema.code.instructions.items(.tag);
   2936             const zir_data = sema.code.instructions.items(.data);
   2937             var i = inst.?;
   2938             while (i < zir_tags.len) : (i += 1) switch (zir_tags[i]) {
   2939                 .dbg_var_ptr, .dbg_var_val => {
   2940                     if (zir_data[i].str_op.operand != ref) continue;
   2941 
   2942                     const name = try mod.intern_pool.getOrPutStringFmt(gpa, "{}.{s}", .{
   2943                         src_decl.name.fmt(&mod.intern_pool), zir_data[i].str_op.getStr(sema.code),
   2944                     });
   2945 
   2946                     try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name);
   2947                     return new_decl_index;
   2948                 },
   2949                 else => {},
   2950             };
   2951             return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null);
   2952         },
   2953     }
   2954 }
   2955 
   2956 fn zirEnumDecl(
   2957     sema: *Sema,
   2958     block: *Block,
   2959     extended: Zir.Inst.Extended.InstData,
   2960     inst: Zir.Inst.Index,
   2961 ) CompileError!Air.Inst.Ref {
   2962     const tracy = trace(@src());
   2963     defer tracy.end();
   2964 
   2965     const mod = sema.mod;
   2966     const gpa = sema.gpa;
   2967     const small = @as(Zir.Inst.EnumDecl.Small, @bitCast(extended.small));
   2968     var extra_index: usize = extended.operand;
   2969 
   2970     const src: LazySrcLoc = if (small.has_src_node) blk: {
   2971         const node_offset = @as(i32, @bitCast(sema.code.extra[extra_index]));
   2972         extra_index += 1;
   2973         break :blk LazySrcLoc.nodeOffset(node_offset);
   2974     } else sema.src;
   2975     const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x };
   2976 
   2977     const tag_type_ref = if (small.has_tag_type) blk: {
   2978         const tag_type_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
   2979         extra_index += 1;
   2980         break :blk tag_type_ref;
   2981     } else .none;
   2982 
   2983     const body_len = if (small.has_body_len) blk: {
   2984         const body_len = sema.code.extra[extra_index];
   2985         extra_index += 1;
   2986         break :blk body_len;
   2987     } else 0;
   2988 
   2989     const fields_len = if (small.has_fields_len) blk: {
   2990         const fields_len = sema.code.extra[extra_index];
   2991         extra_index += 1;
   2992         break :blk fields_len;
   2993     } else 0;
   2994 
   2995     const decls_len = if (small.has_decls_len) blk: {
   2996         const decls_len = sema.code.extra[extra_index];
   2997         extra_index += 1;
   2998         break :blk decls_len;
   2999     } else 0;
   3000 
   3001     // Because these three things each reference each other, `undefined`
   3002     // placeholders are used before being set after the enum type gains an
   3003     // InternPool index.
   3004 
   3005     var done = false;
   3006     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
   3007         .ty = Type.noreturn,
   3008         .val = Value.@"unreachable",
   3009     }, small.name_strategy, "enum", inst);
   3010     const new_decl = mod.declPtr(new_decl_index);
   3011     new_decl.owns_tv = true;
   3012     errdefer if (!done) mod.abortAnonDecl(new_decl_index);
   3013 
   3014     const new_namespace_index = try mod.createNamespace(.{
   3015         .parent = block.namespace.toOptional(),
   3016         .ty = undefined,
   3017         .file_scope = block.getFileScope(mod),
   3018     });
   3019     const new_namespace = mod.namespacePtr(new_namespace_index);
   3020     errdefer if (!done) mod.destroyNamespace(new_namespace_index);
   3021 
   3022     extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl);
   3023 
   3024     const body = sema.code.extra[extra_index..][0..body_len];
   3025     extra_index += body.len;
   3026 
   3027     const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
   3028     const body_end = extra_index;
   3029     extra_index += bit_bags_count;
   3030 
   3031     const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| {
   3032         if (bag != 0) break true;
   3033     } else false;
   3034 
   3035     const incomplete_enum = try mod.intern_pool.getIncompleteEnum(gpa, .{
   3036         .decl = new_decl_index,
   3037         .namespace = new_namespace_index.toOptional(),
   3038         .fields_len = fields_len,
   3039         .has_values = any_values,
   3040         .tag_mode = if (small.nonexhaustive)
   3041             .nonexhaustive
   3042         else if (tag_type_ref == .none)
   3043             .auto
   3044         else
   3045             .explicit,
   3046     });
   3047     // TODO: figure out InternPool removals for incremental compilation
   3048     //errdefer if (!done) mod.intern_pool.remove(incomplete_enum.index);
   3049 
   3050     new_decl.ty = Type.type;
   3051     new_decl.val = incomplete_enum.index.toValue();
   3052     new_namespace.ty = incomplete_enum.index.toType();
   3053 
   3054     const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index);
   3055     try mod.finalizeAnonDecl(new_decl_index);
   3056     done = true;
   3057 
   3058     const int_tag_ty = ty: {
   3059         // We create a block for the field type instructions because they
   3060         // may need to reference Decls from inside the enum namespace.
   3061         // Within the field type, default value, and alignment expressions, the "owner decl"
   3062         // should be the enum itself.
   3063 
   3064         const prev_owner_decl = sema.owner_decl;
   3065         const prev_owner_decl_index = sema.owner_decl_index;
   3066         sema.owner_decl = new_decl;
   3067         sema.owner_decl_index = new_decl_index;
   3068         defer {
   3069             sema.owner_decl = prev_owner_decl;
   3070             sema.owner_decl_index = prev_owner_decl_index;
   3071         }
   3072 
   3073         const prev_owner_func = sema.owner_func;
   3074         const prev_owner_func_index = sema.owner_func_index;
   3075         sema.owner_func = null;
   3076         sema.owner_func_index = .none;
   3077         defer sema.owner_func = prev_owner_func;
   3078         defer sema.owner_func_index = prev_owner_func_index;
   3079 
   3080         const prev_func = sema.func;
   3081         const prev_func_index = sema.func_index;
   3082         sema.func = null;
   3083         sema.func_index = .none;
   3084         defer sema.func = prev_func;
   3085         defer sema.func_index = prev_func_index;
   3086 
   3087         var wip_captures = try WipCaptureScope.init(gpa, new_decl.src_scope);
   3088         defer wip_captures.deinit();
   3089 
   3090         var enum_block: Block = .{
   3091             .parent = null,
   3092             .sema = sema,
   3093             .src_decl = new_decl_index,
   3094             .namespace = new_namespace_index,
   3095             .wip_capture_scope = wip_captures.scope,
   3096             .instructions = .{},
   3097             .inlining = null,
   3098             .is_comptime = true,
   3099         };
   3100         defer enum_block.instructions.deinit(sema.gpa);
   3101 
   3102         if (body.len != 0) {
   3103             try sema.analyzeBody(&enum_block, body);
   3104         }
   3105 
   3106         try wip_captures.finalize();
   3107 
   3108         if (tag_type_ref != .none) {
   3109             const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref);
   3110             if (ty.zigTypeTag(mod) != .Int and ty.zigTypeTag(mod) != .ComptimeInt) {
   3111                 return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)});
   3112             }
   3113             incomplete_enum.setTagType(&mod.intern_pool, ty.toIntern());
   3114             break :ty ty;
   3115         } else if (fields_len == 0) {
   3116             break :ty try mod.intType(.unsigned, 0);
   3117         } else {
   3118             const bits = std.math.log2_int_ceil(usize, fields_len);
   3119             break :ty try mod.intType(.unsigned, bits);
   3120         }
   3121     };
   3122 
   3123     if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
   3124         if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(mod)) {
   3125             return sema.fail(block, src, "non-exhaustive enum specifies every value", .{});
   3126         }
   3127     }
   3128 
   3129     var bit_bag_index: usize = body_end;
   3130     var cur_bit_bag: u32 = undefined;
   3131     var field_i: u32 = 0;
   3132     var last_tag_val: ?Value = null;
   3133     while (field_i < fields_len) : (field_i += 1) {
   3134         if (field_i % 32 == 0) {
   3135             cur_bit_bag = sema.code.extra[bit_bag_index];
   3136             bit_bag_index += 1;
   3137         }
   3138         const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0;
   3139         cur_bit_bag >>= 1;
   3140 
   3141         const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
   3142         extra_index += 1;
   3143 
   3144         // doc comment
   3145         extra_index += 1;
   3146 
   3147         const field_name = try mod.intern_pool.getOrPutString(gpa, field_name_zir);
   3148         if (try incomplete_enum.addFieldName(&mod.intern_pool, gpa, field_name)) |other_index| {
   3149             const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy;
   3150             const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
   3151             const msg = msg: {
   3152                 const msg = try sema.errMsg(block, field_src, "duplicate enum field '{s}'", .{field_name_zir});
   3153                 errdefer msg.destroy(gpa);
   3154                 try sema.errNote(block, other_field_src, msg, "other field here", .{});
   3155                 break :msg msg;
   3156             };
   3157             return sema.failWithOwnedErrorMsg(msg);
   3158         }
   3159 
   3160         const tag_overflow = if (has_tag_value) overflow: {
   3161             const tag_val_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
   3162             extra_index += 1;
   3163             const tag_inst = try sema.resolveInst(tag_val_ref);
   3164             last_tag_val = sema.resolveConstValue(block, .unneeded, tag_inst, "") catch |err| switch (err) {
   3165                 error.NeededSourceLocation => {
   3166                     const value_src = mod.fieldSrcLoc(new_decl_index, .{
   3167                         .index = field_i,
   3168                         .range = .value,
   3169                     }).lazy;
   3170                     _ = try sema.resolveConstValue(block, value_src, tag_inst, "enum tag value must be comptime-known");
   3171                     unreachable;
   3172                 },
   3173                 else => |e| return e,
   3174             };
   3175             if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
   3176             last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty);
   3177             if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, last_tag_val.?.toIntern())) |other_index| {
   3178                 const value_src = mod.fieldSrcLoc(new_decl_index, .{
   3179                     .index = field_i,
   3180                     .range = .value,
   3181                 }).lazy;
   3182                 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
   3183                 const msg = msg: {
   3184                     const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)});
   3185                     errdefer msg.destroy(gpa);
   3186                     try sema.errNote(block, other_field_src, msg, "other occurrence here", .{});
   3187                     break :msg msg;
   3188                 };
   3189                 return sema.failWithOwnedErrorMsg(msg);
   3190             }
   3191             break :overflow false;
   3192         } else if (any_values) overflow: {
   3193             var overflow: ?usize = null;
   3194             last_tag_val = if (last_tag_val) |val|
   3195                 try sema.intAdd(val, try mod.intValue(int_tag_ty, 1), int_tag_ty, &overflow)
   3196             else
   3197                 try mod.intValue(int_tag_ty, 0);
   3198             if (overflow != null) break :overflow true;
   3199             if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, last_tag_val.?.toIntern())) |other_index| {
   3200                 const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy;
   3201                 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
   3202                 const msg = msg: {
   3203                     const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)});
   3204                     errdefer msg.destroy(gpa);
   3205                     try sema.errNote(block, other_field_src, msg, "other occurrence here", .{});
   3206                     break :msg msg;
   3207                 };
   3208                 return sema.failWithOwnedErrorMsg(msg);
   3209             }
   3210             break :overflow false;
   3211         } else overflow: {
   3212             last_tag_val = try mod.intValue(Type.comptime_int, field_i);
   3213             if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true;
   3214             last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty);
   3215             break :overflow false;
   3216         };
   3217 
   3218         if (tag_overflow) {
   3219             const value_src = mod.fieldSrcLoc(new_decl_index, .{
   3220                 .index = field_i,
   3221                 .range = if (has_tag_value) .value else .name,
   3222             }).lazy;
   3223             const msg = try sema.errMsg(block, value_src, "enumeration value '{}' too large for type '{}'", .{
   3224                 last_tag_val.?.fmtValue(int_tag_ty, mod), int_tag_ty.fmt(mod),
   3225             });
   3226             return sema.failWithOwnedErrorMsg(msg);
   3227         }
   3228     }
   3229     return decl_val;
   3230 }
   3231 
   3232 fn zirUnionDecl(
   3233     sema: *Sema,
   3234     block: *Block,
   3235     extended: Zir.Inst.Extended.InstData,
   3236     inst: Zir.Inst.Index,
   3237 ) CompileError!Air.Inst.Ref {
   3238     const tracy = trace(@src());
   3239     defer tracy.end();
   3240 
   3241     const mod = sema.mod;
   3242     const gpa = sema.gpa;
   3243     const small = @as(Zir.Inst.UnionDecl.Small, @bitCast(extended.small));
   3244     var extra_index: usize = extended.operand;
   3245 
   3246     const src: LazySrcLoc = if (small.has_src_node) blk: {
   3247         const node_offset = @as(i32, @bitCast(sema.code.extra[extra_index]));
   3248         extra_index += 1;
   3249         break :blk LazySrcLoc.nodeOffset(node_offset);
   3250     } else sema.src;
   3251 
   3252     extra_index += @intFromBool(small.has_tag_type);
   3253     extra_index += @intFromBool(small.has_body_len);
   3254     extra_index += @intFromBool(small.has_fields_len);
   3255 
   3256     const decls_len = if (small.has_decls_len) blk: {
   3257         const decls_len = sema.code.extra[extra_index];
   3258         extra_index += 1;
   3259         break :blk decls_len;
   3260     } else 0;
   3261 
   3262     // Because these three things each reference each other, `undefined`
   3263     // placeholders are used before being set after the union type gains an
   3264     // InternPool index.
   3265 
   3266     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
   3267         .ty = Type.noreturn,
   3268         .val = Value.@"unreachable",
   3269     }, small.name_strategy, "union", inst);
   3270     const new_decl = mod.declPtr(new_decl_index);
   3271     new_decl.owns_tv = true;
   3272     errdefer mod.abortAnonDecl(new_decl_index);
   3273 
   3274     const new_namespace_index = try mod.createNamespace(.{
   3275         .parent = block.namespace.toOptional(),
   3276         .ty = undefined,
   3277         .file_scope = block.getFileScope(mod),
   3278     });
   3279     const new_namespace = mod.namespacePtr(new_namespace_index);
   3280     errdefer mod.destroyNamespace(new_namespace_index);
   3281 
   3282     const union_index = try mod.createUnion(.{
   3283         .owner_decl = new_decl_index,
   3284         .tag_ty = Type.null,
   3285         .fields = .{},
   3286         .zir_index = inst,
   3287         .layout = small.layout,
   3288         .status = .none,
   3289         .namespace = new_namespace_index,
   3290     });
   3291     errdefer mod.destroyUnion(union_index);
   3292 
   3293     const union_ty = try mod.intern_pool.get(gpa, .{ .union_type = .{
   3294         .index = union_index,
   3295         .runtime_tag = if (small.has_tag_type or small.auto_enum_tag)
   3296             .tagged
   3297         else if (small.layout != .Auto)
   3298             .none
   3299         else switch (block.sema.mod.optimizeMode()) {
   3300             .Debug, .ReleaseSafe => .safety,
   3301             .ReleaseFast, .ReleaseSmall => .none,
   3302         },
   3303     } });
   3304     // TODO: figure out InternPool removals for incremental compilation
   3305     //errdefer mod.intern_pool.remove(union_ty);
   3306 
   3307     new_decl.ty = Type.type;
   3308     new_decl.val = union_ty.toValue();
   3309     new_namespace.ty = union_ty.toType();
   3310 
   3311     _ = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl);
   3312 
   3313     const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
   3314     try mod.finalizeAnonDecl(new_decl_index);
   3315     return decl_val;
   3316 }
   3317 
   3318 fn zirOpaqueDecl(
   3319     sema: *Sema,
   3320     block: *Block,
   3321     extended: Zir.Inst.Extended.InstData,
   3322     inst: Zir.Inst.Index,
   3323 ) CompileError!Air.Inst.Ref {
   3324     const tracy = trace(@src());
   3325     defer tracy.end();
   3326 
   3327     const mod = sema.mod;
   3328     const small = @as(Zir.Inst.OpaqueDecl.Small, @bitCast(extended.small));
   3329     var extra_index: usize = extended.operand;
   3330 
   3331     const src: LazySrcLoc = if (small.has_src_node) blk: {
   3332         const node_offset = @as(i32, @bitCast(sema.code.extra[extra_index]));
   3333         extra_index += 1;
   3334         break :blk LazySrcLoc.nodeOffset(node_offset);
   3335     } else sema.src;
   3336 
   3337     const decls_len = if (small.has_decls_len) blk: {
   3338         const decls_len = sema.code.extra[extra_index];
   3339         extra_index += 1;
   3340         break :blk decls_len;
   3341     } else 0;
   3342 
   3343     // Because these three things each reference each other, `undefined`
   3344     // placeholders are used in two places before being set after the opaque
   3345     // type gains an InternPool index.
   3346 
   3347     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
   3348         .ty = Type.noreturn,
   3349         .val = Value.@"unreachable",
   3350     }, small.name_strategy, "opaque", inst);
   3351     const new_decl = mod.declPtr(new_decl_index);
   3352     new_decl.owns_tv = true;
   3353     errdefer mod.abortAnonDecl(new_decl_index);
   3354 
   3355     const new_namespace_index = try mod.createNamespace(.{
   3356         .parent = block.namespace.toOptional(),
   3357         .ty = undefined,
   3358         .file_scope = block.getFileScope(mod),
   3359     });
   3360     const new_namespace = mod.namespacePtr(new_namespace_index);
   3361     errdefer mod.destroyNamespace(new_namespace_index);
   3362 
   3363     const opaque_ty = try mod.intern(.{ .opaque_type = .{
   3364         .decl = new_decl_index,
   3365         .namespace = new_namespace_index,
   3366     } });
   3367     // TODO: figure out InternPool removals for incremental compilation
   3368     //errdefer mod.intern_pool.remove(opaque_ty);
   3369 
   3370     new_decl.ty = Type.type;
   3371     new_decl.val = opaque_ty.toValue();
   3372     new_namespace.ty = opaque_ty.toType();
   3373 
   3374     extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl);
   3375 
   3376     const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
   3377     try mod.finalizeAnonDecl(new_decl_index);
   3378     return decl_val;
   3379 }
   3380 
   3381 fn zirErrorSetDecl(
   3382     sema: *Sema,
   3383     block: *Block,
   3384     inst: Zir.Inst.Index,
   3385     name_strategy: Zir.Inst.NameStrategy,
   3386 ) CompileError!Air.Inst.Ref {
   3387     const tracy = trace(@src());
   3388     defer tracy.end();
   3389 
   3390     const mod = sema.mod;
   3391     const gpa = sema.gpa;
   3392     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   3393     const src = inst_data.src();
   3394     const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index);
   3395 
   3396     var names: Module.Fn.InferredErrorSet.NameMap = .{};
   3397     try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len);
   3398 
   3399     var extra_index = @as(u32, @intCast(extra.end));
   3400     const extra_index_end = extra_index + (extra.data.fields_len * 2);
   3401     while (extra_index < extra_index_end) : (extra_index += 2) { // +2 to skip over doc_string
   3402         const str_index = sema.code.extra[extra_index];
   3403         const name = sema.code.nullTerminatedString(str_index);
   3404         const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
   3405         _ = try mod.getErrorValue(name_ip);
   3406         const result = names.getOrPutAssumeCapacity(name_ip);
   3407         assert(!result.found_existing); // verified in AstGen
   3408     }
   3409 
   3410     const error_set_ty = try mod.errorSetFromUnsortedNames(names.keys());
   3411 
   3412     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
   3413         .ty = Type.type,
   3414         .val = error_set_ty.toValue(),
   3415     }, name_strategy, "error", inst);
   3416     const new_decl = mod.declPtr(new_decl_index);
   3417     new_decl.owns_tv = true;
   3418     errdefer mod.abortAnonDecl(new_decl_index);
   3419 
   3420     const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
   3421     try mod.finalizeAnonDecl(new_decl_index);
   3422     return decl_val;
   3423 }
   3424 
   3425 fn zirRetPtr(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
   3426     const tracy = trace(@src());
   3427     defer tracy.end();
   3428 
   3429     if (block.is_comptime or try sema.typeRequiresComptime(sema.fn_ret_ty)) {
   3430         const fn_ret_ty = try sema.resolveTypeFields(sema.fn_ret_ty);
   3431         return sema.analyzeComptimeAlloc(block, fn_ret_ty, .none);
   3432     }
   3433 
   3434     const target = sema.mod.getTarget();
   3435     const ptr_type = try sema.mod.ptrType(.{
   3436         .child = sema.fn_ret_ty.toIntern(),
   3437         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   3438     });
   3439 
   3440     if (block.inlining != null) {
   3441         // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr.
   3442         // TODO when functions gain result location support, the inlining struct in
   3443         // Block should contain the return pointer, and we would pass that through here.
   3444         return block.addTy(.alloc, ptr_type);
   3445     }
   3446 
   3447     return block.addTy(.ret_ptr, ptr_type);
   3448 }
   3449 
   3450 fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3451     const tracy = trace(@src());
   3452     defer tracy.end();
   3453 
   3454     const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
   3455     const operand = try sema.resolveInst(inst_data.operand);
   3456     return sema.analyzeRef(block, inst_data.src(), operand);
   3457 }
   3458 
   3459 fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3460     const tracy = trace(@src());
   3461     defer tracy.end();
   3462 
   3463     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   3464     const operand = try sema.resolveInst(inst_data.operand);
   3465     const src = inst_data.src();
   3466 
   3467     return sema.ensureResultUsed(block, sema.typeOf(operand), src);
   3468 }
   3469 
   3470 fn ensureResultUsed(
   3471     sema: *Sema,
   3472     block: *Block,
   3473     ty: Type,
   3474     src: LazySrcLoc,
   3475 ) CompileError!void {
   3476     const mod = sema.mod;
   3477     switch (ty.zigTypeTag(mod)) {
   3478         .Void, .NoReturn => return,
   3479         .ErrorSet, .ErrorUnion => {
   3480             const msg = msg: {
   3481                 const msg = try sema.errMsg(block, src, "error is ignored", .{});
   3482                 errdefer msg.destroy(sema.gpa);
   3483                 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{});
   3484                 break :msg msg;
   3485             };
   3486             return sema.failWithOwnedErrorMsg(msg);
   3487         },
   3488         else => {
   3489             const msg = msg: {
   3490                 const msg = try sema.errMsg(block, src, "value of type '{}' ignored", .{ty.fmt(sema.mod)});
   3491                 errdefer msg.destroy(sema.gpa);
   3492                 try sema.errNote(block, src, msg, "all non-void values must be used", .{});
   3493                 try sema.errNote(block, src, msg, "this error can be suppressed by assigning the value to '_'", .{});
   3494                 break :msg msg;
   3495             };
   3496             return sema.failWithOwnedErrorMsg(msg);
   3497         },
   3498     }
   3499 }
   3500 
   3501 fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3502     const tracy = trace(@src());
   3503     defer tracy.end();
   3504 
   3505     const mod = sema.mod;
   3506     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   3507     const operand = try sema.resolveInst(inst_data.operand);
   3508     const src = inst_data.src();
   3509     const operand_ty = sema.typeOf(operand);
   3510     switch (operand_ty.zigTypeTag(mod)) {
   3511         .ErrorSet, .ErrorUnion => {
   3512             const msg = msg: {
   3513                 const msg = try sema.errMsg(block, src, "error is discarded", .{});
   3514                 errdefer msg.destroy(sema.gpa);
   3515                 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{});
   3516                 break :msg msg;
   3517             };
   3518             return sema.failWithOwnedErrorMsg(msg);
   3519         },
   3520         else => return,
   3521     }
   3522 }
   3523 
   3524 fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3525     const tracy = trace(@src());
   3526     defer tracy.end();
   3527 
   3528     const mod = sema.mod;
   3529     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   3530     const src = inst_data.src();
   3531     const operand = try sema.resolveInst(inst_data.operand);
   3532     const operand_ty = sema.typeOf(operand);
   3533     const err_union_ty = if (operand_ty.zigTypeTag(mod) == .Pointer)
   3534         operand_ty.childType(mod)
   3535     else
   3536         operand_ty;
   3537     if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) return;
   3538     const payload_ty = err_union_ty.errorUnionPayload(mod).zigTypeTag(mod);
   3539     if (payload_ty != .Void and payload_ty != .NoReturn) {
   3540         const msg = msg: {
   3541             const msg = try sema.errMsg(block, src, "error union payload is ignored", .{});
   3542             errdefer msg.destroy(sema.gpa);
   3543             try sema.errNote(block, src, msg, "payload value can be explicitly ignored with '|_|'", .{});
   3544             break :msg msg;
   3545         };
   3546         return sema.failWithOwnedErrorMsg(msg);
   3547     }
   3548 }
   3549 
   3550 fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3551     const tracy = trace(@src());
   3552     defer tracy.end();
   3553 
   3554     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   3555     const src = inst_data.src();
   3556     const object = try sema.resolveInst(inst_data.operand);
   3557 
   3558     return indexablePtrLen(sema, block, src, object);
   3559 }
   3560 
   3561 fn indexablePtrLen(
   3562     sema: *Sema,
   3563     block: *Block,
   3564     src: LazySrcLoc,
   3565     object: Air.Inst.Ref,
   3566 ) CompileError!Air.Inst.Ref {
   3567     const mod = sema.mod;
   3568     const object_ty = sema.typeOf(object);
   3569     const is_pointer_to = object_ty.isSinglePointer(mod);
   3570     const indexable_ty = if (is_pointer_to) object_ty.childType(mod) else object_ty;
   3571     try checkIndexable(sema, block, src, indexable_ty);
   3572     const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len");
   3573     return sema.fieldVal(block, src, object, field_name, src);
   3574 }
   3575 
   3576 fn indexablePtrLenOrNone(
   3577     sema: *Sema,
   3578     block: *Block,
   3579     src: LazySrcLoc,
   3580     operand: Air.Inst.Ref,
   3581 ) CompileError!Air.Inst.Ref {
   3582     const mod = sema.mod;
   3583     const operand_ty = sema.typeOf(operand);
   3584     try checkMemOperand(sema, block, src, operand_ty);
   3585     if (operand_ty.ptrSize(mod) == .Many) return .none;
   3586     const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len");
   3587     return sema.fieldVal(block, src, operand, field_name, src);
   3588 }
   3589 
   3590 fn zirAllocExtended(
   3591     sema: *Sema,
   3592     block: *Block,
   3593     extended: Zir.Inst.Extended.InstData,
   3594 ) CompileError!Air.Inst.Ref {
   3595     const gpa = sema.gpa;
   3596     const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
   3597     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = extra.data.src_node };
   3598     const align_src: LazySrcLoc = .{ .node_offset_var_decl_align = extra.data.src_node };
   3599     const small = @as(Zir.Inst.AllocExtended.Small, @bitCast(extended.small));
   3600 
   3601     var extra_index: usize = extra.end;
   3602 
   3603     const var_ty: Type = if (small.has_type) blk: {
   3604         const type_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
   3605         extra_index += 1;
   3606         break :blk try sema.resolveType(block, ty_src, type_ref);
   3607     } else undefined;
   3608 
   3609     const alignment = if (small.has_align) blk: {
   3610         const align_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
   3611         extra_index += 1;
   3612         const alignment = try sema.resolveAlign(block, align_src, align_ref);
   3613         break :blk alignment;
   3614     } else .none;
   3615 
   3616     if (block.is_comptime or small.is_comptime) {
   3617         if (small.has_type) {
   3618             return sema.analyzeComptimeAlloc(block, var_ty, alignment);
   3619         } else {
   3620             try sema.air_instructions.append(gpa, .{
   3621                 .tag = .inferred_alloc_comptime,
   3622                 .data = .{ .inferred_alloc_comptime = .{
   3623                     .decl_index = undefined,
   3624                     .alignment = alignment,
   3625                     .is_const = small.is_const,
   3626                 } },
   3627             });
   3628             return Air.indexToRef(@as(u32, @intCast(sema.air_instructions.len - 1)));
   3629         }
   3630     }
   3631 
   3632     if (small.has_type) {
   3633         if (!small.is_const) {
   3634             try sema.validateVarType(block, ty_src, var_ty, false);
   3635         }
   3636         const target = sema.mod.getTarget();
   3637         try sema.resolveTypeLayout(var_ty);
   3638         const ptr_type = try sema.mod.ptrType(.{
   3639             .child = var_ty.toIntern(),
   3640             .flags = .{
   3641                 .alignment = alignment,
   3642                 .address_space = target_util.defaultAddressSpace(target, .local),
   3643             },
   3644         });
   3645         return block.addTy(.alloc, ptr_type);
   3646     }
   3647 
   3648     const result_index = try block.addInstAsIndex(.{
   3649         .tag = .inferred_alloc,
   3650         .data = .{ .inferred_alloc = .{
   3651             .alignment = alignment,
   3652             .is_const = small.is_const,
   3653         } },
   3654     });
   3655     try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{});
   3656     return Air.indexToRef(result_index);
   3657 }
   3658 
   3659 fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3660     const tracy = trace(@src());
   3661     defer tracy.end();
   3662 
   3663     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   3664     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
   3665     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   3666     return sema.analyzeComptimeAlloc(block, var_ty, .none);
   3667 }
   3668 
   3669 fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3670     const mod = sema.mod;
   3671     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   3672     const alloc = try sema.resolveInst(inst_data.operand);
   3673     const alloc_ty = sema.typeOf(alloc);
   3674 
   3675     var ptr_info = alloc_ty.ptrInfo(mod);
   3676     const elem_ty = ptr_info.child.toType();
   3677 
   3678     // Detect if all stores to an `.alloc` were comptime-known.
   3679     ct: {
   3680         var search_index: usize = block.instructions.items.len;
   3681         const air_tags = sema.air_instructions.items(.tag);
   3682         const air_datas = sema.air_instructions.items(.data);
   3683 
   3684         const store_inst = while (true) {
   3685             if (search_index == 0) break :ct;
   3686             search_index -= 1;
   3687 
   3688             const candidate = block.instructions.items[search_index];
   3689             switch (air_tags[candidate]) {
   3690                 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
   3691                 .store, .store_safe => break candidate,
   3692                 else => break :ct,
   3693             }
   3694         };
   3695 
   3696         while (true) {
   3697             if (search_index == 0) break :ct;
   3698             search_index -= 1;
   3699 
   3700             const candidate = block.instructions.items[search_index];
   3701             switch (air_tags[candidate]) {
   3702                 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
   3703                 .alloc => {
   3704                     if (Air.indexToRef(candidate) != alloc) break :ct;
   3705                     break;
   3706                 },
   3707                 else => break :ct,
   3708             }
   3709         }
   3710 
   3711         const store_op = air_datas[store_inst].bin_op;
   3712         const store_val = (try sema.resolveMaybeUndefVal(store_op.rhs)) orelse break :ct;
   3713         if (store_op.lhs != alloc) break :ct;
   3714 
   3715         // Remove all the unnecessary runtime instructions.
   3716         block.instructions.shrinkRetainingCapacity(search_index);
   3717 
   3718         var anon_decl = try block.startAnonDecl();
   3719         defer anon_decl.deinit();
   3720         return sema.analyzeDeclRef(try anon_decl.finish(elem_ty, store_val, ptr_info.flags.alignment));
   3721     }
   3722 
   3723     return sema.makePtrConst(block, alloc);
   3724 }
   3725 
   3726 fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref {
   3727     const mod = sema.mod;
   3728     const alloc_ty = sema.typeOf(alloc);
   3729 
   3730     var ptr_info = alloc_ty.ptrInfo(mod);
   3731     ptr_info.flags.is_const = true;
   3732     const const_ptr_ty = try mod.ptrType(ptr_info);
   3733 
   3734     // Detect if a comptime value simply needs to have its type changed.
   3735     if (try sema.resolveMaybeUndefVal(alloc)) |val| {
   3736         return sema.addConstant(try mod.getCoerced(val, const_ptr_ty));
   3737     }
   3738 
   3739     return block.addBitCast(const_ptr_ty, alloc);
   3740 }
   3741 
   3742 fn zirAllocInferredComptime(
   3743     sema: *Sema,
   3744     inst: Zir.Inst.Index,
   3745     is_const: bool,
   3746 ) CompileError!Air.Inst.Ref {
   3747     const gpa = sema.gpa;
   3748     const src_node = sema.code.instructions.items(.data)[inst].node;
   3749     const src = LazySrcLoc.nodeOffset(src_node);
   3750     sema.src = src;
   3751 
   3752     try sema.air_instructions.append(gpa, .{
   3753         .tag = .inferred_alloc_comptime,
   3754         .data = .{ .inferred_alloc_comptime = .{
   3755             .decl_index = undefined,
   3756             .alignment = .none,
   3757             .is_const = is_const,
   3758         } },
   3759     });
   3760     return Air.indexToRef(@as(u32, @intCast(sema.air_instructions.len - 1)));
   3761 }
   3762 
   3763 fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3764     const tracy = trace(@src());
   3765     defer tracy.end();
   3766 
   3767     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   3768     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
   3769     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   3770     if (block.is_comptime) {
   3771         return sema.analyzeComptimeAlloc(block, var_ty, .none);
   3772     }
   3773     const target = sema.mod.getTarget();
   3774     const ptr_type = try sema.mod.ptrType(.{
   3775         .child = var_ty.toIntern(),
   3776         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   3777     });
   3778     try sema.queueFullTypeResolution(var_ty);
   3779     return block.addTy(.alloc, ptr_type);
   3780 }
   3781 
   3782 fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3783     const tracy = trace(@src());
   3784     defer tracy.end();
   3785 
   3786     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   3787     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
   3788     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   3789     if (block.is_comptime) {
   3790         return sema.analyzeComptimeAlloc(block, var_ty, .none);
   3791     }
   3792     try sema.validateVarType(block, ty_src, var_ty, false);
   3793     const target = sema.mod.getTarget();
   3794     const ptr_type = try sema.mod.ptrType(.{
   3795         .child = var_ty.toIntern(),
   3796         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   3797     });
   3798     try sema.queueFullTypeResolution(var_ty);
   3799     return block.addTy(.alloc, ptr_type);
   3800 }
   3801 
   3802 fn zirAllocInferred(
   3803     sema: *Sema,
   3804     block: *Block,
   3805     inst: Zir.Inst.Index,
   3806     is_const: bool,
   3807 ) CompileError!Air.Inst.Ref {
   3808     const tracy = trace(@src());
   3809     defer tracy.end();
   3810 
   3811     const gpa = sema.gpa;
   3812     const src_node = sema.code.instructions.items(.data)[inst].node;
   3813     const src = LazySrcLoc.nodeOffset(src_node);
   3814     sema.src = src;
   3815 
   3816     if (block.is_comptime) {
   3817         try sema.air_instructions.append(gpa, .{
   3818             .tag = .inferred_alloc_comptime,
   3819             .data = .{ .inferred_alloc_comptime = .{
   3820                 .decl_index = undefined,
   3821                 .alignment = .none,
   3822                 .is_const = is_const,
   3823             } },
   3824         });
   3825         return Air.indexToRef(@as(u32, @intCast(sema.air_instructions.len - 1)));
   3826     }
   3827 
   3828     const result_index = try block.addInstAsIndex(.{
   3829         .tag = .inferred_alloc,
   3830         .data = .{ .inferred_alloc = .{
   3831             .alignment = .none,
   3832             .is_const = is_const,
   3833         } },
   3834     });
   3835     try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{});
   3836     return Air.indexToRef(result_index);
   3837 }
   3838 
   3839 fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3840     const tracy = trace(@src());
   3841     defer tracy.end();
   3842 
   3843     const mod = sema.mod;
   3844     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   3845     const src = inst_data.src();
   3846     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
   3847     const ptr = try sema.resolveInst(inst_data.operand);
   3848     const ptr_inst = Air.refToIndex(ptr).?;
   3849     const target = mod.getTarget();
   3850 
   3851     switch (sema.air_instructions.items(.tag)[ptr_inst]) {
   3852         .inferred_alloc_comptime => {
   3853             const iac = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime;
   3854             const decl_index = iac.decl_index;
   3855             try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
   3856 
   3857             const decl = mod.declPtr(decl_index);
   3858             if (iac.is_const) try decl.intern(mod);
   3859             const final_elem_ty = decl.ty;
   3860             const final_ptr_ty = try mod.ptrType(.{
   3861                 .child = final_elem_ty.toIntern(),
   3862                 .flags = .{
   3863                     .is_const = false,
   3864                     .alignment = iac.alignment,
   3865                     .address_space = target_util.defaultAddressSpace(target, .local),
   3866                 },
   3867             });
   3868 
   3869             if (std.debug.runtime_safety) {
   3870                 // The inferred_alloc_comptime should never be referenced again
   3871                 sema.air_instructions.set(ptr_inst, .{ .tag = undefined, .data = undefined });
   3872             }
   3873 
   3874             try sema.maybeQueueFuncBodyAnalysis(decl_index);
   3875 
   3876             const interned = try mod.intern(.{ .ptr = .{
   3877                 .ty = final_ptr_ty.toIntern(),
   3878                 .addr = if (!iac.is_const) .{ .mut_decl = .{
   3879                     .decl = decl_index,
   3880                     .runtime_index = block.runtime_index,
   3881                 } } else .{ .decl = decl_index },
   3882             } });
   3883 
   3884             // Remap the ZIR operand to the resolved pointer value
   3885             sema.inst_map.putAssumeCapacity(Zir.refToIndex(inst_data.operand).?, Air.internedToRef(interned));
   3886         },
   3887         .inferred_alloc => {
   3888             const ia1 = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc;
   3889             const ia2 = sema.unresolved_inferred_allocs.fetchRemove(ptr_inst).?.value;
   3890             const peer_inst_list = ia2.prongs.items(.stored_inst);
   3891             const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
   3892 
   3893             const final_ptr_ty = try mod.ptrType(.{
   3894                 .child = final_elem_ty.toIntern(),
   3895                 .flags = .{
   3896                     .alignment = ia1.alignment,
   3897                     .address_space = target_util.defaultAddressSpace(target, .local),
   3898                 },
   3899             });
   3900 
   3901             if (!ia1.is_const) {
   3902                 try sema.validateVarType(block, ty_src, final_elem_ty, false);
   3903             } else ct: {
   3904                 // Detect if the value is comptime-known. In such case, the
   3905                 // last 3 AIR instructions of the block will look like this:
   3906                 //
   3907                 //   %a = inferred_alloc
   3908                 //   %b = bitcast(%a)
   3909                 //   %c = store(%b, %d)
   3910                 //
   3911                 // If `%d` is comptime-known, then we want to store the value
   3912                 // inside an anonymous Decl and then erase these three AIR
   3913                 // instructions from the block, replacing the inst_map entry
   3914                 // corresponding to the ZIR alloc instruction with a constant
   3915                 // decl_ref pointing at our new Decl.
   3916                 // dbg_stmt instructions may be interspersed into this pattern
   3917                 // which must be ignored.
   3918                 if (block.instructions.items.len < 3) break :ct;
   3919                 var search_index: usize = block.instructions.items.len;
   3920                 const air_tags = sema.air_instructions.items(.tag);
   3921                 const air_datas = sema.air_instructions.items(.data);
   3922 
   3923                 const store_inst = while (true) {
   3924                     if (search_index == 0) break :ct;
   3925                     search_index -= 1;
   3926 
   3927                     const candidate = block.instructions.items[search_index];
   3928                     switch (air_tags[candidate]) {
   3929                         .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
   3930                         .store, .store_safe => break candidate,
   3931                         else => break :ct,
   3932                     }
   3933                 };
   3934 
   3935                 const bitcast_inst = while (true) {
   3936                     if (search_index == 0) break :ct;
   3937                     search_index -= 1;
   3938 
   3939                     const candidate = block.instructions.items[search_index];
   3940                     switch (air_tags[candidate]) {
   3941                         .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
   3942                         .bitcast => break candidate,
   3943                         else => break :ct,
   3944                     }
   3945                 };
   3946 
   3947                 while (true) {
   3948                     if (search_index == 0) break :ct;
   3949                     search_index -= 1;
   3950 
   3951                     const candidate = block.instructions.items[search_index];
   3952                     if (candidate == ptr_inst) break;
   3953                     switch (air_tags[candidate]) {
   3954                         .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
   3955                         else => break :ct,
   3956                     }
   3957                 }
   3958 
   3959                 const store_op = air_datas[store_inst].bin_op;
   3960                 const store_val = (try sema.resolveMaybeUndefVal(store_op.rhs)) orelse break :ct;
   3961                 if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct;
   3962                 if (air_datas[bitcast_inst].ty_op.operand != ptr) break :ct;
   3963 
   3964                 const new_decl_index = d: {
   3965                     var anon_decl = try block.startAnonDecl();
   3966                     defer anon_decl.deinit();
   3967                     const new_decl_index = try anon_decl.finish(final_elem_ty, store_val, ia1.alignment);
   3968                     break :d new_decl_index;
   3969                 };
   3970                 try mod.declareDeclDependency(sema.owner_decl_index, new_decl_index);
   3971 
   3972                 // Remove the instruction from the block so that codegen does not see it.
   3973                 block.instructions.shrinkRetainingCapacity(search_index);
   3974                 try sema.maybeQueueFuncBodyAnalysis(new_decl_index);
   3975 
   3976                 if (std.debug.runtime_safety) {
   3977                     // The inferred_alloc should never be referenced again
   3978                     sema.air_instructions.set(ptr_inst, .{ .tag = undefined, .data = undefined });
   3979                 }
   3980 
   3981                 const interned = try mod.intern(.{ .ptr = .{
   3982                     .ty = final_ptr_ty.toIntern(),
   3983                     .addr = .{ .decl = new_decl_index },
   3984                 } });
   3985 
   3986                 // Remap the ZIR oeprand to the resolved pointer value
   3987                 sema.inst_map.putAssumeCapacity(Zir.refToIndex(inst_data.operand).?, Air.internedToRef(interned));
   3988 
   3989                 // Unless the block is comptime, `alloc_inferred` always produces
   3990                 // a runtime constant. The final inferred type needs to be
   3991                 // fully resolved so it can be lowered in codegen.
   3992                 try sema.resolveTypeFully(final_elem_ty);
   3993 
   3994                 return;
   3995             }
   3996 
   3997             try sema.queueFullTypeResolution(final_elem_ty);
   3998 
   3999             // Change it to a normal alloc.
   4000             sema.air_instructions.set(ptr_inst, .{
   4001                 .tag = .alloc,
   4002                 .data = .{ .ty = final_ptr_ty },
   4003             });
   4004 
   4005             // Now we need to go back over all the coerce_result_ptr instructions, which
   4006             // previously inserted a bitcast as a placeholder, and do the logic as if
   4007             // the new result ptr type was available.
   4008             const placeholders = ia2.prongs.items(.placeholder);
   4009             const gpa = sema.gpa;
   4010 
   4011             var trash_block = block.makeSubBlock();
   4012             trash_block.is_comptime = false;
   4013             defer trash_block.instructions.deinit(gpa);
   4014 
   4015             const mut_final_ptr_ty = try mod.ptrType(.{
   4016                 .child = final_elem_ty.toIntern(),
   4017                 .flags = .{
   4018                     .alignment = ia1.alignment,
   4019                     .address_space = target_util.defaultAddressSpace(target, .local),
   4020                 },
   4021             });
   4022             const dummy_ptr = try trash_block.addTy(.alloc, mut_final_ptr_ty);
   4023             const empty_trash_count = trash_block.instructions.items.len;
   4024 
   4025             for (peer_inst_list, placeholders) |peer_inst, placeholder_inst| {
   4026                 const sub_ptr_ty = sema.typeOf(Air.indexToRef(placeholder_inst));
   4027 
   4028                 if (mut_final_ptr_ty.eql(sub_ptr_ty, mod)) {
   4029                     // New result location type is the same as the old one; nothing
   4030                     // to do here.
   4031                     continue;
   4032                 }
   4033 
   4034                 var replacement_block = block.makeSubBlock();
   4035                 defer replacement_block.instructions.deinit(gpa);
   4036 
   4037                 const result = switch (sema.air_instructions.items(.tag)[placeholder_inst]) {
   4038                     .bitcast => result: {
   4039                         trash_block.instructions.shrinkRetainingCapacity(empty_trash_count);
   4040                         const sub_ptr = try sema.coerceResultPtr(&replacement_block, src, ptr, dummy_ptr, peer_inst, &trash_block);
   4041 
   4042                         assert(replacement_block.instructions.items.len > 0);
   4043                         break :result sub_ptr;
   4044                     },
   4045                     .store, .store_safe => result: {
   4046                         const bin_op = sema.air_instructions.items(.data)[placeholder_inst].bin_op;
   4047                         try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .bitcast);
   4048                         break :result .void_value;
   4049                     },
   4050                     else => unreachable,
   4051                 };
   4052 
   4053                 // If only one instruction is produced then we can replace the bitcast
   4054                 // placeholder instruction with this instruction; no need for an entire block.
   4055                 if (replacement_block.instructions.items.len == 1) {
   4056                     const only_inst = replacement_block.instructions.items[0];
   4057                     sema.air_instructions.set(placeholder_inst, sema.air_instructions.get(only_inst));
   4058                     continue;
   4059                 }
   4060 
   4061                 // Here we replace the placeholder bitcast instruction with a block
   4062                 // that does the coerce_result_ptr logic.
   4063                 _ = try replacement_block.addBr(placeholder_inst, result);
   4064                 const ty_inst = if (result == .void_value)
   4065                     .void_type
   4066                 else
   4067                     sema.air_instructions.items(.data)[placeholder_inst].ty_op.ty;
   4068                 try sema.air_extra.ensureUnusedCapacity(
   4069                     gpa,
   4070                     @typeInfo(Air.Block).Struct.fields.len + replacement_block.instructions.items.len,
   4071                 );
   4072                 sema.air_instructions.set(placeholder_inst, .{
   4073                     .tag = .block,
   4074                     .data = .{ .ty_pl = .{
   4075                         .ty = ty_inst,
   4076                         .payload = sema.addExtraAssumeCapacity(Air.Block{
   4077                             .body_len = @as(u32, @intCast(replacement_block.instructions.items.len)),
   4078                         }),
   4079                     } },
   4080                 });
   4081                 sema.air_extra.appendSliceAssumeCapacity(replacement_block.instructions.items);
   4082             }
   4083         },
   4084         else => unreachable,
   4085     }
   4086 }
   4087 
   4088 fn zirArrayBasePtr(
   4089     sema: *Sema,
   4090     block: *Block,
   4091     inst: Zir.Inst.Index,
   4092 ) CompileError!Air.Inst.Ref {
   4093     const mod = sema.mod;
   4094     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   4095     const src = inst_data.src();
   4096 
   4097     const start_ptr = try sema.resolveInst(inst_data.operand);
   4098     var base_ptr = start_ptr;
   4099     while (true) switch (sema.typeOf(base_ptr).childType(mod).zigTypeTag(mod)) {
   4100         .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true),
   4101         .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true),
   4102         else => break,
   4103     };
   4104 
   4105     const elem_ty = sema.typeOf(base_ptr).childType(mod);
   4106     switch (elem_ty.zigTypeTag(mod)) {
   4107         .Array, .Vector => return base_ptr,
   4108         .Struct => if (elem_ty.isTuple(mod)) {
   4109             // TODO validate element count
   4110             return base_ptr;
   4111         },
   4112         else => {},
   4113     }
   4114     return sema.failWithArrayInitNotSupported(block, src, sema.typeOf(start_ptr).childType(mod));
   4115 }
   4116 
   4117 fn zirFieldBasePtr(
   4118     sema: *Sema,
   4119     block: *Block,
   4120     inst: Zir.Inst.Index,
   4121 ) CompileError!Air.Inst.Ref {
   4122     const mod = sema.mod;
   4123     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   4124     const src = inst_data.src();
   4125 
   4126     const start_ptr = try sema.resolveInst(inst_data.operand);
   4127     var base_ptr = start_ptr;
   4128     while (true) switch (sema.typeOf(base_ptr).childType(mod).zigTypeTag(mod)) {
   4129         .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true),
   4130         .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true),
   4131         else => break,
   4132     };
   4133 
   4134     const elem_ty = sema.typeOf(base_ptr).childType(mod);
   4135     switch (elem_ty.zigTypeTag(mod)) {
   4136         .Struct, .Union => return base_ptr,
   4137         else => {},
   4138     }
   4139     return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType(mod));
   4140 }
   4141 
   4142 fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4143     const mod = sema.mod;
   4144     const gpa = sema.gpa;
   4145     const ip = &mod.intern_pool;
   4146     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   4147     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
   4148     const args = sema.code.refSlice(extra.end, extra.data.operands_len);
   4149     const src = inst_data.src();
   4150 
   4151     var len: Air.Inst.Ref = .none;
   4152     var len_val: ?Value = null;
   4153     var len_idx: u32 = undefined;
   4154     var any_runtime = false;
   4155 
   4156     const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, args.len);
   4157     defer gpa.free(runtime_arg_lens);
   4158 
   4159     // First pass to look for comptime values.
   4160     for (args, 0..) |zir_arg, i_usize| {
   4161         const i = @as(u32, @intCast(i_usize));
   4162         runtime_arg_lens[i] = .none;
   4163         if (zir_arg == .none) continue;
   4164         const object = try sema.resolveInst(zir_arg);
   4165         const object_ty = sema.typeOf(object);
   4166         // Each arg could be an indexable, or a range, in which case the length
   4167         // is passed directly as an integer.
   4168         const is_int = switch (object_ty.zigTypeTag(mod)) {
   4169             .Int, .ComptimeInt => true,
   4170             else => false,
   4171         };
   4172         const arg_src: LazySrcLoc = .{ .for_input = .{
   4173             .for_node_offset = inst_data.src_node,
   4174             .input_index = i,
   4175         } };
   4176         const arg_len_uncoerced = if (is_int) object else l: {
   4177             if (!object_ty.isIndexable(mod)) {
   4178                 // Instead of using checkIndexable we customize this error.
   4179                 const msg = msg: {
   4180                     const msg = try sema.errMsg(block, arg_src, "type '{}' is not indexable and not a range", .{object_ty.fmt(sema.mod)});
   4181                     errdefer msg.destroy(sema.gpa);
   4182                     try sema.errNote(block, arg_src, msg, "for loop operand must be a range, array, slice, tuple, or vector", .{});
   4183                     break :msg msg;
   4184                 };
   4185                 return sema.failWithOwnedErrorMsg(msg);
   4186             }
   4187             if (!object_ty.indexableHasLen(mod)) continue;
   4188 
   4189             break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, "len"), arg_src);
   4190         };
   4191         const arg_len = try sema.coerce(block, Type.usize, arg_len_uncoerced, arg_src);
   4192         if (len == .none) {
   4193             len = arg_len;
   4194             len_idx = i;
   4195         }
   4196         if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| {
   4197             if (len_val) |v| {
   4198                 if (!(try sema.valuesEqual(arg_val, v, Type.usize))) {
   4199                     const msg = msg: {
   4200                         const msg = try sema.errMsg(block, src, "non-matching for loop lengths", .{});
   4201                         errdefer msg.destroy(gpa);
   4202                         const a_src: LazySrcLoc = .{ .for_input = .{
   4203                             .for_node_offset = inst_data.src_node,
   4204                             .input_index = len_idx,
   4205                         } };
   4206                         try sema.errNote(block, a_src, msg, "length {} here", .{
   4207                             v.fmtValue(Type.usize, sema.mod),
   4208                         });
   4209                         try sema.errNote(block, arg_src, msg, "length {} here", .{
   4210                             arg_val.fmtValue(Type.usize, sema.mod),
   4211                         });
   4212                         break :msg msg;
   4213                     };
   4214                     return sema.failWithOwnedErrorMsg(msg);
   4215                 }
   4216             } else {
   4217                 len = arg_len;
   4218                 len_val = arg_val;
   4219                 len_idx = i;
   4220             }
   4221             continue;
   4222         }
   4223         runtime_arg_lens[i] = arg_len;
   4224         any_runtime = true;
   4225     }
   4226 
   4227     if (len == .none) {
   4228         const msg = msg: {
   4229             const msg = try sema.errMsg(block, src, "unbounded for loop", .{});
   4230             errdefer msg.destroy(gpa);
   4231             for (args, 0..) |zir_arg, i_usize| {
   4232                 const i = @as(u32, @intCast(i_usize));
   4233                 if (zir_arg == .none) continue;
   4234                 const object = try sema.resolveInst(zir_arg);
   4235                 const object_ty = sema.typeOf(object);
   4236                 // Each arg could be an indexable, or a range, in which case the length
   4237                 // is passed directly as an integer.
   4238                 switch (object_ty.zigTypeTag(mod)) {
   4239                     .Int, .ComptimeInt => continue,
   4240                     else => {},
   4241                 }
   4242                 const arg_src: LazySrcLoc = .{ .for_input = .{
   4243                     .for_node_offset = inst_data.src_node,
   4244                     .input_index = i,
   4245                 } };
   4246                 try sema.errNote(block, arg_src, msg, "type '{}' has no upper bound", .{
   4247                     object_ty.fmt(sema.mod),
   4248                 });
   4249             }
   4250             break :msg msg;
   4251         };
   4252         return sema.failWithOwnedErrorMsg(msg);
   4253     }
   4254 
   4255     // Now for the runtime checks.
   4256     if (any_runtime and block.wantSafety()) {
   4257         for (runtime_arg_lens, 0..) |arg_len, i| {
   4258             if (arg_len == .none) continue;
   4259             if (i == len_idx) continue;
   4260             const ok = try block.addBinOp(.cmp_eq, len, arg_len);
   4261             try sema.addSafetyCheck(block, ok, .for_len_mismatch);
   4262         }
   4263     }
   4264 
   4265     return len;
   4266 }
   4267 
   4268 fn validateArrayInitTy(
   4269     sema: *Sema,
   4270     block: *Block,
   4271     inst: Zir.Inst.Index,
   4272 ) CompileError!void {
   4273     const mod = sema.mod;
   4274     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   4275     const src = inst_data.src();
   4276     const ty_src: LazySrcLoc = .{ .node_offset_init_ty = inst_data.src_node };
   4277     const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
   4278     const ty = try sema.resolveType(block, ty_src, extra.ty);
   4279 
   4280     switch (ty.zigTypeTag(mod)) {
   4281         .Array => {
   4282             const array_len = ty.arrayLen(mod);
   4283             if (extra.init_count != array_len) {
   4284                 return sema.fail(block, src, "expected {d} array elements; found {d}", .{
   4285                     array_len, extra.init_count,
   4286                 });
   4287             }
   4288             return;
   4289         },
   4290         .Vector => {
   4291             const array_len = ty.arrayLen(mod);
   4292             if (extra.init_count != array_len) {
   4293                 return sema.fail(block, src, "expected {d} vector elements; found {d}", .{
   4294                     array_len, extra.init_count,
   4295                 });
   4296             }
   4297             return;
   4298         },
   4299         .Struct => if (ty.isTuple(mod)) {
   4300             _ = try sema.resolveTypeFields(ty);
   4301             const array_len = ty.arrayLen(mod);
   4302             if (extra.init_count > array_len) {
   4303                 return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{
   4304                     array_len, extra.init_count,
   4305                 });
   4306             }
   4307             return;
   4308         },
   4309         else => {},
   4310     }
   4311     return sema.failWithArrayInitNotSupported(block, ty_src, ty);
   4312 }
   4313 
   4314 fn validateStructInitTy(
   4315     sema: *Sema,
   4316     block: *Block,
   4317     inst: Zir.Inst.Index,
   4318 ) CompileError!void {
   4319     const mod = sema.mod;
   4320     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   4321     const src = inst_data.src();
   4322     const ty = try sema.resolveType(block, src, inst_data.operand);
   4323 
   4324     switch (ty.zigTypeTag(mod)) {
   4325         .Struct, .Union => return,
   4326         else => {},
   4327     }
   4328     return sema.failWithStructInitNotSupported(block, src, ty);
   4329 }
   4330 
   4331 fn zirValidateStructInit(
   4332     sema: *Sema,
   4333     block: *Block,
   4334     inst: Zir.Inst.Index,
   4335 ) CompileError!void {
   4336     const tracy = trace(@src());
   4337     defer tracy.end();
   4338 
   4339     const mod = sema.mod;
   4340     const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
   4341     const init_src = validate_inst.src();
   4342     const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
   4343     const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
   4344     const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
   4345     const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
   4346     const object_ptr = try sema.resolveInst(field_ptr_extra.lhs);
   4347     const agg_ty = sema.typeOf(object_ptr).childType(mod);
   4348     switch (agg_ty.zigTypeTag(mod)) {
   4349         .Struct => return sema.validateStructInit(
   4350             block,
   4351             agg_ty,
   4352             init_src,
   4353             instrs,
   4354         ),
   4355         .Union => return sema.validateUnionInit(
   4356             block,
   4357             agg_ty,
   4358             init_src,
   4359             instrs,
   4360             object_ptr,
   4361         ),
   4362         else => unreachable,
   4363     }
   4364 }
   4365 
   4366 fn validateUnionInit(
   4367     sema: *Sema,
   4368     block: *Block,
   4369     union_ty: Type,
   4370     init_src: LazySrcLoc,
   4371     instrs: []const Zir.Inst.Index,
   4372     union_ptr: Air.Inst.Ref,
   4373 ) CompileError!void {
   4374     const mod = sema.mod;
   4375     const gpa = sema.gpa;
   4376 
   4377     if (instrs.len != 1) {
   4378         const msg = msg: {
   4379             const msg = try sema.errMsg(
   4380                 block,
   4381                 init_src,
   4382                 "cannot initialize multiple union fields at once; unions can only have one active field",
   4383                 .{},
   4384             );
   4385             errdefer msg.destroy(gpa);
   4386 
   4387             for (instrs[1..]) |inst| {
   4388                 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   4389                 const inst_src: LazySrcLoc = .{ .node_offset_initializer = inst_data.src_node };
   4390                 try sema.errNote(block, inst_src, msg, "additional initializer here", .{});
   4391             }
   4392             try sema.addDeclaredHereNote(msg, union_ty);
   4393             break :msg msg;
   4394         };
   4395         return sema.failWithOwnedErrorMsg(msg);
   4396     }
   4397 
   4398     if (block.is_comptime and
   4399         (try sema.resolveDefinedValue(block, init_src, union_ptr)) != null)
   4400     {
   4401         // In this case, comptime machinery already did everything. No work to do here.
   4402         return;
   4403     }
   4404 
   4405     const field_ptr = instrs[0];
   4406     const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
   4407     const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node };
   4408     const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
   4409     const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_ptr_extra.field_name_start));
   4410     // Validate the field access but ignore the index since we want the tag enum field index.
   4411     _ = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
   4412     const air_tags = sema.air_instructions.items(.tag);
   4413     const air_datas = sema.air_instructions.items(.data);
   4414     const field_ptr_air_ref = sema.inst_map.get(field_ptr).?;
   4415 
   4416     // Our task here is to determine if the union is comptime-known. In such case,
   4417     // we erase the runtime AIR instructions for initializing the union, and replace
   4418     // the mapping with the comptime value. Either way, we will need to populate the tag.
   4419 
   4420     // We expect to see something like this in the current block AIR:
   4421     //   %a = alloc(*const U)
   4422     //   %b = bitcast(*U, %a)
   4423     //   %c = field_ptr(..., %b)
   4424     //   %e!= store(%c!, %d!)
   4425     // If %d is a comptime operand, the union is comptime.
   4426     // If the union is comptime, we want `first_block_index`
   4427     // to point at %c so that the bitcast becomes the last instruction in the block.
   4428     //
   4429     // In the case of a comptime-known pointer to a union, the
   4430     // the field_ptr instruction is missing, so we have to pattern-match
   4431     // based only on the store instructions.
   4432     // `first_block_index` needs to point to the `field_ptr` if it exists;
   4433     // the `store` otherwise.
   4434     //
   4435     // It's also possible for there to be no store instruction, in the case
   4436     // of nested `coerce_result_ptr` instructions. If we see the `field_ptr`
   4437     // but we have not found a `store`, treat as a runtime-known field.
   4438     var first_block_index = block.instructions.items.len;
   4439     var block_index = block.instructions.items.len - 1;
   4440     var init_val: ?Value = null;
   4441     var make_runtime = false;
   4442     while (block_index > 0) : (block_index -= 1) {
   4443         const store_inst = block.instructions.items[block_index];
   4444         if (Air.indexToRef(store_inst) == field_ptr_air_ref) break;
   4445         switch (air_tags[store_inst]) {
   4446             .store, .store_safe => {},
   4447             else => continue,
   4448         }
   4449         const bin_op = air_datas[store_inst].bin_op;
   4450         var lhs = bin_op.lhs;
   4451         if (Air.refToIndex(lhs)) |lhs_index| {
   4452             if (air_tags[lhs_index] == .bitcast) {
   4453                 lhs = air_datas[lhs_index].ty_op.operand;
   4454                 block_index -= 1;
   4455             }
   4456         }
   4457         if (lhs != field_ptr_air_ref) continue;
   4458         while (block_index > 0) : (block_index -= 1) {
   4459             const block_inst = block.instructions.items[block_index - 1];
   4460             if (air_tags[block_inst] != .dbg_stmt) break;
   4461         }
   4462         if (block_index > 0 and
   4463             field_ptr_air_ref == Air.indexToRef(block.instructions.items[block_index - 1]))
   4464         {
   4465             first_block_index = @min(first_block_index, block_index - 1);
   4466         } else {
   4467             first_block_index = @min(first_block_index, block_index);
   4468         }
   4469         init_val = try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(bin_op.rhs, &make_runtime);
   4470         break;
   4471     }
   4472 
   4473     const tag_ty = union_ty.unionTagTypeHypothetical(mod);
   4474     const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?));
   4475     const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index);
   4476 
   4477     if (init_val) |val| {
   4478         // Our task is to delete all the `field_ptr` and `store` instructions, and insert
   4479         // instead a single `store` to the result ptr with a comptime union value.
   4480         block.instructions.shrinkRetainingCapacity(first_block_index);
   4481 
   4482         var union_val = try mod.intern(.{ .un = .{
   4483             .ty = union_ty.toIntern(),
   4484             .tag = tag_val.toIntern(),
   4485             .val = val.toIntern(),
   4486         } });
   4487         if (make_runtime) union_val = try mod.intern(.{ .runtime_value = .{
   4488             .ty = union_ty.toIntern(),
   4489             .val = union_val,
   4490         } });
   4491         const union_init = try sema.addConstant(union_val.toValue());
   4492         try sema.storePtr2(block, init_src, union_ptr, init_src, union_init, init_src, .store);
   4493         return;
   4494     } else if (try sema.typeRequiresComptime(union_ty)) {
   4495         return sema.failWithNeededComptime(block, field_ptr_data.src(), "initializer of comptime only union must be comptime-known");
   4496     }
   4497 
   4498     const new_tag = try sema.addConstant(tag_val);
   4499     _ = try block.addBinOp(.set_union_tag, union_ptr, new_tag);
   4500 }
   4501 
   4502 fn validateStructInit(
   4503     sema: *Sema,
   4504     block: *Block,
   4505     struct_ty: Type,
   4506     init_src: LazySrcLoc,
   4507     instrs: []const Zir.Inst.Index,
   4508 ) CompileError!void {
   4509     const mod = sema.mod;
   4510     const gpa = sema.gpa;
   4511     const ip = &mod.intern_pool;
   4512 
   4513     // Maps field index to field_ptr index of where it was already initialized.
   4514     const found_fields = try gpa.alloc(Zir.Inst.Index, struct_ty.structFieldCount(mod));
   4515     defer gpa.free(found_fields);
   4516     @memset(found_fields, 0);
   4517 
   4518     var struct_ptr_zir_ref: Zir.Inst.Ref = undefined;
   4519 
   4520     for (instrs) |field_ptr| {
   4521         const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
   4522         const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node };
   4523         const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
   4524         struct_ptr_zir_ref = field_ptr_extra.lhs;
   4525         const field_name = try ip.getOrPutString(
   4526             gpa,
   4527             sema.code.nullTerminatedString(field_ptr_extra.field_name_start),
   4528         );
   4529         const field_index = if (struct_ty.isTuple(mod))
   4530             try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
   4531         else
   4532             try sema.structFieldIndex(block, struct_ty, field_name, field_src);
   4533         if (found_fields[field_index] != 0) {
   4534             const other_field_ptr = found_fields[field_index];
   4535             const other_field_ptr_data = sema.code.instructions.items(.data)[other_field_ptr].pl_node;
   4536             const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_ptr_data.src_node };
   4537             const msg = msg: {
   4538                 const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
   4539                 errdefer msg.destroy(gpa);
   4540                 try sema.errNote(block, other_field_src, msg, "other field here", .{});
   4541                 break :msg msg;
   4542             };
   4543             return sema.failWithOwnedErrorMsg(msg);
   4544         }
   4545         found_fields[field_index] = field_ptr;
   4546     }
   4547 
   4548     var root_msg: ?*Module.ErrorMsg = null;
   4549     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
   4550 
   4551     const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref);
   4552     if (block.is_comptime and
   4553         (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null)
   4554     {
   4555         try sema.resolveStructLayout(struct_ty);
   4556         // In this case the only thing we need to do is evaluate the implicit
   4557         // store instructions for default field values, and report any missing fields.
   4558         // Avoid the cost of the extra machinery for detecting a comptime struct init value.
   4559         for (found_fields, 0..) |field_ptr, i| {
   4560             if (field_ptr != 0) continue;
   4561 
   4562             const default_val = struct_ty.structFieldDefaultValue(i, mod);
   4563             if (default_val.toIntern() == .unreachable_value) {
   4564                 if (struct_ty.isTuple(mod)) {
   4565                     const template = "missing tuple field with index {d}";
   4566                     if (root_msg) |msg| {
   4567                         try sema.errNote(block, init_src, msg, template, .{i});
   4568                     } else {
   4569                         root_msg = try sema.errMsg(block, init_src, template, .{i});
   4570                     }
   4571                     continue;
   4572                 }
   4573                 const field_name = struct_ty.structFieldName(i, mod);
   4574                 const template = "missing struct field: {}";
   4575                 const args = .{field_name.fmt(ip)};
   4576                 if (root_msg) |msg| {
   4577                     try sema.errNote(block, init_src, msg, template, args);
   4578                 } else {
   4579                     root_msg = try sema.errMsg(block, init_src, template, args);
   4580                 }
   4581                 continue;
   4582             }
   4583 
   4584             const field_src = init_src; // TODO better source location
   4585             const default_field_ptr = if (struct_ty.isTuple(mod))
   4586                 try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @as(u32, @intCast(i)), true)
   4587             else
   4588                 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @as(u32, @intCast(i)), field_src, struct_ty, true);
   4589             const init = try sema.addConstant(default_val);
   4590             try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
   4591         }
   4592 
   4593         if (root_msg) |msg| {
   4594             if (mod.typeToStruct(struct_ty)) |struct_obj| {
   4595                 const fqn = try struct_obj.getFullyQualifiedName(mod);
   4596                 try mod.errNoteNonLazy(
   4597                     struct_obj.srcLoc(mod),
   4598                     msg,
   4599                     "struct '{}' declared here",
   4600                     .{fqn.fmt(ip)},
   4601                 );
   4602             }
   4603             root_msg = null;
   4604             return sema.failWithOwnedErrorMsg(msg);
   4605         }
   4606 
   4607         return;
   4608     }
   4609 
   4610     var struct_is_comptime = true;
   4611     var first_block_index = block.instructions.items.len;
   4612     var make_runtime = false;
   4613 
   4614     const require_comptime = try sema.typeRequiresComptime(struct_ty);
   4615     const air_tags = sema.air_instructions.items(.tag);
   4616     const air_datas = sema.air_instructions.items(.data);
   4617 
   4618     // We collect the comptime field values in case the struct initialization
   4619     // ends up being comptime-known.
   4620     const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(mod));
   4621 
   4622     field: for (found_fields, 0..) |field_ptr, i| {
   4623         if (field_ptr != 0) {
   4624             // Determine whether the value stored to this pointer is comptime-known.
   4625             const field_ty = struct_ty.structFieldType(i, mod);
   4626             if (try sema.typeHasOnePossibleValue(field_ty)) |opv| {
   4627                 field_values[i] = opv.toIntern();
   4628                 continue;
   4629             }
   4630 
   4631             const field_ptr_air_ref = sema.inst_map.get(field_ptr).?;
   4632 
   4633             //std.debug.print("validateStructInit (field_ptr_air_inst=%{d}):\n", .{
   4634             //    field_ptr_air_inst,
   4635             //});
   4636             //for (block.instructions.items) |item| {
   4637             //    std.debug.print("  %{d} = {s}\n", .{item, @tagName(air_tags[item])});
   4638             //}
   4639 
   4640             // We expect to see something like this in the current block AIR:
   4641             //   %a = field_ptr(...)
   4642             //   store(%a, %b)
   4643             // With an optional bitcast between the store and the field_ptr.
   4644             // If %b is a comptime operand, this field is comptime.
   4645             //
   4646             // However, in the case of a comptime-known pointer to a struct, the
   4647             // the field_ptr instruction is missing, so we have to pattern-match
   4648             // based only on the store instructions.
   4649             // `first_block_index` needs to point to the `field_ptr` if it exists;
   4650             // the `store` otherwise.
   4651             //
   4652             // It's also possible for there to be no store instruction, in the case
   4653             // of nested `coerce_result_ptr` instructions. If we see the `field_ptr`
   4654             // but we have not found a `store`, treat as a runtime-known field.
   4655 
   4656             // Possible performance enhancement: save the `block_index` between iterations
   4657             // of the for loop.
   4658             var block_index = block.instructions.items.len - 1;
   4659             while (block_index > 0) : (block_index -= 1) {
   4660                 const store_inst = block.instructions.items[block_index];
   4661                 if (Air.indexToRef(store_inst) == field_ptr_air_ref) {
   4662                     struct_is_comptime = false;
   4663                     continue :field;
   4664                 }
   4665                 switch (air_tags[store_inst]) {
   4666                     .store, .store_safe => {},
   4667                     else => continue,
   4668                 }
   4669                 const bin_op = air_datas[store_inst].bin_op;
   4670                 var lhs = bin_op.lhs;
   4671                 {
   4672                     const lhs_index = Air.refToIndex(lhs) orelse continue;
   4673                     if (air_tags[lhs_index] == .bitcast) {
   4674                         lhs = air_datas[lhs_index].ty_op.operand;
   4675                         block_index -= 1;
   4676                     }
   4677                 }
   4678                 if (lhs != field_ptr_air_ref) continue;
   4679                 while (block_index > 0) : (block_index -= 1) {
   4680                     const block_inst = block.instructions.items[block_index - 1];
   4681                     if (air_tags[block_inst] != .dbg_stmt) break;
   4682                 }
   4683                 if (block_index > 0 and
   4684                     field_ptr_air_ref == Air.indexToRef(block.instructions.items[block_index - 1]))
   4685                 {
   4686                     first_block_index = @min(first_block_index, block_index - 1);
   4687                 } else {
   4688                     first_block_index = @min(first_block_index, block_index);
   4689                 }
   4690                 if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(bin_op.rhs, &make_runtime)) |val| {
   4691                     field_values[i] = val.toIntern();
   4692                 } else if (require_comptime) {
   4693                     const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
   4694                     return sema.failWithNeededComptime(block, field_ptr_data.src(), "initializer of comptime only struct must be comptime-known");
   4695                 } else {
   4696                     struct_is_comptime = false;
   4697                 }
   4698                 continue :field;
   4699             }
   4700             struct_is_comptime = false;
   4701             continue :field;
   4702         }
   4703 
   4704         const default_val = struct_ty.structFieldDefaultValue(i, mod);
   4705         if (default_val.toIntern() == .unreachable_value) {
   4706             if (struct_ty.isTuple(mod)) {
   4707                 const template = "missing tuple field with index {d}";
   4708                 if (root_msg) |msg| {
   4709                     try sema.errNote(block, init_src, msg, template, .{i});
   4710                 } else {
   4711                     root_msg = try sema.errMsg(block, init_src, template, .{i});
   4712                 }
   4713                 continue;
   4714             }
   4715             const field_name = struct_ty.structFieldName(i, mod);
   4716             const template = "missing struct field: {}";
   4717             const args = .{field_name.fmt(ip)};
   4718             if (root_msg) |msg| {
   4719                 try sema.errNote(block, init_src, msg, template, args);
   4720             } else {
   4721                 root_msg = try sema.errMsg(block, init_src, template, args);
   4722             }
   4723             continue;
   4724         }
   4725         field_values[i] = default_val.toIntern();
   4726     }
   4727 
   4728     if (root_msg) |msg| {
   4729         if (mod.typeToStruct(struct_ty)) |struct_obj| {
   4730             const fqn = try struct_obj.getFullyQualifiedName(mod);
   4731             try mod.errNoteNonLazy(
   4732                 struct_obj.srcLoc(mod),
   4733                 msg,
   4734                 "struct '{}' declared here",
   4735                 .{fqn.fmt(ip)},
   4736             );
   4737         }
   4738         root_msg = null;
   4739         return sema.failWithOwnedErrorMsg(msg);
   4740     }
   4741 
   4742     if (struct_is_comptime) {
   4743         // Our task is to delete all the `field_ptr` and `store` instructions, and insert
   4744         // instead a single `store` to the struct_ptr with a comptime struct value.
   4745 
   4746         block.instructions.shrinkRetainingCapacity(first_block_index);
   4747         var struct_val = try mod.intern(.{ .aggregate = .{
   4748             .ty = struct_ty.toIntern(),
   4749             .storage = .{ .elems = field_values },
   4750         } });
   4751         if (make_runtime) struct_val = try mod.intern(.{ .runtime_value = .{
   4752             .ty = struct_ty.toIntern(),
   4753             .val = struct_val,
   4754         } });
   4755         const struct_init = try sema.addConstant(struct_val.toValue());
   4756         try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store);
   4757         return;
   4758     }
   4759     try sema.resolveStructLayout(struct_ty);
   4760 
   4761     // Our task is to insert `store` instructions for all the default field values.
   4762     for (found_fields, 0..) |field_ptr, i| {
   4763         if (field_ptr != 0) continue;
   4764 
   4765         const field_src = init_src; // TODO better source location
   4766         const default_field_ptr = if (struct_ty.isTuple(mod))
   4767             try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @as(u32, @intCast(i)), true)
   4768         else
   4769             try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @as(u32, @intCast(i)), field_src, struct_ty, true);
   4770         const init = try sema.addConstant(field_values[i].toValue());
   4771         try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
   4772     }
   4773 }
   4774 
   4775 fn zirValidateArrayInit(
   4776     sema: *Sema,
   4777     block: *Block,
   4778     inst: Zir.Inst.Index,
   4779 ) CompileError!void {
   4780     const mod = sema.mod;
   4781     const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
   4782     const init_src = validate_inst.src();
   4783     const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
   4784     const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
   4785     const first_elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
   4786     const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data;
   4787     const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr);
   4788     const array_ty = sema.typeOf(array_ptr).childType(mod);
   4789     const array_len = array_ty.arrayLen(mod);
   4790 
   4791     if (instrs.len != array_len) switch (array_ty.zigTypeTag(mod)) {
   4792         .Struct => {
   4793             var root_msg: ?*Module.ErrorMsg = null;
   4794             errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
   4795 
   4796             var i = instrs.len;
   4797             while (i < array_len) : (i += 1) {
   4798                 const default_val = array_ty.structFieldDefaultValue(i, mod);
   4799                 if (default_val.toIntern() == .unreachable_value) {
   4800                     const template = "missing tuple field with index {d}";
   4801                     if (root_msg) |msg| {
   4802                         try sema.errNote(block, init_src, msg, template, .{i});
   4803                     } else {
   4804                         root_msg = try sema.errMsg(block, init_src, template, .{i});
   4805                     }
   4806                 }
   4807             }
   4808 
   4809             if (root_msg) |msg| {
   4810                 root_msg = null;
   4811                 return sema.failWithOwnedErrorMsg(msg);
   4812             }
   4813         },
   4814         .Array => {
   4815             return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
   4816                 array_len, instrs.len,
   4817             });
   4818         },
   4819         .Vector => {
   4820             return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
   4821                 array_len, instrs.len,
   4822             });
   4823         },
   4824         else => unreachable,
   4825     };
   4826 
   4827     if (block.is_comptime and
   4828         (try sema.resolveDefinedValue(block, init_src, array_ptr)) != null)
   4829     {
   4830         // In this case the comptime machinery will have evaluated the store instructions
   4831         // at comptime so we have almost nothing to do here. However, in case of a
   4832         // sentinel-terminated array, the sentinel will not have been populated by
   4833         // any ZIR instructions at comptime; we need to do that here.
   4834         if (array_ty.sentinel(mod)) |sentinel_val| {
   4835             const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len);
   4836             const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true);
   4837             const sentinel = try sema.addConstant(sentinel_val);
   4838             try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store);
   4839         }
   4840         return;
   4841     }
   4842 
   4843     // If the array has one possible value, the value is always comptime-known.
   4844     if (try sema.typeHasOnePossibleValue(array_ty)) |array_opv| {
   4845         const array_init = try sema.addConstant(array_opv);
   4846         try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
   4847         return;
   4848     }
   4849 
   4850     var array_is_comptime = true;
   4851     var first_block_index = block.instructions.items.len;
   4852     var make_runtime = false;
   4853 
   4854     // Collect the comptime element values in case the array literal ends up
   4855     // being comptime-known.
   4856     const element_vals = try sema.arena.alloc(
   4857         InternPool.Index,
   4858         try sema.usizeCast(block, init_src, array_len),
   4859     );
   4860     const air_tags = sema.air_instructions.items(.tag);
   4861     const air_datas = sema.air_instructions.items(.data);
   4862 
   4863     outer: for (instrs, 0..) |elem_ptr, i| {
   4864         // Determine whether the value stored to this pointer is comptime-known.
   4865 
   4866         if (array_ty.isTuple(mod)) {
   4867             if (try array_ty.structFieldValueComptime(mod, i)) |opv| {
   4868                 element_vals[i] = opv.toIntern();
   4869                 continue;
   4870             }
   4871         }
   4872 
   4873         const elem_ptr_air_ref = sema.inst_map.get(elem_ptr).?;
   4874 
   4875         // We expect to see something like this in the current block AIR:
   4876         //   %a = elem_ptr(...)
   4877         //   store(%a, %b)
   4878         // With an optional bitcast between the store and the elem_ptr.
   4879         // If %b is a comptime operand, this element is comptime.
   4880         //
   4881         // However, in the case of a comptime-known pointer to an array, the
   4882         // the elem_ptr instruction is missing, so we have to pattern-match
   4883         // based only on the store instructions.
   4884         // `first_block_index` needs to point to the `elem_ptr` if it exists;
   4885         // the `store` otherwise.
   4886         //
   4887         // It's also possible for there to be no store instruction, in the case
   4888         // of nested `coerce_result_ptr` instructions. If we see the `elem_ptr`
   4889         // but we have not found a `store`, treat as a runtime-known element.
   4890         //
   4891         // This is nearly identical to similar logic in `validateStructInit`.
   4892 
   4893         // Possible performance enhancement: save the `block_index` between iterations
   4894         // of the for loop.
   4895         var block_index = block.instructions.items.len - 1;
   4896         while (block_index > 0) : (block_index -= 1) {
   4897             const store_inst = block.instructions.items[block_index];
   4898             if (Air.indexToRef(store_inst) == elem_ptr_air_ref) {
   4899                 array_is_comptime = false;
   4900                 continue :outer;
   4901             }
   4902             switch (air_tags[store_inst]) {
   4903                 .store, .store_safe => {},
   4904                 else => continue,
   4905             }
   4906             const bin_op = air_datas[store_inst].bin_op;
   4907             var lhs = bin_op.lhs;
   4908             {
   4909                 const lhs_index = Air.refToIndex(lhs) orelse continue;
   4910                 if (air_tags[lhs_index] == .bitcast) {
   4911                     lhs = air_datas[lhs_index].ty_op.operand;
   4912                     block_index -= 1;
   4913                 }
   4914             }
   4915             if (lhs != elem_ptr_air_ref) continue;
   4916             while (block_index > 0) : (block_index -= 1) {
   4917                 const block_inst = block.instructions.items[block_index - 1];
   4918                 if (air_tags[block_inst] != .dbg_stmt) break;
   4919             }
   4920             if (block_index > 0 and
   4921                 elem_ptr_air_ref == Air.indexToRef(block.instructions.items[block_index - 1]))
   4922             {
   4923                 first_block_index = @min(first_block_index, block_index - 1);
   4924             } else {
   4925                 first_block_index = @min(first_block_index, block_index);
   4926             }
   4927             if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(bin_op.rhs, &make_runtime)) |val| {
   4928                 element_vals[i] = val.toIntern();
   4929             } else {
   4930                 array_is_comptime = false;
   4931             }
   4932             continue :outer;
   4933         }
   4934         array_is_comptime = false;
   4935         continue :outer;
   4936     }
   4937 
   4938     if (array_is_comptime) {
   4939         if (try sema.resolveDefinedValue(block, init_src, array_ptr)) |ptr_val| {
   4940             switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) {
   4941                 .ptr => |ptr| switch (ptr.addr) {
   4942                     .comptime_field => return, // This store was validated by the individual elem ptrs.
   4943                     else => {},
   4944                 },
   4945                 else => {},
   4946             }
   4947         }
   4948 
   4949         // Our task is to delete all the `elem_ptr` and `store` instructions, and insert
   4950         // instead a single `store` to the array_ptr with a comptime struct value.
   4951         block.instructions.shrinkRetainingCapacity(first_block_index);
   4952 
   4953         var array_val = try mod.intern(.{ .aggregate = .{
   4954             .ty = array_ty.toIntern(),
   4955             .storage = .{ .elems = element_vals },
   4956         } });
   4957         if (make_runtime) array_val = try mod.intern(.{ .runtime_value = .{
   4958             .ty = array_ty.toIntern(),
   4959             .val = array_val,
   4960         } });
   4961         const array_init = try sema.addConstant(array_val.toValue());
   4962         try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
   4963     }
   4964 }
   4965 
   4966 fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   4967     const mod = sema.mod;
   4968     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   4969     const src = inst_data.src();
   4970     const operand = try sema.resolveInst(inst_data.operand);
   4971     const operand_ty = sema.typeOf(operand);
   4972 
   4973     if (operand_ty.zigTypeTag(mod) != .Pointer) {
   4974         return sema.fail(block, src, "cannot dereference non-pointer type '{}'", .{operand_ty.fmt(mod)});
   4975     } else switch (operand_ty.ptrSize(mod)) {
   4976         .One, .C => {},
   4977         .Many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{}'", .{operand_ty.fmt(mod)}),
   4978         .Slice => return sema.fail(block, src, "index syntax required for slice type '{}'", .{operand_ty.fmt(mod)}),
   4979     }
   4980 
   4981     if ((try sema.typeHasOnePossibleValue(operand_ty.childType(mod))) != null) {
   4982         // No need to validate the actual pointer value, we don't need it!
   4983         return;
   4984     }
   4985 
   4986     const elem_ty = operand_ty.elemType2(mod);
   4987     if (try sema.resolveMaybeUndefVal(operand)) |val| {
   4988         if (val.isUndef(mod)) {
   4989             return sema.fail(block, src, "cannot dereference undefined value", .{});
   4990         }
   4991     } else if (!(try sema.validateRunTimeType(elem_ty, false))) {
   4992         const msg = msg: {
   4993             const msg = try sema.errMsg(
   4994                 block,
   4995                 src,
   4996                 "values of type '{}' must be comptime-known, but operand value is runtime-known",
   4997                 .{elem_ty.fmt(mod)},
   4998             );
   4999             errdefer msg.destroy(sema.gpa);
   5000 
   5001             const src_decl = mod.declPtr(block.src_decl);
   5002             try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), elem_ty);
   5003             break :msg msg;
   5004         };
   5005         return sema.failWithOwnedErrorMsg(msg);
   5006     }
   5007 }
   5008 
   5009 fn failWithBadMemberAccess(
   5010     sema: *Sema,
   5011     block: *Block,
   5012     agg_ty: Type,
   5013     field_src: LazySrcLoc,
   5014     field_name: InternPool.NullTerminatedString,
   5015 ) CompileError {
   5016     const mod = sema.mod;
   5017     const kw_name = switch (agg_ty.zigTypeTag(mod)) {
   5018         .Union => "union",
   5019         .Struct => "struct",
   5020         .Opaque => "opaque",
   5021         .Enum => "enum",
   5022         else => unreachable,
   5023     };
   5024     if (agg_ty.getOwnerDeclOrNull(mod)) |some| if (mod.declIsRoot(some)) {
   5025         return sema.fail(block, field_src, "root struct of file '{}' has no member named '{}'", .{
   5026             agg_ty.fmt(mod), field_name.fmt(&mod.intern_pool),
   5027         });
   5028     };
   5029     const msg = msg: {
   5030         const msg = try sema.errMsg(block, field_src, "{s} '{}' has no member named '{}'", .{
   5031             kw_name, agg_ty.fmt(mod), field_name.fmt(&mod.intern_pool),
   5032         });
   5033         errdefer msg.destroy(sema.gpa);
   5034         try sema.addDeclaredHereNote(msg, agg_ty);
   5035         break :msg msg;
   5036     };
   5037     return sema.failWithOwnedErrorMsg(msg);
   5038 }
   5039 
   5040 fn failWithBadStructFieldAccess(
   5041     sema: *Sema,
   5042     block: *Block,
   5043     struct_obj: *Module.Struct,
   5044     field_src: LazySrcLoc,
   5045     field_name: InternPool.NullTerminatedString,
   5046 ) CompileError {
   5047     const mod = sema.mod;
   5048     const gpa = sema.gpa;
   5049 
   5050     const fqn = try struct_obj.getFullyQualifiedName(mod);
   5051 
   5052     const msg = msg: {
   5053         const msg = try sema.errMsg(
   5054             block,
   5055             field_src,
   5056             "no field named '{}' in struct '{}'",
   5057             .{ field_name.fmt(&mod.intern_pool), fqn.fmt(&mod.intern_pool) },
   5058         );
   5059         errdefer msg.destroy(gpa);
   5060         try mod.errNoteNonLazy(struct_obj.srcLoc(mod), msg, "struct declared here", .{});
   5061         break :msg msg;
   5062     };
   5063     return sema.failWithOwnedErrorMsg(msg);
   5064 }
   5065 
   5066 fn failWithBadUnionFieldAccess(
   5067     sema: *Sema,
   5068     block: *Block,
   5069     union_obj: *Module.Union,
   5070     field_src: LazySrcLoc,
   5071     field_name: InternPool.NullTerminatedString,
   5072 ) CompileError {
   5073     const mod = sema.mod;
   5074     const gpa = sema.gpa;
   5075 
   5076     const fqn = try union_obj.getFullyQualifiedName(mod);
   5077 
   5078     const msg = msg: {
   5079         const msg = try sema.errMsg(
   5080             block,
   5081             field_src,
   5082             "no field named '{}' in union '{}'",
   5083             .{ field_name.fmt(&mod.intern_pool), fqn.fmt(&mod.intern_pool) },
   5084         );
   5085         errdefer msg.destroy(gpa);
   5086         try mod.errNoteNonLazy(union_obj.srcLoc(mod), msg, "union declared here", .{});
   5087         break :msg msg;
   5088     };
   5089     return sema.failWithOwnedErrorMsg(msg);
   5090 }
   5091 
   5092 fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !void {
   5093     const mod = sema.mod;
   5094     const src_loc = decl_ty.declSrcLocOrNull(mod) orelse return;
   5095     const category = switch (decl_ty.zigTypeTag(mod)) {
   5096         .Union => "union",
   5097         .Struct => "struct",
   5098         .Enum => "enum",
   5099         .Opaque => "opaque",
   5100         .ErrorSet => "error set",
   5101         else => unreachable,
   5102     };
   5103     try mod.errNoteNonLazy(src_loc, parent, "{s} declared here", .{category});
   5104 }
   5105 
   5106 fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5107     const tracy = trace(@src());
   5108     defer tracy.end();
   5109 
   5110     const bin_inst = sema.code.instructions.items(.data)[inst].bin;
   5111     const ptr = sema.inst_map.get(Zir.refToIndex(bin_inst.lhs).?) orelse {
   5112         // This is an elided instruction, but AstGen was unable to omit it.
   5113         return;
   5114     };
   5115     const operand = try sema.resolveInst(bin_inst.rhs);
   5116     const src: LazySrcLoc = sema.src;
   5117     blk: {
   5118         const ptr_inst = Air.refToIndex(ptr) orelse break :blk;
   5119         switch (sema.air_instructions.items(.tag)[ptr_inst]) {
   5120             .inferred_alloc_comptime => {
   5121                 const iac = &sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime;
   5122                 return sema.storeToInferredAllocComptime(block, src, operand, iac);
   5123             },
   5124             .inferred_alloc => {
   5125                 const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?;
   5126                 return sema.storeToInferredAlloc(block, ptr, operand, ia);
   5127             },
   5128             else => break :blk,
   5129         }
   5130     }
   5131 
   5132     return sema.storePtr(block, src, ptr, operand);
   5133 }
   5134 
   5135 fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5136     const tracy = trace(@src());
   5137     defer tracy.end();
   5138 
   5139     const src: LazySrcLoc = sema.src;
   5140     const bin_inst = sema.code.instructions.items(.data)[inst].bin;
   5141     const ptr = try sema.resolveInst(bin_inst.lhs);
   5142     const operand = try sema.resolveInst(bin_inst.rhs);
   5143     const ptr_inst = Air.refToIndex(ptr).?;
   5144     const air_datas = sema.air_instructions.items(.data);
   5145 
   5146     switch (sema.air_instructions.items(.tag)[ptr_inst]) {
   5147         .inferred_alloc_comptime => {
   5148             const iac = &air_datas[ptr_inst].inferred_alloc_comptime;
   5149             return sema.storeToInferredAllocComptime(block, src, operand, iac);
   5150         },
   5151         .inferred_alloc => {
   5152             const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?;
   5153             return sema.storeToInferredAlloc(block, ptr, operand, ia);
   5154         },
   5155         else => unreachable,
   5156     }
   5157 }
   5158 
   5159 fn storeToInferredAlloc(
   5160     sema: *Sema,
   5161     block: *Block,
   5162     ptr: Air.Inst.Ref,
   5163     operand: Air.Inst.Ref,
   5164     inferred_alloc: *InferredAlloc,
   5165 ) CompileError!void {
   5166     // Create a store instruction as a placeholder.  This will be replaced by a
   5167     // proper store sequence once we know the stored type.
   5168     const dummy_store = try block.addBinOp(.store, ptr, operand);
   5169     // Add the stored instruction to the set we will use to resolve peer types
   5170     // for the inferred allocation.
   5171     try inferred_alloc.prongs.append(sema.arena, .{
   5172         .stored_inst = operand,
   5173         .placeholder = Air.refToIndex(dummy_store).?,
   5174     });
   5175 }
   5176 
   5177 fn storeToInferredAllocComptime(
   5178     sema: *Sema,
   5179     block: *Block,
   5180     src: LazySrcLoc,
   5181     operand: Air.Inst.Ref,
   5182     iac: *Air.Inst.Data.InferredAllocComptime,
   5183 ) CompileError!void {
   5184     const operand_ty = sema.typeOf(operand);
   5185     // There will be only one store_to_inferred_ptr because we are running at comptime.
   5186     // The alloc will turn into a Decl.
   5187     if (try sema.resolveMaybeUndefValAllowVariables(operand)) |operand_val| store: {
   5188         if (operand_val.getVariable(sema.mod) != null) break :store;
   5189         var anon_decl = try block.startAnonDecl();
   5190         defer anon_decl.deinit();
   5191         iac.decl_index = try anon_decl.finish(operand_ty, operand_val, iac.alignment);
   5192         try sema.comptime_mutable_decls.append(iac.decl_index);
   5193         return;
   5194     }
   5195 
   5196     return sema.failWithNeededComptime(block, src, "value being stored to a comptime variable must be comptime-known");
   5197 }
   5198 
   5199 fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5200     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   5201     const src = inst_data.src();
   5202     const quota = @as(u32, @intCast(try sema.resolveInt(block, src, inst_data.operand, Type.u32, "eval branch quota must be comptime-known")));
   5203     sema.branch_quota = @max(sema.branch_quota, quota);
   5204 }
   5205 
   5206 fn zirStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5207     const tracy = trace(@src());
   5208     defer tracy.end();
   5209 
   5210     const bin_inst = sema.code.instructions.items(.data)[inst].bin;
   5211     const ptr = try sema.resolveInst(bin_inst.lhs);
   5212     const value = try sema.resolveInst(bin_inst.rhs);
   5213     return sema.storePtr(block, sema.src, ptr, value);
   5214 }
   5215 
   5216 fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5217     const tracy = trace(@src());
   5218     defer tracy.end();
   5219 
   5220     const mod = sema.mod;
   5221     const zir_tags = sema.code.instructions.items(.tag);
   5222     const zir_datas = sema.code.instructions.items(.data);
   5223     const inst_data = zir_datas[inst].pl_node;
   5224     const src = inst_data.src();
   5225     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   5226     const ptr = try sema.resolveInst(extra.lhs);
   5227     const operand = try sema.resolveInst(extra.rhs);
   5228 
   5229     const is_ret = if (Zir.refToIndex(extra.lhs)) |ptr_index|
   5230         zir_tags[ptr_index] == .ret_ptr
   5231     else
   5232         false;
   5233 
   5234     // Check for the possibility of this pattern:
   5235     //   %a = ret_ptr
   5236     //   %b = store(%a, %c)
   5237     // Where %c is an error union or error set. In such case we need to add
   5238     // to the current function's inferred error set, if any.
   5239     if (is_ret and (sema.typeOf(operand).zigTypeTag(mod) == .ErrorUnion or
   5240         sema.typeOf(operand).zigTypeTag(mod) == .ErrorSet) and
   5241         sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion)
   5242     {
   5243         try sema.addToInferredErrorSet(operand);
   5244     }
   5245 
   5246     const ptr_src: LazySrcLoc = .{ .node_offset_store_ptr = inst_data.src_node };
   5247     const operand_src: LazySrcLoc = .{ .node_offset_store_operand = inst_data.src_node };
   5248     const air_tag: Air.Inst.Tag = if (is_ret)
   5249         .ret_ptr
   5250     else if (block.wantSafety())
   5251         .store_safe
   5252     else
   5253         .store;
   5254     return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
   5255 }
   5256 
   5257 fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5258     const tracy = trace(@src());
   5259     defer tracy.end();
   5260 
   5261     const bytes = sema.code.instructions.items(.data)[inst].str.get(sema.code);
   5262     return sema.addStrLit(block, bytes);
   5263 }
   5264 
   5265 fn addStrLit(sema: *Sema, block: *Block, bytes: []const u8) CompileError!Air.Inst.Ref {
   5266     const mod = sema.mod;
   5267     const gpa = sema.gpa;
   5268     // TODO: write something like getCoercedInts to avoid needing to dupe
   5269     const duped_bytes = try sema.arena.dupe(u8, bytes);
   5270     const ty = try mod.arrayType(.{
   5271         .len = bytes.len,
   5272         .sentinel = .zero_u8,
   5273         .child = .u8_type,
   5274     });
   5275     const val = try mod.intern(.{ .aggregate = .{
   5276         .ty = ty.toIntern(),
   5277         .storage = .{ .bytes = duped_bytes },
   5278     } });
   5279     const gop = try mod.memoized_decls.getOrPut(gpa, val);
   5280     if (!gop.found_existing) {
   5281         const new_decl_index = try mod.createAnonymousDecl(block, .{
   5282             .ty = ty,
   5283             .val = val.toValue(),
   5284         });
   5285         gop.value_ptr.* = new_decl_index;
   5286         try mod.finalizeAnonDecl(new_decl_index);
   5287     }
   5288     return sema.analyzeDeclRef(gop.value_ptr.*);
   5289 }
   5290 
   5291 fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5292     _ = block;
   5293     const tracy = trace(@src());
   5294     defer tracy.end();
   5295 
   5296     const int = sema.code.instructions.items(.data)[inst].int;
   5297     return sema.addIntUnsigned(Type.comptime_int, int);
   5298 }
   5299 
   5300 fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5301     _ = block;
   5302     const tracy = trace(@src());
   5303     defer tracy.end();
   5304 
   5305     const mod = sema.mod;
   5306     const int = sema.code.instructions.items(.data)[inst].str;
   5307     const byte_count = int.len * @sizeOf(std.math.big.Limb);
   5308     const limb_bytes = sema.code.string_bytes[int.start..][0..byte_count];
   5309 
   5310     // TODO: this allocation and copy is only needed because the limbs may be unaligned.
   5311     // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these
   5312     // two lines can be removed.
   5313     const limbs = try sema.arena.alloc(std.math.big.Limb, int.len);
   5314     @memcpy(mem.sliceAsBytes(limbs), limb_bytes);
   5315 
   5316     return sema.addConstant(
   5317         try mod.intValue_big(Type.comptime_int, .{
   5318             .limbs = limbs,
   5319             .positive = true,
   5320         }),
   5321     );
   5322 }
   5323 
   5324 fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5325     _ = block;
   5326     const number = sema.code.instructions.items(.data)[inst].float;
   5327     return sema.addConstant(
   5328         try sema.mod.floatValue(Type.comptime_float, number),
   5329     );
   5330 }
   5331 
   5332 fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5333     _ = block;
   5334     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   5335     const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data;
   5336     const number = extra.get();
   5337     return sema.addConstant(
   5338         try sema.mod.floatValue(Type.comptime_float, number),
   5339     );
   5340 }
   5341 
   5342 fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
   5343     const tracy = trace(@src());
   5344     defer tracy.end();
   5345 
   5346     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   5347     const src = inst_data.src();
   5348     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   5349     const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, "compile error string must be comptime-known");
   5350     return sema.fail(block, src, "{s}", .{msg});
   5351 }
   5352 
   5353 fn zirCompileLog(
   5354     sema: *Sema,
   5355     extended: Zir.Inst.Extended.InstData,
   5356 ) CompileError!Air.Inst.Ref {
   5357     const mod = sema.mod;
   5358 
   5359     var managed = mod.compile_log_text.toManaged(sema.gpa);
   5360     defer sema.mod.compile_log_text = managed.moveToUnmanaged();
   5361     const writer = managed.writer();
   5362 
   5363     const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
   5364     const src_node = extra.data.src_node;
   5365     const args = sema.code.refSlice(extra.end, extended.small);
   5366 
   5367     for (args, 0..) |arg_ref, i| {
   5368         if (i != 0) try writer.print(", ", .{});
   5369 
   5370         const arg = try sema.resolveInst(arg_ref);
   5371         const arg_ty = sema.typeOf(arg);
   5372         if (try sema.resolveMaybeUndefLazyVal(arg)) |val| {
   5373             try writer.print("@as({}, {})", .{
   5374                 arg_ty.fmt(mod), val.fmtValue(arg_ty, mod),
   5375             });
   5376         } else {
   5377             try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(mod)});
   5378         }
   5379     }
   5380     try writer.print("\n", .{});
   5381 
   5382     const decl_index = if (sema.func) |some| some.owner_decl else sema.owner_decl_index;
   5383     const gop = try mod.compile_log_decls.getOrPut(sema.gpa, decl_index);
   5384     if (!gop.found_existing) {
   5385         gop.value_ptr.* = src_node;
   5386     }
   5387     return Air.Inst.Ref.void_value;
   5388 }
   5389 
   5390 fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
   5391     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   5392     const src = inst_data.src();
   5393     const msg_inst = try sema.resolveInst(inst_data.operand);
   5394 
   5395     if (block.is_comptime) {
   5396         return sema.fail(block, src, "encountered @panic at comptime", .{});
   5397     }
   5398     try sema.panicWithMsg(block, msg_inst);
   5399     return always_noreturn;
   5400 }
   5401 
   5402 fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
   5403     const src_node = sema.code.instructions.items(.data)[inst].node;
   5404     const src = LazySrcLoc.nodeOffset(src_node);
   5405     sema.src = src;
   5406     _ = try block.addNoOp(.trap);
   5407     return always_noreturn;
   5408 }
   5409 
   5410 fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5411     const tracy = trace(@src());
   5412     defer tracy.end();
   5413 
   5414     const mod = sema.mod;
   5415     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   5416     const src = inst_data.src();
   5417     const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   5418     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
   5419     const gpa = sema.gpa;
   5420 
   5421     // AIR expects a block outside the loop block too.
   5422     // Reserve space for a Loop instruction so that generated Break instructions can
   5423     // point to it, even if it doesn't end up getting used because the code ends up being
   5424     // comptime evaluated.
   5425     const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
   5426     const loop_inst = block_inst + 1;
   5427     try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
   5428     sema.air_instructions.appendAssumeCapacity(.{
   5429         .tag = .block,
   5430         .data = undefined,
   5431     });
   5432     sema.air_instructions.appendAssumeCapacity(.{
   5433         .tag = .loop,
   5434         .data = .{ .ty_pl = .{
   5435             .ty = .noreturn_type,
   5436             .payload = undefined,
   5437         } },
   5438     });
   5439     var label: Block.Label = .{
   5440         .zir_block = inst,
   5441         .merges = .{
   5442             .src_locs = .{},
   5443             .results = .{},
   5444             .br_list = .{},
   5445             .block_inst = block_inst,
   5446         },
   5447     };
   5448     var child_block = parent_block.makeSubBlock();
   5449     child_block.label = &label;
   5450     child_block.runtime_cond = null;
   5451     child_block.runtime_loop = src;
   5452     child_block.runtime_index.increment();
   5453     const merges = &child_block.label.?.merges;
   5454 
   5455     defer child_block.instructions.deinit(gpa);
   5456     defer merges.deinit(gpa);
   5457 
   5458     var loop_block = child_block.makeSubBlock();
   5459     defer loop_block.instructions.deinit(gpa);
   5460 
   5461     try sema.analyzeBody(&loop_block, body);
   5462 
   5463     const loop_block_len = loop_block.instructions.items.len;
   5464     if (loop_block_len > 0 and sema.typeOf(Air.indexToRef(loop_block.instructions.items[loop_block_len - 1])).isNoReturn(mod)) {
   5465         // If the loop ended with a noreturn terminator, then there is no way for it to loop,
   5466         // so we can just use the block instead.
   5467         try child_block.instructions.appendSlice(gpa, loop_block.instructions.items);
   5468     } else {
   5469         try child_block.instructions.append(gpa, loop_inst);
   5470 
   5471         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + loop_block_len);
   5472         sema.air_instructions.items(.data)[loop_inst].ty_pl.payload = sema.addExtraAssumeCapacity(
   5473             Air.Block{ .body_len = @as(u32, @intCast(loop_block_len)) },
   5474         );
   5475         sema.air_extra.appendSliceAssumeCapacity(loop_block.instructions.items);
   5476     }
   5477     return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
   5478 }
   5479 
   5480 fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5481     const tracy = trace(@src());
   5482     defer tracy.end();
   5483 
   5484     const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
   5485     const src = pl_node.src();
   5486     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
   5487     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
   5488 
   5489     // we check this here to avoid undefined symbols
   5490     if (!@import("build_options").have_llvm)
   5491         return sema.fail(parent_block, src, "C import unavailable; Zig compiler built without LLVM extensions", .{});
   5492 
   5493     var c_import_buf = std.ArrayList(u8).init(sema.gpa);
   5494     defer c_import_buf.deinit();
   5495 
   5496     var comptime_reason: Block.ComptimeReason = .{ .c_import = .{
   5497         .block = parent_block,
   5498         .src = src,
   5499     } };
   5500     var child_block: Block = .{
   5501         .parent = parent_block,
   5502         .sema = sema,
   5503         .src_decl = parent_block.src_decl,
   5504         .namespace = parent_block.namespace,
   5505         .wip_capture_scope = parent_block.wip_capture_scope,
   5506         .instructions = .{},
   5507         .inlining = parent_block.inlining,
   5508         .is_comptime = true,
   5509         .comptime_reason = &comptime_reason,
   5510         .c_import_buf = &c_import_buf,
   5511         .runtime_cond = parent_block.runtime_cond,
   5512         .runtime_loop = parent_block.runtime_loop,
   5513         .runtime_index = parent_block.runtime_index,
   5514     };
   5515     defer child_block.instructions.deinit(sema.gpa);
   5516 
   5517     // Ignore the result, all the relevant operations have written to c_import_buf already.
   5518     _ = try sema.analyzeBodyBreak(&child_block, body);
   5519 
   5520     const mod = sema.mod;
   5521     const c_import_res = mod.comp.cImport(c_import_buf.items) catch |err|
   5522         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
   5523 
   5524     if (c_import_res.errors.len != 0) {
   5525         const msg = msg: {
   5526             defer @import("clang.zig").ErrorMsg.delete(c_import_res.errors.ptr, c_import_res.errors.len);
   5527 
   5528             const msg = try sema.errMsg(&child_block, src, "C import failed", .{});
   5529             errdefer msg.destroy(sema.gpa);
   5530 
   5531             if (!mod.comp.bin_file.options.link_libc)
   5532                 try sema.errNote(&child_block, src, msg, "libc headers not available; compilation does not link against libc", .{});
   5533 
   5534             const gop = try mod.cimport_errors.getOrPut(sema.gpa, sema.owner_decl_index);
   5535             if (!gop.found_existing) {
   5536                 var errs = try std.ArrayListUnmanaged(Module.CImportError).initCapacity(sema.gpa, c_import_res.errors.len);
   5537                 errdefer {
   5538                     for (errs.items) |err| err.deinit(sema.gpa);
   5539                     errs.deinit(sema.gpa);
   5540                 }
   5541 
   5542                 for (c_import_res.errors) |c_error| {
   5543                     const path = if (c_error.filename_ptr) |some|
   5544                         try sema.gpa.dupeZ(u8, some[0..c_error.filename_len])
   5545                     else
   5546                         null;
   5547                     errdefer if (path) |some| sema.gpa.free(some);
   5548 
   5549                     const c_msg = try sema.gpa.dupeZ(u8, c_error.msg_ptr[0..c_error.msg_len]);
   5550                     errdefer sema.gpa.free(c_msg);
   5551 
   5552                     const line = line: {
   5553                         const source = c_error.source orelse break :line null;
   5554                         var start = c_error.offset;
   5555                         while (start > 0) : (start -= 1) {
   5556                             if (source[start - 1] == '\n') break;
   5557                         }
   5558                         var end = c_error.offset;
   5559                         while (true) : (end += 1) {
   5560                             if (source[end] == 0) break;
   5561                             if (source[end] == '\n') break;
   5562                         }
   5563                         break :line try sema.gpa.dupeZ(u8, source[start..end]);
   5564                     };
   5565                     errdefer if (line) |some| sema.gpa.free(some);
   5566 
   5567                     errs.appendAssumeCapacity(.{
   5568                         .path = path orelse null,
   5569                         .source_line = line orelse null,
   5570                         .line = c_error.line,
   5571                         .column = c_error.column,
   5572                         .offset = c_error.offset,
   5573                         .msg = c_msg,
   5574                     });
   5575                 }
   5576                 gop.value_ptr.* = errs.items;
   5577             }
   5578             break :msg msg;
   5579         };
   5580         return sema.failWithOwnedErrorMsg(msg);
   5581     }
   5582     const c_import_pkg = Package.create(
   5583         sema.gpa,
   5584         null,
   5585         c_import_res.out_zig_path,
   5586     ) catch |err| switch (err) {
   5587         error.OutOfMemory => return error.OutOfMemory,
   5588         else => unreachable, // we pass null for root_src_dir_path
   5589     };
   5590 
   5591     const result = mod.importPkg(c_import_pkg) catch |err|
   5592         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
   5593 
   5594     mod.astGenFile(result.file) catch |err|
   5595         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
   5596 
   5597     try mod.semaFile(result.file);
   5598     const file_root_decl_index = result.file.root_decl.unwrap().?;
   5599     const file_root_decl = mod.declPtr(file_root_decl_index);
   5600     try mod.declareDeclDependency(sema.owner_decl_index, file_root_decl_index);
   5601     return sema.addConstant(file_root_decl.val);
   5602 }
   5603 
   5604 fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5605     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   5606     const src = inst_data.src();
   5607     return sema.failWithUseOfAsync(parent_block, src);
   5608 }
   5609 
   5610 fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index, force_comptime: bool) CompileError!Air.Inst.Ref {
   5611     const tracy = trace(@src());
   5612     defer tracy.end();
   5613 
   5614     const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
   5615     const src = pl_node.src();
   5616     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
   5617     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
   5618     const gpa = sema.gpa;
   5619 
   5620     // Reserve space for a Block instruction so that generated Break instructions can
   5621     // point to it, even if it doesn't end up getting used because the code ends up being
   5622     // comptime evaluated or is an unlabeled block.
   5623     const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
   5624     try sema.air_instructions.append(gpa, .{
   5625         .tag = .block,
   5626         .data = undefined,
   5627     });
   5628 
   5629     var label: Block.Label = .{
   5630         .zir_block = inst,
   5631         .merges = .{
   5632             .src_locs = .{},
   5633             .results = .{},
   5634             .br_list = .{},
   5635             .block_inst = block_inst,
   5636         },
   5637     };
   5638 
   5639     var child_block: Block = .{
   5640         .parent = parent_block,
   5641         .sema = sema,
   5642         .src_decl = parent_block.src_decl,
   5643         .namespace = parent_block.namespace,
   5644         .wip_capture_scope = parent_block.wip_capture_scope,
   5645         .instructions = .{},
   5646         .label = &label,
   5647         .inlining = parent_block.inlining,
   5648         .is_comptime = parent_block.is_comptime or force_comptime,
   5649         .comptime_reason = parent_block.comptime_reason,
   5650         .is_typeof = parent_block.is_typeof,
   5651         .want_safety = parent_block.want_safety,
   5652         .float_mode = parent_block.float_mode,
   5653         .c_import_buf = parent_block.c_import_buf,
   5654         .runtime_cond = parent_block.runtime_cond,
   5655         .runtime_loop = parent_block.runtime_loop,
   5656         .runtime_index = parent_block.runtime_index,
   5657         .error_return_trace_index = parent_block.error_return_trace_index,
   5658     };
   5659 
   5660     defer child_block.instructions.deinit(gpa);
   5661     defer label.merges.deinit(gpa);
   5662 
   5663     return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
   5664 }
   5665 
   5666 fn resolveBlockBody(
   5667     sema: *Sema,
   5668     parent_block: *Block,
   5669     src: LazySrcLoc,
   5670     child_block: *Block,
   5671     body: []const Zir.Inst.Index,
   5672     /// This is the instruction that a break instruction within `body` can
   5673     /// use to return from the body.
   5674     body_inst: Zir.Inst.Index,
   5675     merges: *Block.Merges,
   5676 ) CompileError!Air.Inst.Ref {
   5677     if (child_block.is_comptime) {
   5678         return sema.resolveBody(child_block, body, body_inst);
   5679     } else {
   5680         if (sema.analyzeBodyInner(child_block, body)) |_| {
   5681             return sema.analyzeBlockBody(parent_block, src, child_block, merges);
   5682         } else |err| switch (err) {
   5683             error.ComptimeBreak => {
   5684                 // Comptime control flow is happening, however child_block may still contain
   5685                 // runtime instructions which need to be copied to the parent block.
   5686                 try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items);
   5687 
   5688                 const break_inst = sema.comptime_break_inst;
   5689                 const break_data = sema.code.instructions.items(.data)[break_inst].@"break";
   5690                 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
   5691                 if (extra.block_inst == body_inst) {
   5692                     return try sema.resolveInst(break_data.operand);
   5693                 } else {
   5694                     return error.ComptimeBreak;
   5695                 }
   5696             },
   5697             else => |e| return e,
   5698         }
   5699     }
   5700 }
   5701 
   5702 fn analyzeBlockBody(
   5703     sema: *Sema,
   5704     parent_block: *Block,
   5705     src: LazySrcLoc,
   5706     child_block: *Block,
   5707     merges: *Block.Merges,
   5708 ) CompileError!Air.Inst.Ref {
   5709     const tracy = trace(@src());
   5710     defer tracy.end();
   5711 
   5712     const gpa = sema.gpa;
   5713     const mod = sema.mod;
   5714 
   5715     // Blocks must terminate with noreturn instruction.
   5716     assert(child_block.instructions.items.len != 0);
   5717     assert(sema.typeOf(Air.indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1])).isNoReturn(mod));
   5718 
   5719     if (merges.results.items.len == 0) {
   5720         // No need for a block instruction. We can put the new instructions
   5721         // directly into the parent block.
   5722         try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);
   5723         return Air.indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1]);
   5724     }
   5725     if (merges.results.items.len == 1) {
   5726         const last_inst_index = child_block.instructions.items.len - 1;
   5727         const last_inst = child_block.instructions.items[last_inst_index];
   5728         if (sema.getBreakBlock(last_inst)) |br_block| {
   5729             if (br_block == merges.block_inst) {
   5730                 // No need for a block instruction. We can put the new instructions directly
   5731                 // into the parent block. Here we omit the break instruction.
   5732                 const without_break = child_block.instructions.items[0..last_inst_index];
   5733                 try parent_block.instructions.appendSlice(gpa, without_break);
   5734                 return merges.results.items[0];
   5735             }
   5736         }
   5737     }
   5738     // It is impossible to have the number of results be > 1 in a comptime scope.
   5739     assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition.
   5740 
   5741     // Need to set the type and emit the Block instruction. This allows machine code generation
   5742     // to emit a jump instruction to after the block when it encounters the break.
   5743     try parent_block.instructions.append(gpa, merges.block_inst);
   5744     const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items });
   5745     // TODO add note "missing else causes void value"
   5746 
   5747     const type_src = src; // TODO: better source location
   5748     const valid_rt = try sema.validateRunTimeType(resolved_ty, false);
   5749     if (!valid_rt) {
   5750         const msg = msg: {
   5751             const msg = try sema.errMsg(child_block, type_src, "value with comptime-only type '{}' depends on runtime control flow", .{resolved_ty.fmt(mod)});
   5752             errdefer msg.destroy(sema.gpa);
   5753 
   5754             const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?;
   5755             try sema.errNote(child_block, runtime_src, msg, "runtime control flow here", .{});
   5756 
   5757             const child_src_decl = mod.declPtr(child_block.src_decl);
   5758             try sema.explainWhyTypeIsComptime(msg, type_src.toSrcLoc(child_src_decl, mod), resolved_ty);
   5759 
   5760             break :msg msg;
   5761         };
   5762         return sema.failWithOwnedErrorMsg(msg);
   5763     }
   5764     const ty_inst = try sema.addType(resolved_ty);
   5765     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
   5766         child_block.instructions.items.len);
   5767     sema.air_instructions.items(.data)[merges.block_inst] = .{ .ty_pl = .{
   5768         .ty = ty_inst,
   5769         .payload = sema.addExtraAssumeCapacity(Air.Block{
   5770             .body_len = @as(u32, @intCast(child_block.instructions.items.len)),
   5771         }),
   5772     } };
   5773     sema.air_extra.appendSliceAssumeCapacity(child_block.instructions.items);
   5774     // Now that the block has its type resolved, we need to go back into all the break
   5775     // instructions, and insert type coercion on the operands.
   5776     for (merges.br_list.items) |br| {
   5777         const br_operand = sema.air_instructions.items(.data)[br].br.operand;
   5778         const br_operand_src = src;
   5779         const br_operand_ty = sema.typeOf(br_operand);
   5780         if (br_operand_ty.eql(resolved_ty, mod)) {
   5781             // No type coercion needed.
   5782             continue;
   5783         }
   5784         var coerce_block = parent_block.makeSubBlock();
   5785         defer coerce_block.instructions.deinit(gpa);
   5786         const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src);
   5787         // If no instructions were produced, such as in the case of a coercion of a
   5788         // constant value to a new type, we can simply point the br operand to it.
   5789         if (coerce_block.instructions.items.len == 0) {
   5790             sema.air_instructions.items(.data)[br].br.operand = coerced_operand;
   5791             continue;
   5792         }
   5793         assert(Air.indexToRef(coerce_block.instructions.items[coerce_block.instructions.items.len - 1]) == coerced_operand);
   5794 
   5795         // Convert the br instruction to a block instruction that has the coercion
   5796         // and then a new br inside that returns the coerced instruction.
   5797         const sub_block_len = @as(u32, @intCast(coerce_block.instructions.items.len + 1));
   5798         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
   5799             sub_block_len);
   5800         try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
   5801         const sub_br_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
   5802 
   5803         sema.air_instructions.items(.tag)[br] = .block;
   5804         sema.air_instructions.items(.data)[br] = .{ .ty_pl = .{
   5805             .ty = Air.Inst.Ref.noreturn_type,
   5806             .payload = sema.addExtraAssumeCapacity(Air.Block{
   5807                 .body_len = sub_block_len,
   5808             }),
   5809         } };
   5810         sema.air_extra.appendSliceAssumeCapacity(coerce_block.instructions.items);
   5811         sema.air_extra.appendAssumeCapacity(sub_br_inst);
   5812 
   5813         sema.air_instructions.appendAssumeCapacity(.{
   5814             .tag = .br,
   5815             .data = .{ .br = .{
   5816                 .block_inst = merges.block_inst,
   5817                 .operand = coerced_operand,
   5818             } },
   5819         });
   5820     }
   5821     return Air.indexToRef(merges.block_inst);
   5822 }
   5823 
   5824 fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5825     const tracy = trace(@src());
   5826     defer tracy.end();
   5827 
   5828     const mod = sema.mod;
   5829     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   5830     const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
   5831     const src = inst_data.src();
   5832     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   5833     const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
   5834     const decl_name = try mod.intern_pool.getOrPutString(mod.gpa, sema.code.nullTerminatedString(extra.decl_name));
   5835     const decl_index = if (extra.namespace != .none) index_blk: {
   5836         const container_ty = try sema.resolveType(block, operand_src, extra.namespace);
   5837         const container_namespace = container_ty.getNamespaceIndex(mod).unwrap().?;
   5838 
   5839         const maybe_index = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false);
   5840         break :index_blk maybe_index orelse
   5841             return sema.failWithBadMemberAccess(block, container_ty, operand_src, decl_name);
   5842     } else try sema.lookupIdentifier(block, operand_src, decl_name);
   5843     const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) {
   5844         error.NeededSourceLocation => {
   5845             _ = try sema.resolveExportOptions(block, options_src, extra.options);
   5846             unreachable;
   5847         },
   5848         else => |e| return e,
   5849     };
   5850     {
   5851         try mod.ensureDeclAnalyzed(decl_index);
   5852         const exported_decl = mod.declPtr(decl_index);
   5853         if (exported_decl.val.getFunction(mod)) |function| {
   5854             return sema.analyzeExport(block, src, options, function.owner_decl);
   5855         }
   5856     }
   5857     try sema.analyzeExport(block, src, options, decl_index);
   5858 }
   5859 
   5860 fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5861     const tracy = trace(@src());
   5862     defer tracy.end();
   5863 
   5864     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   5865     const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;
   5866     const src = inst_data.src();
   5867     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   5868     const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
   5869     const operand = try sema.resolveInstConst(block, operand_src, extra.operand, "export target must be comptime-known");
   5870     const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) {
   5871         error.NeededSourceLocation => {
   5872             _ = try sema.resolveExportOptions(block, options_src, extra.options);
   5873             unreachable;
   5874         },
   5875         else => |e| return e,
   5876     };
   5877     const decl_index = if (operand.val.getFunction(sema.mod)) |function| function.owner_decl else blk: {
   5878         var anon_decl = try block.startAnonDecl();
   5879         defer anon_decl.deinit();
   5880         break :blk try anon_decl.finish(operand.ty, operand.val, .none);
   5881     };
   5882     try sema.analyzeExport(block, src, options, decl_index);
   5883 }
   5884 
   5885 pub fn analyzeExport(
   5886     sema: *Sema,
   5887     block: *Block,
   5888     src: LazySrcLoc,
   5889     options: Module.Export.Options,
   5890     exported_decl_index: Decl.Index,
   5891 ) !void {
   5892     const Export = Module.Export;
   5893     const mod = sema.mod;
   5894 
   5895     if (options.linkage == .Internal) {
   5896         return;
   5897     }
   5898 
   5899     try mod.ensureDeclAnalyzed(exported_decl_index);
   5900     const exported_decl = mod.declPtr(exported_decl_index);
   5901 
   5902     if (!try sema.validateExternType(exported_decl.ty, .other)) {
   5903         const msg = msg: {
   5904             const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(mod)});
   5905             errdefer msg.destroy(sema.gpa);
   5906 
   5907             const src_decl = mod.declPtr(block.src_decl);
   5908             try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), exported_decl.ty, .other);
   5909 
   5910             try sema.addDeclaredHereNote(msg, exported_decl.ty);
   5911             break :msg msg;
   5912         };
   5913         return sema.failWithOwnedErrorMsg(msg);
   5914     }
   5915 
   5916     // TODO: some backends might support re-exporting extern decls
   5917     if (exported_decl.isExtern(mod)) {
   5918         return sema.fail(block, src, "export target cannot be extern", .{});
   5919     }
   5920 
   5921     // This decl is alive no matter what, since it's being exported
   5922     try mod.markDeclAlive(exported_decl);
   5923     try sema.maybeQueueFuncBodyAnalysis(exported_decl_index);
   5924 
   5925     const gpa = sema.gpa;
   5926 
   5927     try mod.decl_exports.ensureUnusedCapacity(gpa, 1);
   5928     try mod.export_owners.ensureUnusedCapacity(gpa, 1);
   5929 
   5930     const new_export = try gpa.create(Export);
   5931     errdefer gpa.destroy(new_export);
   5932 
   5933     new_export.* = .{
   5934         .opts = options,
   5935         .src = src,
   5936         .owner_decl = sema.owner_decl_index,
   5937         .src_decl = block.src_decl,
   5938         .exported_decl = exported_decl_index,
   5939         .status = .in_progress,
   5940     };
   5941 
   5942     // Add to export_owners table.
   5943     const eo_gop = mod.export_owners.getOrPutAssumeCapacity(sema.owner_decl_index);
   5944     if (!eo_gop.found_existing) {
   5945         eo_gop.value_ptr.* = .{};
   5946     }
   5947     try eo_gop.value_ptr.append(gpa, new_export);
   5948     errdefer _ = eo_gop.value_ptr.pop();
   5949 
   5950     // Add to exported_decl table.
   5951     const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl_index);
   5952     if (!de_gop.found_existing) {
   5953         de_gop.value_ptr.* = .{};
   5954     }
   5955     try de_gop.value_ptr.append(gpa, new_export);
   5956     errdefer _ = de_gop.value_ptr.pop();
   5957 }
   5958 
   5959 fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
   5960     const mod = sema.mod;
   5961     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   5962     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   5963     const src = LazySrcLoc.nodeOffset(extra.node);
   5964     const alignment = try sema.resolveAlign(block, operand_src, extra.operand);
   5965     if (alignment.order(Alignment.fromNonzeroByteUnits(256)).compare(.gt)) {
   5966         return sema.fail(block, src, "attempt to @setAlignStack({d}); maximum is 256", .{
   5967             alignment.toByteUnitsOptional().?,
   5968         });
   5969     }
   5970     const func_index = sema.func_index.unwrap() orelse
   5971         return sema.fail(block, src, "@setAlignStack outside function body", .{});
   5972     const func = mod.funcPtr(func_index);
   5973 
   5974     const fn_owner_decl = mod.declPtr(func.owner_decl);
   5975     switch (fn_owner_decl.ty.fnCallingConvention(mod)) {
   5976         .Naked => return sema.fail(block, src, "@setAlignStack in naked function", .{}),
   5977         .Inline => return sema.fail(block, src, "@setAlignStack in inline function", .{}),
   5978         else => if (block.inlining != null) {
   5979             return sema.fail(block, src, "@setAlignStack in inline call", .{});
   5980         },
   5981     }
   5982 
   5983     const gop = try mod.align_stack_fns.getOrPut(sema.gpa, func_index);
   5984     if (gop.found_existing) {
   5985         const msg = msg: {
   5986             const msg = try sema.errMsg(block, src, "multiple @setAlignStack in the same function body", .{});
   5987             errdefer msg.destroy(sema.gpa);
   5988             try sema.errNote(block, gop.value_ptr.src, msg, "other instance here", .{});
   5989             break :msg msg;
   5990         };
   5991         return sema.failWithOwnedErrorMsg(msg);
   5992     }
   5993     gop.value_ptr.* = .{ .alignment = alignment, .src = src };
   5994 }
   5995 
   5996 fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
   5997     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   5998     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   5999     const is_cold = try sema.resolveConstBool(block, operand_src, extra.operand, "operand to @setCold must be comptime-known");
   6000     const func = sema.func orelse return; // does nothing outside a function
   6001     func.is_cold = is_cold;
   6002 }
   6003 
   6004 fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
   6005     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   6006     const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   6007     block.float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, "FloatMode", "operand to @setFloatMode must be comptime-known");
   6008 }
   6009 
   6010 fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6011     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   6012     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   6013     block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, "operand to @setRuntimeSafety must be comptime-known");
   6014 }
   6015 
   6016 fn zirFence(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
   6017     if (block.is_comptime) return;
   6018 
   6019     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   6020     const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   6021     const order = try sema.resolveAtomicOrder(block, order_src, extra.operand, "atomic order of @fence must be comptime-known");
   6022 
   6023     if (@intFromEnum(order) < @intFromEnum(std.builtin.AtomicOrder.Acquire)) {
   6024         return sema.fail(block, order_src, "atomic ordering must be Acquire or stricter", .{});
   6025     }
   6026 
   6027     _ = try block.addInst(.{
   6028         .tag = .fence,
   6029         .data = .{ .fence = order },
   6030     });
   6031 }
   6032 
   6033 fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
   6034     const tracy = trace(@src());
   6035     defer tracy.end();
   6036 
   6037     const inst_data = sema.code.instructions.items(.data)[inst].@"break";
   6038     const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data;
   6039     const operand = try sema.resolveInst(inst_data.operand);
   6040     const zir_block = extra.block_inst;
   6041 
   6042     var block = start_block;
   6043     while (true) {
   6044         if (block.label) |label| {
   6045             if (label.zir_block == zir_block) {
   6046                 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
   6047                 const src_loc = if (extra.operand_src_node != Zir.Inst.Break.no_src_node)
   6048                     LazySrcLoc.nodeOffset(extra.operand_src_node)
   6049                 else
   6050                     null;
   6051                 try label.merges.src_locs.append(sema.gpa, src_loc);
   6052                 try label.merges.results.append(sema.gpa, operand);
   6053                 try label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?);
   6054                 block.runtime_index.increment();
   6055                 if (block.runtime_cond == null and block.runtime_loop == null) {
   6056                     block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop;
   6057                     block.runtime_loop = start_block.runtime_loop;
   6058                 }
   6059                 return inst;
   6060             }
   6061         }
   6062         block = block.parent.?;
   6063     }
   6064 }
   6065 
   6066 fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6067     // We do not set sema.src here because dbg_stmt instructions are only emitted for
   6068     // ZIR code that possibly will need to generate runtime code. So error messages
   6069     // and other source locations must not rely on sema.src being set from dbg_stmt
   6070     // instructions.
   6071     if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
   6072 
   6073     const inst_data = sema.code.instructions.items(.data)[inst].dbg_stmt;
   6074 
   6075     if (block.instructions.items.len != 0) {
   6076         const idx = block.instructions.items[block.instructions.items.len - 1];
   6077         if (sema.air_instructions.items(.tag)[idx] == .dbg_stmt) {
   6078             // The previous dbg_stmt didn't correspond to any actual code, so replace it.
   6079             sema.air_instructions.items(.data)[idx].dbg_stmt = .{
   6080                 .line = inst_data.line,
   6081                 .column = inst_data.column,
   6082             };
   6083             return;
   6084         }
   6085     }
   6086 
   6087     _ = try block.addInst(.{
   6088         .tag = .dbg_stmt,
   6089         .data = .{ .dbg_stmt = .{
   6090             .line = inst_data.line,
   6091             .column = inst_data.column,
   6092         } },
   6093     });
   6094 }
   6095 
   6096 fn zirDbgBlockBegin(sema: *Sema, block: *Block) CompileError!void {
   6097     if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
   6098 
   6099     _ = try block.addInst(.{
   6100         .tag = .dbg_block_begin,
   6101         .data = undefined,
   6102     });
   6103 }
   6104 
   6105 fn zirDbgBlockEnd(sema: *Sema, block: *Block) CompileError!void {
   6106     if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
   6107 
   6108     _ = try block.addInst(.{
   6109         .tag = .dbg_block_end,
   6110         .data = undefined,
   6111     });
   6112 }
   6113 
   6114 fn zirDbgVar(
   6115     sema: *Sema,
   6116     block: *Block,
   6117     inst: Zir.Inst.Index,
   6118     air_tag: Air.Inst.Tag,
   6119 ) CompileError!void {
   6120     if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
   6121 
   6122     const str_op = sema.code.instructions.items(.data)[inst].str_op;
   6123     const operand = try sema.resolveInst(str_op.operand);
   6124     const name = str_op.getStr(sema.code);
   6125     try sema.addDbgVar(block, operand, air_tag, name);
   6126 }
   6127 
   6128 fn addDbgVar(
   6129     sema: *Sema,
   6130     block: *Block,
   6131     operand: Air.Inst.Ref,
   6132     air_tag: Air.Inst.Tag,
   6133     name: []const u8,
   6134 ) CompileError!void {
   6135     const mod = sema.mod;
   6136     const operand_ty = sema.typeOf(operand);
   6137     switch (air_tag) {
   6138         .dbg_var_ptr => {
   6139             if (!(try sema.typeHasRuntimeBits(operand_ty.childType(mod)))) return;
   6140         },
   6141         .dbg_var_val => {
   6142             if (!(try sema.typeHasRuntimeBits(operand_ty))) return;
   6143         },
   6144         else => unreachable,
   6145     }
   6146 
   6147     try sema.queueFullTypeResolution(operand_ty);
   6148 
   6149     // Add the name to the AIR.
   6150     const name_extra_index = @as(u32, @intCast(sema.air_extra.items.len));
   6151     const elements_used = name.len / 4 + 1;
   6152     try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements_used);
   6153     const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
   6154     @memcpy(buffer[0..name.len], name);
   6155     buffer[name.len] = 0;
   6156     sema.air_extra.items.len += elements_used;
   6157 
   6158     _ = try block.addInst(.{
   6159         .tag = air_tag,
   6160         .data = .{ .pl_op = .{
   6161             .payload = name_extra_index,
   6162             .operand = operand,
   6163         } },
   6164     });
   6165 }
   6166 
   6167 fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   6168     const mod = sema.mod;
   6169     const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
   6170     const src = inst_data.src();
   6171     const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
   6172     const decl_index = try sema.lookupIdentifier(block, src, decl_name);
   6173     try sema.addReferencedBy(block, src, decl_index);
   6174     return sema.analyzeDeclRef(decl_index);
   6175 }
   6176 
   6177 fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   6178     const mod = sema.mod;
   6179     const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
   6180     const src = inst_data.src();
   6181     const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
   6182     const decl = try sema.lookupIdentifier(block, src, decl_name);
   6183     return sema.analyzeDeclVal(block, src, decl);
   6184 }
   6185 
   6186 fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPool.NullTerminatedString) !Decl.Index {
   6187     const mod = sema.mod;
   6188     var namespace = block.namespace;
   6189     while (true) {
   6190         if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl_index| {
   6191             return decl_index;
   6192         }
   6193         namespace = mod.namespacePtr(namespace).parent.unwrap() orelse break;
   6194     }
   6195     unreachable; // AstGen detects use of undeclared identifier errors.
   6196 }
   6197 
   6198 /// This looks up a member of a specific namespace. It is affected by `usingnamespace` but
   6199 /// only for ones in the specified namespace.
   6200 fn lookupInNamespace(
   6201     sema: *Sema,
   6202     block: *Block,
   6203     src: LazySrcLoc,
   6204     namespace_index: Namespace.Index,
   6205     ident_name: InternPool.NullTerminatedString,
   6206     observe_usingnamespace: bool,
   6207 ) CompileError!?Decl.Index {
   6208     const mod = sema.mod;
   6209 
   6210     const namespace = mod.namespacePtr(namespace_index);
   6211     const namespace_decl_index = namespace.getDeclIndex(mod);
   6212     const namespace_decl = mod.declPtr(namespace_decl_index);
   6213     if (namespace_decl.analysis == .file_failure) {
   6214         try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index);
   6215         return error.AnalysisFail;
   6216     }
   6217 
   6218     if (observe_usingnamespace and namespace.usingnamespace_set.count() != 0) {
   6219         const src_file = mod.namespacePtr(block.namespace).file_scope;
   6220 
   6221         const gpa = sema.gpa;
   6222         var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, bool) = .{};
   6223         defer checked_namespaces.deinit(gpa);
   6224 
   6225         // Keep track of name conflicts for error notes.
   6226         var candidates: std.ArrayListUnmanaged(Decl.Index) = .{};
   6227         defer candidates.deinit(gpa);
   6228 
   6229         try checked_namespaces.put(gpa, namespace, namespace.file_scope == src_file);
   6230         var check_i: usize = 0;
   6231 
   6232         while (check_i < checked_namespaces.count()) : (check_i += 1) {
   6233             const check_ns = checked_namespaces.keys()[check_i];
   6234             if (check_ns.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| {
   6235                 // Skip decls which are not marked pub, which are in a different
   6236                 // file than the `a.b`/`@hasDecl` syntax.
   6237                 const decl = mod.declPtr(decl_index);
   6238                 if (decl.is_pub or (src_file == decl.getFileScope(mod) and checked_namespaces.values()[check_i])) {
   6239                     try candidates.append(gpa, decl_index);
   6240                 }
   6241             }
   6242             var it = check_ns.usingnamespace_set.iterator();
   6243             while (it.next()) |entry| {
   6244                 const sub_usingnamespace_decl_index = entry.key_ptr.*;
   6245                 // Skip the decl we're currently analysing.
   6246                 if (sub_usingnamespace_decl_index == sema.owner_decl_index) continue;
   6247                 const sub_usingnamespace_decl = mod.declPtr(sub_usingnamespace_decl_index);
   6248                 const sub_is_pub = entry.value_ptr.*;
   6249                 if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope(mod)) {
   6250                     // Skip usingnamespace decls which are not marked pub, which are in
   6251                     // a different file than the `a.b`/`@hasDecl` syntax.
   6252                     continue;
   6253                 }
   6254                 try sema.ensureDeclAnalyzed(sub_usingnamespace_decl_index);
   6255                 const ns_ty = sub_usingnamespace_decl.val.toType();
   6256                 const sub_ns = ns_ty.getNamespace(mod).?;
   6257                 try checked_namespaces.put(gpa, sub_ns, src_file == sub_usingnamespace_decl.getFileScope(mod));
   6258             }
   6259         }
   6260 
   6261         {
   6262             var i: usize = 0;
   6263             while (i < candidates.items.len) {
   6264                 if (candidates.items[i] == sema.owner_decl_index) {
   6265                     _ = candidates.orderedRemove(i);
   6266                 } else {
   6267                     i += 1;
   6268                 }
   6269             }
   6270         }
   6271 
   6272         switch (candidates.items.len) {
   6273             0 => {},
   6274             1 => {
   6275                 const decl_index = candidates.items[0];
   6276                 try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
   6277                 return decl_index;
   6278             },
   6279             else => {
   6280                 const msg = msg: {
   6281                     const msg = try sema.errMsg(block, src, "ambiguous reference", .{});
   6282                     errdefer msg.destroy(gpa);
   6283                     for (candidates.items) |candidate_index| {
   6284                         const candidate = mod.declPtr(candidate_index);
   6285                         const src_loc = candidate.srcLoc(mod);
   6286                         try mod.errNoteNonLazy(src_loc, msg, "declared here", .{});
   6287                     }
   6288                     break :msg msg;
   6289                 };
   6290                 return sema.failWithOwnedErrorMsg(msg);
   6291             },
   6292         }
   6293     } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| {
   6294         try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
   6295         return decl_index;
   6296     }
   6297 
   6298     // TODO This dependency is too strong. Really, it should only be a dependency
   6299     // on the non-existence of `ident_name` in the namespace. We can lessen the number of
   6300     // outdated declarations by making this dependency more sophisticated.
   6301     try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index);
   6302     return null;
   6303 }
   6304 
   6305 fn funcDeclSrc(sema: *Sema, func_inst: Air.Inst.Ref) !?*Decl {
   6306     const mod = sema.mod;
   6307     const func_val = (try sema.resolveMaybeUndefVal(func_inst)) orelse return null;
   6308     if (func_val.isUndef(mod)) return null;
   6309     const owner_decl_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) {
   6310         .extern_func => |extern_func| extern_func.decl,
   6311         .func => |func| mod.funcPtr(func.index).owner_decl,
   6312         .ptr => |ptr| switch (ptr.addr) {
   6313             .decl => |decl| mod.declPtr(decl).val.getFunction(mod).?.owner_decl,
   6314             else => return null,
   6315         },
   6316         else => return null,
   6317     };
   6318     return mod.declPtr(owner_decl_index);
   6319 }
   6320 
   6321 pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref {
   6322     const mod = sema.mod;
   6323     const gpa = sema.gpa;
   6324     const src = sema.src;
   6325 
   6326     if (!mod.backendSupportsFeature(.error_return_trace)) return .none;
   6327     if (!mod.comp.bin_file.options.error_return_tracing) return .none;
   6328 
   6329     if (block.is_comptime)
   6330         return .none;
   6331 
   6332     const unresolved_stack_trace_ty = sema.getBuiltinType("StackTrace") catch |err| switch (err) {
   6333         error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable,
   6334         else => |e| return e,
   6335     };
   6336     const stack_trace_ty = sema.resolveTypeFields(unresolved_stack_trace_ty) catch |err| switch (err) {
   6337         error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable,
   6338         else => |e| return e,
   6339     };
   6340     const field_name = try mod.intern_pool.getOrPutString(gpa, "index");
   6341     const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, src) catch |err| switch (err) {
   6342         error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable,
   6343         else => |e| return e,
   6344     };
   6345 
   6346     return try block.addInst(.{
   6347         .tag = .save_err_return_trace_index,
   6348         .data = .{ .ty_pl = .{
   6349             .ty = try sema.addType(stack_trace_ty),
   6350             .payload = @as(u32, @intCast(field_index)),
   6351         } },
   6352     });
   6353 }
   6354 
   6355 /// Add instructions to block to "pop" the error return trace.
   6356 /// If `operand` is provided, only pops if operand is non-error.
   6357 fn popErrorReturnTrace(
   6358     sema: *Sema,
   6359     block: *Block,
   6360     src: LazySrcLoc,
   6361     operand: Air.Inst.Ref,
   6362     saved_error_trace_index: Air.Inst.Ref,
   6363 ) CompileError!void {
   6364     const mod = sema.mod;
   6365     const gpa = sema.gpa;
   6366     var is_non_error: ?bool = null;
   6367     var is_non_error_inst: Air.Inst.Ref = undefined;
   6368     if (operand != .none) {
   6369         is_non_error_inst = try sema.analyzeIsNonErr(block, src, operand);
   6370         if (try sema.resolveDefinedValue(block, src, is_non_error_inst)) |cond_val|
   6371             is_non_error = cond_val.toBool();
   6372     } else is_non_error = true; // no operand means pop unconditionally
   6373 
   6374     if (is_non_error == true) {
   6375         // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or
   6376         // the result is comptime-known to be a non-error. Either way, pop unconditionally.
   6377 
   6378         const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
   6379         const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
   6380         const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty);
   6381         const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
   6382         const field_name = try mod.intern_pool.getOrPutString(gpa, "index");
   6383         const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true);
   6384         try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store);
   6385     } else if (is_non_error == null) {
   6386         // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need
   6387         // to pop any error trace that may have been propagated from our arguments.
   6388 
   6389         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len);
   6390         const cond_block_inst = try block.addInstAsIndex(.{
   6391             .tag = .block,
   6392             .data = .{
   6393                 .ty_pl = .{
   6394                     .ty = Air.Inst.Ref.void_type,
   6395                     .payload = undefined, // updated below
   6396                 },
   6397             },
   6398         });
   6399 
   6400         var then_block = block.makeSubBlock();
   6401         defer then_block.instructions.deinit(gpa);
   6402 
   6403         // If non-error, then pop the error return trace by restoring the index.
   6404         const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
   6405         const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
   6406         const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty);
   6407         const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty);
   6408         const field_name = try mod.intern_pool.getOrPutString(gpa, "index");
   6409         const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true);
   6410         try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store);
   6411         _ = try then_block.addBr(cond_block_inst, Air.Inst.Ref.void_value);
   6412 
   6413         // Otherwise, do nothing
   6414         var else_block = block.makeSubBlock();
   6415         defer else_block.instructions.deinit(gpa);
   6416         _ = try else_block.addBr(cond_block_inst, Air.Inst.Ref.void_value);
   6417 
   6418         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
   6419             then_block.instructions.items.len + else_block.instructions.items.len +
   6420             @typeInfo(Air.Block).Struct.fields.len + 1); // +1 for the sole .cond_br instruction in the .block
   6421 
   6422         const cond_br_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
   6423         try sema.air_instructions.append(gpa, .{ .tag = .cond_br, .data = .{ .pl_op = .{
   6424             .operand = is_non_error_inst,
   6425             .payload = sema.addExtraAssumeCapacity(Air.CondBr{
   6426                 .then_body_len = @as(u32, @intCast(then_block.instructions.items.len)),
   6427                 .else_body_len = @as(u32, @intCast(else_block.instructions.items.len)),
   6428             }),
   6429         } } });
   6430         sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items);
   6431         sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items);
   6432 
   6433         sema.air_instructions.items(.data)[cond_block_inst].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 });
   6434         sema.air_extra.appendAssumeCapacity(cond_br_inst);
   6435     }
   6436 }
   6437 
   6438 fn zirCall(
   6439     sema: *Sema,
   6440     block: *Block,
   6441     inst: Zir.Inst.Index,
   6442     comptime kind: enum { direct, field },
   6443 ) CompileError!Air.Inst.Ref {
   6444     const tracy = trace(@src());
   6445     defer tracy.end();
   6446 
   6447     const mod = sema.mod;
   6448     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   6449     const callee_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
   6450     const call_src = inst_data.src();
   6451     const ExtraType = switch (kind) {
   6452         .direct => Zir.Inst.Call,
   6453         .field => Zir.Inst.FieldCall,
   6454     };
   6455     const extra = sema.code.extraData(ExtraType, inst_data.payload_index);
   6456     const args_len = extra.data.flags.args_len;
   6457 
   6458     const modifier = @as(std.builtin.CallModifier, @enumFromInt(extra.data.flags.packed_modifier));
   6459     const ensure_result_used = extra.data.flags.ensure_result_used;
   6460     const pop_error_return_trace = extra.data.flags.pop_error_return_trace;
   6461 
   6462     const callee: ResolvedFieldCallee = switch (kind) {
   6463         .direct => .{ .direct = try sema.resolveInst(extra.data.callee) },
   6464         .field => blk: {
   6465             const object_ptr = try sema.resolveInst(extra.data.obj_ptr);
   6466             const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.data.field_name_start));
   6467             const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
   6468             break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src);
   6469         },
   6470     };
   6471     var resolved_args: []Air.Inst.Ref = undefined;
   6472     var bound_arg_src: ?LazySrcLoc = null;
   6473     var func: Air.Inst.Ref = undefined;
   6474     var arg_index: u32 = 0;
   6475     switch (callee) {
   6476         .direct => |func_inst| {
   6477             resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len);
   6478             func = func_inst;
   6479         },
   6480         .method => |method| {
   6481             resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len + 1);
   6482             func = method.func_inst;
   6483             resolved_args[0] = method.arg0_inst;
   6484             arg_index += 1;
   6485             bound_arg_src = callee_src;
   6486         },
   6487     }
   6488 
   6489     const callee_ty = sema.typeOf(func);
   6490     const total_args = args_len + @intFromBool(bound_arg_src != null);
   6491     const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, bound_arg_src != null);
   6492 
   6493     const args_body = sema.code.extra[extra.end..];
   6494 
   6495     var input_is_error = false;
   6496     const block_index = @as(Air.Inst.Index, @intCast(block.instructions.items.len));
   6497 
   6498     const fn_params_len = mod.typeToFunc(func_ty).?.param_types.len;
   6499     const parent_comptime = block.is_comptime;
   6500     // `extra_index` and `arg_index` are separate since the bound function is passed as the first argument.
   6501     var extra_index: usize = 0;
   6502     var arg_start: u32 = args_len;
   6503     while (extra_index < args_len) : ({
   6504         extra_index += 1;
   6505         arg_index += 1;
   6506     }) {
   6507         const func_ty_info = mod.typeToFunc(func_ty).?;
   6508         const arg_end = sema.code.extra[extra.end + extra_index];
   6509         defer arg_start = arg_end;
   6510 
   6511         // Generate args to comptime params in comptime block.
   6512         defer block.is_comptime = parent_comptime;
   6513         if (arg_index < @min(fn_params_len, 32) and func_ty_info.paramIsComptime(@as(u5, @intCast(arg_index)))) {
   6514             block.is_comptime = true;
   6515             // TODO set comptime_reason
   6516         }
   6517 
   6518         sema.inst_map.putAssumeCapacity(inst, inst: {
   6519             if (arg_index >= fn_params_len)
   6520                 break :inst Air.Inst.Ref.var_args_param_type;
   6521 
   6522             if (func_ty_info.param_types[arg_index] == .generic_poison_type)
   6523                 break :inst Air.Inst.Ref.generic_poison_type;
   6524 
   6525             break :inst try sema.addType(func_ty_info.param_types[arg_index].toType());
   6526         });
   6527 
   6528         const resolved = try sema.resolveBody(block, args_body[arg_start..arg_end], inst);
   6529         const resolved_ty = sema.typeOf(resolved);
   6530         if (resolved_ty.zigTypeTag(mod) == .NoReturn) {
   6531             return resolved;
   6532         }
   6533         if (resolved_ty.isError(mod)) {
   6534             input_is_error = true;
   6535         }
   6536         resolved_args[arg_index] = resolved;
   6537     }
   6538     if (sema.owner_func == null or !sema.owner_func.?.calls_or_awaits_errorable_fn) {
   6539         input_is_error = false; // input was an error type, but no errorable fn's were actually called
   6540     }
   6541 
   6542     // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction.
   6543     const call_dbg_node = inst - 1;
   6544 
   6545     if (mod.backendSupportsFeature(.error_return_trace) and mod.comp.bin_file.options.error_return_tracing and
   6546         !block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace))
   6547     {
   6548         const call_inst: Air.Inst.Ref = if (modifier == .always_tail) undefined else b: {
   6549             break :b try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node);
   6550         };
   6551 
   6552         const return_ty = sema.typeOf(call_inst);
   6553         if (modifier != .always_tail and return_ty.isNoReturn(mod))
   6554             return call_inst; // call to "fn(...) noreturn", don't pop
   6555 
   6556         // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only
   6557         // need to clean-up our own trace if we were passed to a non-error-handling expression.
   6558         if (input_is_error or (pop_error_return_trace and modifier != .always_tail and return_ty.isError(mod))) {
   6559             const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
   6560             const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
   6561             const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "index");
   6562             const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src);
   6563 
   6564             // Insert a save instruction before the arg resolution + call instructions we just generated
   6565             const save_inst = try block.insertInst(block_index, .{
   6566                 .tag = .save_err_return_trace_index,
   6567                 .data = .{ .ty_pl = .{
   6568                     .ty = try sema.addType(stack_trace_ty),
   6569                     .payload = @as(u32, @intCast(field_index)),
   6570                 } },
   6571             });
   6572 
   6573             // Pop the error return trace, testing the result for non-error if necessary
   6574             const operand = if (pop_error_return_trace or modifier == .always_tail) .none else call_inst;
   6575             try sema.popErrorReturnTrace(block, call_src, operand, save_inst);
   6576         }
   6577 
   6578         if (modifier == .always_tail) // Perform the call *after* the restore, so that a tail call is possible.
   6579             return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node);
   6580 
   6581         return call_inst;
   6582     } else {
   6583         return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node);
   6584     }
   6585 }
   6586 
   6587 fn checkCallArgumentCount(
   6588     sema: *Sema,
   6589     block: *Block,
   6590     func: Air.Inst.Ref,
   6591     func_src: LazySrcLoc,
   6592     callee_ty: Type,
   6593     total_args: usize,
   6594     member_fn: bool,
   6595 ) !Type {
   6596     const mod = sema.mod;
   6597     const func_ty = func_ty: {
   6598         switch (callee_ty.zigTypeTag(mod)) {
   6599             .Fn => break :func_ty callee_ty,
   6600             .Pointer => {
   6601                 const ptr_info = callee_ty.ptrInfo(mod);
   6602                 if (ptr_info.flags.size == .One and ptr_info.child.toType().zigTypeTag(mod) == .Fn) {
   6603                     break :func_ty ptr_info.child.toType();
   6604                 }
   6605             },
   6606             .Optional => {
   6607                 const opt_child = callee_ty.optionalChild(mod);
   6608                 if (opt_child.zigTypeTag(mod) == .Fn or (opt_child.isSinglePointer(mod) and
   6609                     opt_child.childType(mod).zigTypeTag(mod) == .Fn))
   6610                 {
   6611                     const msg = msg: {
   6612                         const msg = try sema.errMsg(block, func_src, "cannot call optional type '{}'", .{
   6613                             callee_ty.fmt(mod),
   6614                         });
   6615                         errdefer msg.destroy(sema.gpa);
   6616                         try sema.errNote(block, func_src, msg, "consider using '.?', 'orelse' or 'if'", .{});
   6617                         break :msg msg;
   6618                     };
   6619                     return sema.failWithOwnedErrorMsg(msg);
   6620                 }
   6621             },
   6622             else => {},
   6623         }
   6624         return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(mod)});
   6625     };
   6626 
   6627     const func_ty_info = mod.typeToFunc(func_ty).?;
   6628     const fn_params_len = func_ty_info.param_types.len;
   6629     const args_len = total_args - @intFromBool(member_fn);
   6630     if (func_ty_info.is_var_args) {
   6631         assert(func_ty_info.cc == .C);
   6632         if (total_args >= fn_params_len) return func_ty;
   6633     } else if (fn_params_len == total_args) {
   6634         return func_ty;
   6635     }
   6636 
   6637     const maybe_decl = try sema.funcDeclSrc(func);
   6638     const member_str = if (member_fn) "member function " else "";
   6639     const variadic_str = if (func_ty_info.is_var_args) "at least " else "";
   6640     const msg = msg: {
   6641         const msg = try sema.errMsg(
   6642             block,
   6643             func_src,
   6644             "{s}expected {s}{d} argument(s), found {d}",
   6645             .{
   6646                 member_str,
   6647                 variadic_str,
   6648                 fn_params_len - @intFromBool(member_fn),
   6649                 args_len,
   6650             },
   6651         );
   6652         errdefer msg.destroy(sema.gpa);
   6653 
   6654         if (maybe_decl) |fn_decl| try mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{});
   6655         break :msg msg;
   6656     };
   6657     return sema.failWithOwnedErrorMsg(msg);
   6658 }
   6659 
   6660 fn callBuiltin(
   6661     sema: *Sema,
   6662     block: *Block,
   6663     builtin_fn: Air.Inst.Ref,
   6664     modifier: std.builtin.CallModifier,
   6665     args: []const Air.Inst.Ref,
   6666 ) !void {
   6667     const mod = sema.mod;
   6668     const callee_ty = sema.typeOf(builtin_fn);
   6669     const func_ty = func_ty: {
   6670         switch (callee_ty.zigTypeTag(mod)) {
   6671             .Fn => break :func_ty callee_ty,
   6672             .Pointer => {
   6673                 const ptr_info = callee_ty.ptrInfo(mod);
   6674                 if (ptr_info.flags.size == .One and ptr_info.child.toType().zigTypeTag(mod) == .Fn) {
   6675                     break :func_ty ptr_info.child.toType();
   6676                 }
   6677             },
   6678             else => {},
   6679         }
   6680         std.debug.panic("type '{}' is not a function calling builtin fn", .{callee_ty.fmt(mod)});
   6681     };
   6682 
   6683     const func_ty_info = mod.typeToFunc(func_ty).?;
   6684     const fn_params_len = func_ty_info.param_types.len;
   6685     if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) {
   6686         std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len });
   6687     }
   6688     _ = try sema.analyzeCall(block, builtin_fn, func_ty, sema.src, sema.src, modifier, false, args, null, null);
   6689 }
   6690 
   6691 fn analyzeCall(
   6692     sema: *Sema,
   6693     block: *Block,
   6694     func: Air.Inst.Ref,
   6695     func_ty: Type,
   6696     func_src: LazySrcLoc,
   6697     call_src: LazySrcLoc,
   6698     modifier: std.builtin.CallModifier,
   6699     ensure_result_used: bool,
   6700     uncasted_args: []const Air.Inst.Ref,
   6701     bound_arg_src: ?LazySrcLoc,
   6702     call_dbg_node: ?Zir.Inst.Index,
   6703 ) CompileError!Air.Inst.Ref {
   6704     const mod = sema.mod;
   6705 
   6706     const callee_ty = sema.typeOf(func);
   6707     const func_ty_info = mod.typeToFunc(func_ty).?;
   6708     const fn_params_len = func_ty_info.param_types.len;
   6709     const cc = func_ty_info.cc;
   6710     if (cc == .Naked) {
   6711         const maybe_decl = try sema.funcDeclSrc(func);
   6712         const msg = msg: {
   6713             const msg = try sema.errMsg(
   6714                 block,
   6715                 func_src,
   6716                 "unable to call function with naked calling convention",
   6717                 .{},
   6718             );
   6719             errdefer msg.destroy(sema.gpa);
   6720 
   6721             if (maybe_decl) |fn_decl| try mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{});
   6722             break :msg msg;
   6723         };
   6724         return sema.failWithOwnedErrorMsg(msg);
   6725     }
   6726 
   6727     const call_tag: Air.Inst.Tag = switch (modifier) {
   6728         .auto,
   6729         .always_inline,
   6730         .compile_time,
   6731         .no_async,
   6732         => Air.Inst.Tag.call,
   6733 
   6734         .never_tail => Air.Inst.Tag.call_never_tail,
   6735         .never_inline => Air.Inst.Tag.call_never_inline,
   6736         .always_tail => Air.Inst.Tag.call_always_tail,
   6737 
   6738         .async_kw => return sema.failWithUseOfAsync(block, call_src),
   6739     };
   6740 
   6741     if (modifier == .never_inline and func_ty_info.cc == .Inline) {
   6742         return sema.fail(block, call_src, "'never_inline' call of inline function", .{});
   6743     }
   6744     if (modifier == .always_inline and func_ty_info.is_noinline) {
   6745         return sema.fail(block, call_src, "'always_inline' call of noinline function", .{});
   6746     }
   6747 
   6748     const gpa = sema.gpa;
   6749 
   6750     var is_generic_call = func_ty_info.is_generic;
   6751     var is_comptime_call = block.is_comptime or modifier == .compile_time;
   6752     var comptime_reason_buf: Block.ComptimeReason = undefined;
   6753     var comptime_reason: ?*const Block.ComptimeReason = null;
   6754     if (!is_comptime_call) {
   6755         if (sema.typeRequiresComptime(func_ty_info.return_type.toType())) |ct| {
   6756             is_comptime_call = ct;
   6757             if (ct) {
   6758                 // stage1 can't handle doing this directly
   6759                 comptime_reason_buf = .{ .comptime_ret_ty = .{
   6760                     .block = block,
   6761                     .func = func,
   6762                     .func_src = func_src,
   6763                     .return_ty = func_ty_info.return_type.toType(),
   6764                 } };
   6765                 comptime_reason = &comptime_reason_buf;
   6766             }
   6767         } else |err| switch (err) {
   6768             error.GenericPoison => is_generic_call = true,
   6769             else => |e| return e,
   6770         }
   6771     }
   6772     var is_inline_call = is_comptime_call or modifier == .always_inline or
   6773         func_ty_info.cc == .Inline;
   6774 
   6775     if (!is_inline_call and is_generic_call) {
   6776         if (sema.instantiateGenericCall(
   6777             block,
   6778             func,
   6779             func_src,
   6780             call_src,
   6781             func_ty,
   6782             ensure_result_used,
   6783             uncasted_args,
   6784             call_tag,
   6785             bound_arg_src,
   6786             call_dbg_node,
   6787         )) |some| {
   6788             return some;
   6789         } else |err| switch (err) {
   6790             error.GenericPoison => {
   6791                 is_inline_call = true;
   6792             },
   6793             error.ComptimeReturn => {
   6794                 is_inline_call = true;
   6795                 is_comptime_call = true;
   6796                 // stage1 can't handle doing this directly
   6797                 comptime_reason_buf = .{ .comptime_ret_ty = .{
   6798                     .block = block,
   6799                     .func = func,
   6800                     .func_src = func_src,
   6801                     .return_ty = func_ty_info.return_type.toType(),
   6802                 } };
   6803                 comptime_reason = &comptime_reason_buf;
   6804             },
   6805             else => |e| return e,
   6806         }
   6807     }
   6808 
   6809     if (is_comptime_call and modifier == .never_inline) {
   6810         return sema.fail(block, call_src, "unable to perform 'never_inline' call at compile-time", .{});
   6811     }
   6812 
   6813     const result: Air.Inst.Ref = if (is_inline_call) res: {
   6814         const func_val = sema.resolveConstValue(block, func_src, func, "function being called at comptime must be comptime-known") catch |err| {
   6815             if (err == error.AnalysisFail and comptime_reason != null) try comptime_reason.?.explain(sema, sema.err);
   6816             return err;
   6817         };
   6818         const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) {
   6819             .extern_func => return sema.fail(block, call_src, "{s} call of extern function", .{
   6820                 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
   6821             }),
   6822             .func => |function| function.index,
   6823             .ptr => |ptr| switch (ptr.addr) {
   6824                 .decl => |decl| mod.declPtr(decl).val.getFunctionIndex(mod).unwrap().?,
   6825                 else => {
   6826                     assert(callee_ty.isPtrAtRuntime(mod));
   6827                     return sema.fail(block, call_src, "{s} call of function pointer", .{
   6828                         @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
   6829                     });
   6830                 },
   6831             },
   6832             else => unreachable,
   6833         };
   6834         if (func_ty_info.is_var_args) {
   6835             return sema.fail(block, call_src, "{s} call of variadic function", .{
   6836                 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
   6837             });
   6838         }
   6839 
   6840         // Analyze the ZIR. The same ZIR gets analyzed into a runtime function
   6841         // or an inlined call depending on what union tag the `label` field is
   6842         // set to in the `Block`.
   6843         // This block instruction will be used to capture the return value from the
   6844         // inlined function.
   6845         const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
   6846         try sema.air_instructions.append(gpa, .{
   6847             .tag = .block,
   6848             .data = undefined,
   6849         });
   6850         // This one is shared among sub-blocks within the same callee, but not
   6851         // shared among the entire inline/comptime call stack.
   6852         var inlining: Block.Inlining = .{
   6853             .func = null,
   6854             .comptime_result = undefined,
   6855             .merges = .{
   6856                 .src_locs = .{},
   6857                 .results = .{},
   6858                 .br_list = .{},
   6859                 .block_inst = block_inst,
   6860             },
   6861         };
   6862         // In order to save a bit of stack space, directly modify Sema rather
   6863         // than create a child one.
   6864         const parent_zir = sema.code;
   6865         const module_fn = mod.funcPtr(module_fn_index);
   6866         const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
   6867         sema.code = fn_owner_decl.getFileScope(mod).zir;
   6868         defer sema.code = parent_zir;
   6869 
   6870         try mod.declareDeclDependencyType(sema.owner_decl_index, module_fn.owner_decl, .function_body);
   6871 
   6872         const parent_inst_map = sema.inst_map;
   6873         sema.inst_map = .{};
   6874         defer {
   6875             sema.src = call_src;
   6876             sema.inst_map.deinit(gpa);
   6877             sema.inst_map = parent_inst_map;
   6878         }
   6879 
   6880         const parent_func = sema.func;
   6881         const parent_func_index = sema.func_index;
   6882         sema.func = module_fn;
   6883         sema.func_index = module_fn_index.toOptional();
   6884         defer sema.func = parent_func;
   6885         defer sema.func_index = parent_func_index;
   6886 
   6887         const parent_err_ret_index = sema.error_return_trace_index_on_fn_entry;
   6888         sema.error_return_trace_index_on_fn_entry = block.error_return_trace_index;
   6889         defer sema.error_return_trace_index_on_fn_entry = parent_err_ret_index;
   6890 
   6891         var wip_captures = try WipCaptureScope.init(gpa, fn_owner_decl.src_scope);
   6892         defer wip_captures.deinit();
   6893 
   6894         var child_block: Block = .{
   6895             .parent = null,
   6896             .sema = sema,
   6897             .src_decl = module_fn.owner_decl,
   6898             .namespace = fn_owner_decl.src_namespace,
   6899             .wip_capture_scope = wip_captures.scope,
   6900             .instructions = .{},
   6901             .label = null,
   6902             .inlining = &inlining,
   6903             .is_typeof = block.is_typeof,
   6904             .is_comptime = is_comptime_call,
   6905             .comptime_reason = comptime_reason,
   6906             .error_return_trace_index = block.error_return_trace_index,
   6907         };
   6908 
   6909         const merges = &child_block.inlining.?.merges;
   6910 
   6911         defer child_block.instructions.deinit(gpa);
   6912         defer merges.deinit(gpa);
   6913 
   6914         try sema.emitBackwardBranch(block, call_src);
   6915 
   6916         // Whether this call should be memoized, set to false if the call can mutate comptime state.
   6917         var should_memoize = true;
   6918 
   6919         // If it's a comptime function call, we need to memoize it as long as no external
   6920         // comptime memory is mutated.
   6921         const memoized_arg_values = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len);
   6922 
   6923         var new_fn_info = mod.typeToFunc(fn_owner_decl.ty).?;
   6924         new_fn_info.param_types = try sema.arena.alloc(InternPool.Index, new_fn_info.param_types.len);
   6925         new_fn_info.comptime_bits = 0;
   6926 
   6927         // This will have return instructions analyzed as break instructions to
   6928         // the block_inst above. Here we are performing "comptime/inline semantic analysis"
   6929         // for a function body, which means we must map the parameter ZIR instructions to
   6930         // the AIR instructions of the callsite. The callee could be a generic function
   6931         // which means its parameter type expressions must be resolved in order and used
   6932         // to successively coerce the arguments.
   6933         const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst);
   6934         try sema.inst_map.ensureSpaceForInstructions(sema.gpa, fn_info.param_body);
   6935 
   6936         var has_comptime_args = false;
   6937         var arg_i: usize = 0;
   6938         for (fn_info.param_body) |inst| {
   6939             sema.analyzeInlineCallArg(
   6940                 block,
   6941                 &child_block,
   6942                 .unneeded,
   6943                 inst,
   6944                 &new_fn_info,
   6945                 &arg_i,
   6946                 uncasted_args,
   6947                 is_comptime_call,
   6948                 &should_memoize,
   6949                 memoized_arg_values,
   6950                 mod.typeToFunc(func_ty).?.param_types,
   6951                 func,
   6952                 &has_comptime_args,
   6953             ) catch |err| switch (err) {
   6954                 error.NeededSourceLocation => {
   6955                     _ = sema.inst_map.remove(inst);
   6956                     const decl = mod.declPtr(block.src_decl);
   6957                     try sema.analyzeInlineCallArg(
   6958                         block,
   6959                         &child_block,
   6960                         mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src),
   6961                         inst,
   6962                         &new_fn_info,
   6963                         &arg_i,
   6964                         uncasted_args,
   6965                         is_comptime_call,
   6966                         &should_memoize,
   6967                         memoized_arg_values,
   6968                         mod.typeToFunc(func_ty).?.param_types,
   6969                         func,
   6970                         &has_comptime_args,
   6971                     );
   6972                     unreachable;
   6973                 },
   6974                 else => |e| return e,
   6975             };
   6976         }
   6977 
   6978         if (!has_comptime_args and module_fn.state == .sema_failure) return error.AnalysisFail;
   6979 
   6980         const recursive_msg = "inline call is recursive";
   6981         var head = if (!has_comptime_args) block else null;
   6982         while (head) |some| {
   6983             const parent_inlining = some.inlining orelse break;
   6984             if (parent_inlining.func == module_fn) {
   6985                 return sema.fail(block, call_src, recursive_msg, .{});
   6986             }
   6987             head = some.parent;
   6988         }
   6989         if (!has_comptime_args) inlining.func = module_fn;
   6990 
   6991         // In case it is a generic function with an expression for the return type that depends
   6992         // on parameters, we must now do the same for the return type as we just did with
   6993         // each of the parameters, resolving the return type and providing it to the child
   6994         // `Sema` so that it can be used for the `ret_ptr` instruction.
   6995         const ret_ty_inst = if (fn_info.ret_ty_body.len != 0)
   6996             try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst)
   6997         else
   6998             try sema.resolveInst(fn_info.ret_ty_ref);
   6999         const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
   7000         const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
   7001         // Create a fresh inferred error set type for inline/comptime calls.
   7002         const fn_ret_ty = blk: {
   7003             if (module_fn.hasInferredErrorSet(mod)) {
   7004                 const ies_index = try mod.intern_pool.createInferredErrorSet(gpa, .{
   7005                     .func = module_fn_index,
   7006                 });
   7007                 const error_set_ty = try mod.intern(.{ .inferred_error_set_type = ies_index });
   7008                 break :blk try mod.errorUnionType(error_set_ty.toType(), bare_return_type);
   7009             }
   7010             break :blk bare_return_type;
   7011         };
   7012         new_fn_info.return_type = fn_ret_ty.toIntern();
   7013         const parent_fn_ret_ty = sema.fn_ret_ty;
   7014         sema.fn_ret_ty = fn_ret_ty;
   7015         defer sema.fn_ret_ty = parent_fn_ret_ty;
   7016 
   7017         // This `res2` is here instead of directly breaking from `res` due to a stage1
   7018         // bug generating invalid LLVM IR.
   7019         const res2: Air.Inst.Ref = res2: {
   7020             if (should_memoize and is_comptime_call) {
   7021                 if (mod.intern_pool.getIfExists(.{ .memoized_call = .{
   7022                     .func = module_fn_index,
   7023                     .arg_values = memoized_arg_values,
   7024                     .result = .none,
   7025                 } })) |memoized_call_index| {
   7026                     const memoized_call = mod.intern_pool.indexToKey(memoized_call_index).memoized_call;
   7027                     break :res2 try sema.addConstant(
   7028                         memoized_call.result.toValue(),
   7029                     );
   7030                 }
   7031             }
   7032 
   7033             const new_func_resolved_ty = try mod.funcType(new_fn_info);
   7034             if (!is_comptime_call and !block.is_typeof) {
   7035                 try sema.emitDbgInline(block, parent_func_index.unwrap().?, module_fn_index, new_func_resolved_ty, .dbg_inline_begin);
   7036 
   7037                 const zir_tags = sema.code.instructions.items(.tag);
   7038                 for (fn_info.param_body) |param| switch (zir_tags[param]) {
   7039                     .param, .param_comptime => {
   7040                         const inst_data = sema.code.instructions.items(.data)[param].pl_tok;
   7041                         const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
   7042                         const param_name = sema.code.nullTerminatedString(extra.data.name);
   7043                         const inst = sema.inst_map.get(param).?;
   7044 
   7045                         try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name);
   7046                     },
   7047                     .param_anytype, .param_anytype_comptime => {
   7048                         const inst_data = sema.code.instructions.items(.data)[param].str_tok;
   7049                         const param_name = inst_data.get(sema.code);
   7050                         const inst = sema.inst_map.get(param).?;
   7051 
   7052                         try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name);
   7053                     },
   7054                     else => continue,
   7055                 };
   7056             }
   7057 
   7058             if (is_comptime_call and ensure_result_used) {
   7059                 try sema.ensureResultUsed(block, fn_ret_ty, call_src);
   7060             }
   7061 
   7062             const result = result: {
   7063                 sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
   7064                     error.ComptimeReturn => break :result inlining.comptime_result,
   7065                     error.AnalysisFail => {
   7066                         const err_msg = sema.err orelse return err;
   7067                         if (mem.eql(u8, err_msg.msg, recursive_msg)) return err;
   7068                         try sema.errNote(block, call_src, err_msg, "called from here", .{});
   7069                         err_msg.clearTrace(sema.gpa);
   7070                         return err;
   7071                     },
   7072                     else => |e| return e,
   7073                 };
   7074                 break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges);
   7075             };
   7076 
   7077             if (!is_comptime_call and !block.is_typeof and sema.typeOf(result).zigTypeTag(mod) != .NoReturn) {
   7078                 try sema.emitDbgInline(
   7079                     block,
   7080                     module_fn_index,
   7081                     parent_func_index.unwrap().?,
   7082                     mod.declPtr(parent_func.?.owner_decl).ty,
   7083                     .dbg_inline_end,
   7084                 );
   7085             }
   7086 
   7087             if (should_memoize and is_comptime_call) {
   7088                 const result_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, result, "");
   7089 
   7090                 // TODO: check whether any external comptime memory was mutated by the
   7091                 // comptime function call. If so, then do not memoize the call here.
   7092                 _ = try mod.intern(.{ .memoized_call = .{
   7093                     .func = module_fn_index,
   7094                     .arg_values = memoized_arg_values,
   7095                     .result = try result_val.intern(fn_ret_ty, mod),
   7096                 } });
   7097             }
   7098 
   7099             break :res2 result;
   7100         };
   7101 
   7102         try wip_captures.finalize();
   7103 
   7104         break :res res2;
   7105     } else res: {
   7106         assert(!func_ty_info.is_generic);
   7107 
   7108         const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len);
   7109         for (uncasted_args, 0..) |uncasted_arg, i| {
   7110             if (i < fn_params_len) {
   7111                 const opts: CoerceOpts = .{ .param_src = .{
   7112                     .func_inst = func,
   7113                     .param_i = @as(u32, @intCast(i)),
   7114                 } };
   7115                 const param_ty = mod.typeToFunc(func_ty).?.param_types[i].toType();
   7116                 args[i] = sema.analyzeCallArg(
   7117                     block,
   7118                     .unneeded,
   7119                     param_ty,
   7120                     uncasted_arg,
   7121                     opts,
   7122                 ) catch |err| switch (err) {
   7123                     error.NeededSourceLocation => {
   7124                         const decl = mod.declPtr(block.src_decl);
   7125                         _ = try sema.analyzeCallArg(
   7126                             block,
   7127                             mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src),
   7128                             param_ty,
   7129                             uncasted_arg,
   7130                             opts,
   7131                         );
   7132                         unreachable;
   7133                     },
   7134                     else => |e| return e,
   7135                 };
   7136             } else {
   7137                 args[i] = sema.coerceVarArgParam(block, uncasted_arg, .unneeded) catch |err| switch (err) {
   7138                     error.NeededSourceLocation => {
   7139                         const decl = mod.declPtr(block.src_decl);
   7140                         _ = try sema.coerceVarArgParam(
   7141                             block,
   7142                             uncasted_arg,
   7143                             mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src),
   7144                         );
   7145                         unreachable;
   7146                     },
   7147                     else => |e| return e,
   7148                 };
   7149             }
   7150         }
   7151 
   7152         if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
   7153 
   7154         try sema.queueFullTypeResolution(func_ty_info.return_type.toType());
   7155         if (sema.owner_func != null and func_ty_info.return_type.toType().isError(mod)) {
   7156             sema.owner_func.?.calls_or_awaits_errorable_fn = true;
   7157         }
   7158 
   7159         if (try sema.resolveMaybeUndefVal(func)) |func_val| {
   7160             if (mod.intern_pool.indexToFunc(func_val.toIntern()).unwrap()) |func_index| {
   7161                 try mod.ensureFuncBodyAnalysisQueued(func_index);
   7162             }
   7163         }
   7164 
   7165         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
   7166             args.len);
   7167         const func_inst = try block.addInst(.{
   7168             .tag = call_tag,
   7169             .data = .{ .pl_op = .{
   7170                 .operand = func,
   7171                 .payload = sema.addExtraAssumeCapacity(Air.Call{
   7172                     .args_len = @as(u32, @intCast(args.len)),
   7173                 }),
   7174             } },
   7175         });
   7176         sema.appendRefsAssumeCapacity(args);
   7177 
   7178         if (call_tag == .call_always_tail) {
   7179             if (ensure_result_used) {
   7180                 try sema.ensureResultUsed(block, sema.typeOf(func_inst), call_src);
   7181             }
   7182             return sema.handleTailCall(block, call_src, func_ty, func_inst);
   7183         }
   7184         if (block.wantSafety() and func_ty_info.return_type == .noreturn_type) skip_safety: {
   7185             // Function pointers and extern functions aren't guaranteed to
   7186             // actually be noreturn so we add a safety check for them.
   7187             if (try sema.resolveMaybeUndefVal(func)) |func_val| {
   7188                 switch (mod.intern_pool.indexToKey(func_val.toIntern())) {
   7189                     .func => break :skip_safety,
   7190                     .ptr => |ptr| switch (ptr.addr) {
   7191                         .decl => |decl| if (!mod.declPtr(decl).isExtern(mod)) break :skip_safety,
   7192                         else => {},
   7193                     },
   7194                     else => {},
   7195                 }
   7196             }
   7197             try sema.safetyPanic(block, .noreturn_returned);
   7198             return Air.Inst.Ref.unreachable_value;
   7199         }
   7200         if (func_ty_info.return_type == .noreturn_type) {
   7201             _ = try block.addNoOp(.unreach);
   7202             return Air.Inst.Ref.unreachable_value;
   7203         }
   7204         break :res func_inst;
   7205     };
   7206 
   7207     if (ensure_result_used) {
   7208         try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
   7209     }
   7210     return result;
   7211 }
   7212 
   7213 fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {
   7214     const mod = sema.mod;
   7215     const target = mod.getTarget();
   7216     const backend = mod.comp.getZigBackend();
   7217     if (!target_util.supportsTailCall(target, backend)) {
   7218         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", .{
   7219             @tagName(backend), @tagName(target.cpu.arch),
   7220         });
   7221     }
   7222     const func_decl = mod.declPtr(sema.owner_func.?.owner_decl);
   7223     if (!func_ty.eql(func_decl.ty, mod)) {
   7224         return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{}' does not match type of calling function '{}'", .{
   7225             func_ty.fmt(mod), func_decl.ty.fmt(mod),
   7226         });
   7227     }
   7228     _ = try block.addUnOp(.ret, result);
   7229     return Air.Inst.Ref.unreachable_value;
   7230 }
   7231 
   7232 fn analyzeInlineCallArg(
   7233     sema: *Sema,
   7234     arg_block: *Block,
   7235     param_block: *Block,
   7236     arg_src: LazySrcLoc,
   7237     inst: Zir.Inst.Index,
   7238     new_fn_info: *InternPool.Key.FuncType,
   7239     arg_i: *usize,
   7240     uncasted_args: []const Air.Inst.Ref,
   7241     is_comptime_call: bool,
   7242     should_memoize: *bool,
   7243     memoized_arg_values: []InternPool.Index,
   7244     raw_param_types: []const InternPool.Index,
   7245     func_inst: Air.Inst.Ref,
   7246     has_comptime_args: *bool,
   7247 ) !void {
   7248     const mod = sema.mod;
   7249     const zir_tags = sema.code.instructions.items(.tag);
   7250     switch (zir_tags[inst]) {
   7251         .param_comptime, .param_anytype_comptime => has_comptime_args.* = true,
   7252         else => {},
   7253     }
   7254     switch (zir_tags[inst]) {
   7255         .param, .param_comptime => {
   7256             // Evaluate the parameter type expression now that previous ones have
   7257             // been mapped, and coerce the corresponding argument to it.
   7258             const pl_tok = sema.code.instructions.items(.data)[inst].pl_tok;
   7259             const param_src = pl_tok.src();
   7260             const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index);
   7261             const param_body = sema.code.extra[extra.end..][0..extra.data.body_len];
   7262             const param_ty = param_ty: {
   7263                 const raw_param_ty = raw_param_types[arg_i.*];
   7264                 if (raw_param_ty != .generic_poison_type) break :param_ty raw_param_ty;
   7265                 const param_ty_inst = try sema.resolveBody(param_block, param_body, inst);
   7266                 const param_ty = try sema.analyzeAsType(param_block, param_src, param_ty_inst);
   7267                 break :param_ty param_ty.toIntern();
   7268             };
   7269             new_fn_info.param_types[arg_i.*] = param_ty;
   7270             const uncasted_arg = uncasted_args[arg_i.*];
   7271             if (try sema.typeRequiresComptime(param_ty.toType())) {
   7272                 _ = sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to parameter with comptime-only type must be comptime-known") catch |err| {
   7273                     if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err);
   7274                     return err;
   7275                 };
   7276             } else if (!is_comptime_call and zir_tags[inst] == .param_comptime) {
   7277                 _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime");
   7278             }
   7279             const casted_arg = sema.coerceExtra(arg_block, param_ty.toType(), uncasted_arg, arg_src, .{ .param_src = .{
   7280                 .func_inst = func_inst,
   7281                 .param_i = @as(u32, @intCast(arg_i.*)),
   7282             } }) catch |err| switch (err) {
   7283                 error.NotCoercible => unreachable,
   7284                 else => |e| return e,
   7285             };
   7286 
   7287             if (is_comptime_call) {
   7288                 sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg);
   7289                 const arg_val = sema.resolveConstMaybeUndefVal(arg_block, arg_src, casted_arg, "argument to function being called at comptime must be comptime-known") catch |err| {
   7290                     if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err);
   7291                     return err;
   7292                 };
   7293                 switch (arg_val.toIntern()) {
   7294                     .generic_poison, .generic_poison_type => {
   7295                         // This function is currently evaluated as part of an as-of-yet unresolvable
   7296                         // parameter or return type.
   7297                         return error.GenericPoison;
   7298                     },
   7299                     else => {},
   7300                 }
   7301                 // Needed so that lazy values do not trigger
   7302                 // assertion due to type not being resolved
   7303                 // when the hash function is called.
   7304                 const resolved_arg_val = try sema.resolveLazyValue(arg_val);
   7305                 should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod);
   7306                 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(param_ty.toType(), mod);
   7307             } else {
   7308                 sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg);
   7309             }
   7310 
   7311             if (try sema.resolveMaybeUndefVal(casted_arg)) |_| {
   7312                 has_comptime_args.* = true;
   7313             }
   7314 
   7315             arg_i.* += 1;
   7316         },
   7317         .param_anytype, .param_anytype_comptime => {
   7318             // No coercion needed.
   7319             const uncasted_arg = uncasted_args[arg_i.*];
   7320             new_fn_info.param_types[arg_i.*] = sema.typeOf(uncasted_arg).toIntern();
   7321 
   7322             if (is_comptime_call) {
   7323                 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
   7324                 const arg_val = sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to function being called at comptime must be comptime-known") catch |err| {
   7325                     if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err);
   7326                     return err;
   7327                 };
   7328                 switch (arg_val.toIntern()) {
   7329                     .generic_poison, .generic_poison_type => {
   7330                         // This function is currently evaluated as part of an as-of-yet unresolvable
   7331                         // parameter or return type.
   7332                         return error.GenericPoison;
   7333                     },
   7334                     else => {},
   7335                 }
   7336                 // Needed so that lazy values do not trigger
   7337                 // assertion due to type not being resolved
   7338                 // when the hash function is called.
   7339                 const resolved_arg_val = try sema.resolveLazyValue(arg_val);
   7340                 should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod);
   7341                 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(sema.typeOf(uncasted_arg), mod);
   7342             } else {
   7343                 if (zir_tags[inst] == .param_anytype_comptime) {
   7344                     _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime");
   7345                 }
   7346                 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
   7347             }
   7348 
   7349             if (try sema.resolveMaybeUndefVal(uncasted_arg)) |_| {
   7350                 has_comptime_args.* = true;
   7351             }
   7352 
   7353             arg_i.* += 1;
   7354         },
   7355         else => {},
   7356     }
   7357 }
   7358 
   7359 fn analyzeCallArg(
   7360     sema: *Sema,
   7361     block: *Block,
   7362     arg_src: LazySrcLoc,
   7363     param_ty: Type,
   7364     uncasted_arg: Air.Inst.Ref,
   7365     opts: CoerceOpts,
   7366 ) !Air.Inst.Ref {
   7367     try sema.resolveTypeFully(param_ty);
   7368     return sema.coerceExtra(block, param_ty, uncasted_arg, arg_src, opts) catch |err| switch (err) {
   7369         error.NotCoercible => unreachable,
   7370         else => |e| return e,
   7371     };
   7372 }
   7373 
   7374 fn analyzeGenericCallArg(
   7375     sema: *Sema,
   7376     block: *Block,
   7377     arg_src: LazySrcLoc,
   7378     uncasted_arg: Air.Inst.Ref,
   7379     comptime_arg: TypedValue,
   7380     runtime_args: []Air.Inst.Ref,
   7381     new_fn_info: InternPool.Key.FuncType,
   7382     runtime_i: *u32,
   7383 ) !void {
   7384     const mod = sema.mod;
   7385     const is_runtime = comptime_arg.val.isGenericPoison() and
   7386         comptime_arg.ty.hasRuntimeBits(mod) and
   7387         !(try sema.typeRequiresComptime(comptime_arg.ty));
   7388     if (is_runtime) {
   7389         const param_ty = new_fn_info.param_types[runtime_i.*].toType();
   7390         const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
   7391         try sema.queueFullTypeResolution(param_ty);
   7392         runtime_args[runtime_i.*] = casted_arg;
   7393         runtime_i.* += 1;
   7394     } else if (try sema.typeHasOnePossibleValue(comptime_arg.ty)) |_| {
   7395         _ = try sema.coerce(block, comptime_arg.ty, uncasted_arg, arg_src);
   7396     }
   7397 }
   7398 
   7399 fn analyzeGenericCallArgVal(
   7400     sema: *Sema,
   7401     block: *Block,
   7402     arg_src: LazySrcLoc,
   7403     arg_ty: Type,
   7404     uncasted_arg: Air.Inst.Ref,
   7405     reason: []const u8,
   7406 ) !Value {
   7407     const casted_arg = try sema.coerce(block, arg_ty, uncasted_arg, arg_src);
   7408     return sema.resolveLazyValue(try sema.resolveValue(block, arg_src, casted_arg, reason));
   7409 }
   7410 
   7411 fn instantiateGenericCall(
   7412     sema: *Sema,
   7413     block: *Block,
   7414     func: Air.Inst.Ref,
   7415     func_src: LazySrcLoc,
   7416     call_src: LazySrcLoc,
   7417     generic_func_ty: Type,
   7418     ensure_result_used: bool,
   7419     uncasted_args: []const Air.Inst.Ref,
   7420     call_tag: Air.Inst.Tag,
   7421     bound_arg_src: ?LazySrcLoc,
   7422     call_dbg_node: ?Zir.Inst.Index,
   7423 ) CompileError!Air.Inst.Ref {
   7424     const mod = sema.mod;
   7425     const gpa = sema.gpa;
   7426 
   7427     const func_val = try sema.resolveConstValue(block, func_src, func, "generic function being called must be comptime-known");
   7428     const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) {
   7429         .func => |function| function.index,
   7430         .ptr => |ptr| mod.declPtr(ptr.addr.decl).val.getFunctionIndex(mod).unwrap().?,
   7431         else => unreachable,
   7432     };
   7433     const module_fn = mod.funcPtr(module_fn_index);
   7434     // Check the Module's generic function map with an adapted context, so that we
   7435     // can match against `uncasted_args` rather than doing the work below to create a
   7436     // generic Scope only to junk it if it matches an existing instantiation.
   7437     const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
   7438     const namespace_index = fn_owner_decl.src_namespace;
   7439     const namespace = mod.namespacePtr(namespace_index);
   7440     const fn_zir = namespace.file_scope.zir;
   7441     const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
   7442     const zir_tags = fn_zir.instructions.items(.tag);
   7443 
   7444     const monomorphed_args = try sema.arena.alloc(InternPool.Index, mod.typeToFunc(generic_func_ty).?.param_types.len);
   7445     const callee_index = callee: {
   7446         var arg_i: usize = 0;
   7447         var monomorphed_arg_i: u32 = 0;
   7448         var known_unique = false;
   7449         for (fn_info.param_body) |inst| {
   7450             const generic_func_ty_info = mod.typeToFunc(generic_func_ty).?;
   7451             var is_comptime = false;
   7452             var is_anytype = false;
   7453             switch (zir_tags[inst]) {
   7454                 .param => {
   7455                     is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i)));
   7456                 },
   7457                 .param_comptime => {
   7458                     is_comptime = true;
   7459                 },
   7460                 .param_anytype => {
   7461                     is_anytype = true;
   7462                     is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i)));
   7463                 },
   7464                 .param_anytype_comptime => {
   7465                     is_anytype = true;
   7466                     is_comptime = true;
   7467                 },
   7468                 else => continue,
   7469             }
   7470 
   7471             defer arg_i += 1;
   7472             const param_ty = generic_func_ty_info.param_types[arg_i];
   7473             const is_generic = !is_anytype and param_ty == .generic_poison_type;
   7474 
   7475             if (known_unique) {
   7476                 if (is_comptime or is_anytype or is_generic) {
   7477                     monomorphed_arg_i += 1;
   7478                 }
   7479                 continue;
   7480             }
   7481 
   7482             const uncasted_arg = uncasted_args[arg_i];
   7483             const arg_ty = if (is_generic) mod.monomorphed_funcs.getAdapted(
   7484                 Module.MonomorphedFuncAdaptedKey{
   7485                     .func = module_fn_index,
   7486                     .args = monomorphed_args[0..monomorphed_arg_i],
   7487                 },
   7488                 Module.MonomorphedFuncsAdaptedContext{ .mod = mod },
   7489             ) orelse {
   7490                 known_unique = true;
   7491                 monomorphed_arg_i += 1;
   7492                 continue;
   7493             } else if (is_anytype) sema.typeOf(uncasted_arg).toIntern() else param_ty;
   7494             const was_comptime = is_comptime;
   7495             if (!is_comptime and try sema.typeRequiresComptime(arg_ty.toType())) is_comptime = true;
   7496             if (is_comptime or is_anytype) {
   7497                 // Tuple default values are a part of the type and need to be
   7498                 // resolved to hash the type.
   7499                 try sema.resolveTupleLazyValues(block, call_src, arg_ty.toType());
   7500             }
   7501 
   7502             if (is_comptime) {
   7503                 const casted_arg = sema.analyzeGenericCallArgVal(block, .unneeded, arg_ty.toType(), uncasted_arg, "") catch |err| switch (err) {
   7504                     error.NeededSourceLocation => {
   7505                         const decl = mod.declPtr(block.src_decl);
   7506                         const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src);
   7507                         _ = try sema.analyzeGenericCallArgVal(
   7508                             block,
   7509                             arg_src,
   7510                             arg_ty.toType(),
   7511                             uncasted_arg,
   7512                             if (was_comptime)
   7513                                 "parameter is comptime"
   7514                             else
   7515                                 "argument to parameter with comptime-only type must be comptime-known",
   7516                         );
   7517                         unreachable;
   7518                     },
   7519                     else => |e| return e,
   7520                 };
   7521                 monomorphed_args[monomorphed_arg_i] = casted_arg.toIntern();
   7522                 monomorphed_arg_i += 1;
   7523             } else if (is_anytype or is_generic) {
   7524                 monomorphed_args[monomorphed_arg_i] = try mod.intern(.{ .undef = arg_ty });
   7525                 monomorphed_arg_i += 1;
   7526             }
   7527         }
   7528 
   7529         if (!known_unique) {
   7530             if (mod.monomorphed_funcs.getAdapted(
   7531                 Module.MonomorphedFuncAdaptedKey{
   7532                     .func = module_fn_index,
   7533                     .args = monomorphed_args[0..monomorphed_arg_i],
   7534                 },
   7535                 Module.MonomorphedFuncsAdaptedContext{ .mod = mod },
   7536             )) |callee_func| break :callee mod.intern_pool.indexToKey(callee_func).func.index;
   7537         }
   7538 
   7539         const new_module_func_index = try mod.createFunc(undefined);
   7540         const new_module_func = mod.funcPtr(new_module_func_index);
   7541 
   7542         new_module_func.generic_owner_decl = module_fn.owner_decl.toOptional();
   7543         new_module_func.comptime_args = null;
   7544 
   7545         try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
   7546 
   7547         // Create a Decl for the new function.
   7548         const src_decl_index = namespace.getDeclIndex(mod);
   7549         const src_decl = mod.declPtr(src_decl_index);
   7550         const new_decl_index = try mod.allocateNewDecl(namespace_index, fn_owner_decl.src_node, src_decl.src_scope);
   7551         const new_decl = mod.declPtr(new_decl_index);
   7552         // TODO better names for generic function instantiations
   7553         const decl_name = try mod.intern_pool.getOrPutStringFmt(gpa, "{}__anon_{d}", .{
   7554             fn_owner_decl.name.fmt(&mod.intern_pool), @intFromEnum(new_decl_index),
   7555         });
   7556         new_decl.name = decl_name;
   7557         new_decl.src_line = fn_owner_decl.src_line;
   7558         new_decl.is_pub = fn_owner_decl.is_pub;
   7559         new_decl.is_exported = fn_owner_decl.is_exported;
   7560         new_decl.has_align = fn_owner_decl.has_align;
   7561         new_decl.has_linksection_or_addrspace = fn_owner_decl.has_linksection_or_addrspace;
   7562         new_decl.@"linksection" = fn_owner_decl.@"linksection";
   7563         new_decl.@"addrspace" = fn_owner_decl.@"addrspace";
   7564         new_decl.zir_decl_index = fn_owner_decl.zir_decl_index;
   7565         new_decl.alive = true; // This Decl is called at runtime.
   7566         new_decl.analysis = .in_progress;
   7567         new_decl.generation = mod.generation;
   7568 
   7569         namespace.anon_decls.putAssumeCapacityNoClobber(new_decl_index, {});
   7570 
   7571         // The generic function Decl is guaranteed to be the first dependency
   7572         // of each of its instantiations.
   7573         assert(new_decl.dependencies.keys().len == 0);
   7574         try mod.declareDeclDependencyType(new_decl_index, module_fn.owner_decl, .function_body);
   7575 
   7576         const new_func = sema.resolveGenericInstantiationType(
   7577             block,
   7578             fn_zir,
   7579             new_decl,
   7580             new_decl_index,
   7581             uncasted_args,
   7582             monomorphed_arg_i,
   7583             module_fn_index,
   7584             new_module_func_index,
   7585             namespace_index,
   7586             generic_func_ty,
   7587             call_src,
   7588             bound_arg_src,
   7589         ) catch |err| switch (err) {
   7590             error.GenericPoison, error.ComptimeReturn => {
   7591                 // Resolving the new function type below will possibly declare more decl dependencies
   7592                 // and so we remove them all here in case of error.
   7593                 for (new_decl.dependencies.keys()) |dep_index| {
   7594                     const dep = mod.declPtr(dep_index);
   7595                     dep.removeDependant(new_decl_index);
   7596                 }
   7597                 assert(namespace.anon_decls.orderedRemove(new_decl_index));
   7598                 mod.destroyDecl(new_decl_index);
   7599                 mod.destroyFunc(new_module_func_index);
   7600                 return err;
   7601             },
   7602             else => {
   7603                 // TODO look up the compile error that happened here and attach a note to it
   7604                 // pointing here, at the generic instantiation callsite.
   7605                 if (sema.owner_func) |owner_func| {
   7606                     owner_func.state = .dependency_failure;
   7607                 } else {
   7608                     sema.owner_decl.analysis = .dependency_failure;
   7609                 }
   7610                 return err;
   7611             },
   7612         };
   7613 
   7614         break :callee new_func;
   7615     };
   7616     const callee = mod.funcPtr(callee_index);
   7617     callee.branch_quota = @max(callee.branch_quota, sema.branch_quota);
   7618 
   7619     const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl);
   7620 
   7621     // Make a runtime call to the new function, making sure to omit the comptime args.
   7622     const comptime_args = callee.comptime_args.?;
   7623     const func_ty = mod.declPtr(callee.owner_decl).ty;
   7624     const runtime_args_len = @as(u32, @intCast(mod.typeToFunc(func_ty).?.param_types.len));
   7625     const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len);
   7626     {
   7627         var runtime_i: u32 = 0;
   7628         var total_i: u32 = 0;
   7629         for (fn_info.param_body) |inst| {
   7630             switch (zir_tags[inst]) {
   7631                 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {},
   7632                 else => continue,
   7633             }
   7634             sema.analyzeGenericCallArg(
   7635                 block,
   7636                 .unneeded,
   7637                 uncasted_args[total_i],
   7638                 comptime_args[total_i],
   7639                 runtime_args,
   7640                 mod.typeToFunc(func_ty).?,
   7641                 &runtime_i,
   7642             ) catch |err| switch (err) {
   7643                 error.NeededSourceLocation => {
   7644                     const decl = mod.declPtr(block.src_decl);
   7645                     _ = try sema.analyzeGenericCallArg(
   7646                         block,
   7647                         mod.argSrc(call_src.node_offset.x, decl, total_i, bound_arg_src),
   7648                         uncasted_args[total_i],
   7649                         comptime_args[total_i],
   7650                         runtime_args,
   7651                         mod.typeToFunc(func_ty).?,
   7652                         &runtime_i,
   7653                     );
   7654                     unreachable;
   7655                 },
   7656                 else => |e| return e,
   7657             };
   7658             total_i += 1;
   7659         }
   7660 
   7661         try sema.queueFullTypeResolution(mod.typeToFunc(func_ty).?.return_type.toType());
   7662     }
   7663 
   7664     if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
   7665 
   7666     if (sema.owner_func != null and mod.typeToFunc(func_ty).?.return_type.toType().isError(mod)) {
   7667         sema.owner_func.?.calls_or_awaits_errorable_fn = true;
   7668     }
   7669 
   7670     try mod.ensureFuncBodyAnalysisQueued(callee_index);
   7671 
   7672     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
   7673         runtime_args_len);
   7674     const result = try block.addInst(.{
   7675         .tag = call_tag,
   7676         .data = .{ .pl_op = .{
   7677             .operand = callee_inst,
   7678             .payload = sema.addExtraAssumeCapacity(Air.Call{
   7679                 .args_len = runtime_args_len,
   7680             }),
   7681         } },
   7682     });
   7683     sema.appendRefsAssumeCapacity(runtime_args);
   7684 
   7685     if (ensure_result_used) {
   7686         try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
   7687     }
   7688     if (call_tag == .call_always_tail) {
   7689         return sema.handleTailCall(block, call_src, func_ty, result);
   7690     }
   7691     if (func_ty.fnReturnType(mod).isNoReturn(mod)) {
   7692         _ = try block.addNoOp(.unreach);
   7693         return Air.Inst.Ref.unreachable_value;
   7694     }
   7695     return result;
   7696 }
   7697 
   7698 fn resolveGenericInstantiationType(
   7699     sema: *Sema,
   7700     block: *Block,
   7701     fn_zir: Zir,
   7702     new_decl: *Decl,
   7703     new_decl_index: Decl.Index,
   7704     uncasted_args: []const Air.Inst.Ref,
   7705     monomorphed_args_len: u32,
   7706     module_fn_index: Module.Fn.Index,
   7707     new_module_func: Module.Fn.Index,
   7708     namespace: Namespace.Index,
   7709     generic_func_ty: Type,
   7710     call_src: LazySrcLoc,
   7711     bound_arg_src: ?LazySrcLoc,
   7712 ) !Module.Fn.Index {
   7713     const mod = sema.mod;
   7714     const gpa = sema.gpa;
   7715 
   7716     const zir_tags = fn_zir.instructions.items(.tag);
   7717     const module_fn = mod.funcPtr(module_fn_index);
   7718     const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
   7719 
   7720     // Re-run the block that creates the function, with the comptime parameters
   7721     // pre-populated inside `inst_map`. This causes `param_comptime` and
   7722     // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a
   7723     // new, monomorphized function, with the comptime parameters elided.
   7724     var child_sema: Sema = .{
   7725         .mod = mod,
   7726         .gpa = gpa,
   7727         .arena = sema.arena,
   7728         .code = fn_zir,
   7729         .owner_decl = new_decl,
   7730         .owner_decl_index = new_decl_index,
   7731         .func = null,
   7732         .func_index = .none,
   7733         .fn_ret_ty = Type.void,
   7734         .owner_func = null,
   7735         .owner_func_index = .none,
   7736         // TODO: fully migrate functions into InternPool
   7737         .comptime_args = try mod.tmp_hack_arena.allocator().alloc(TypedValue, uncasted_args.len),
   7738         .comptime_args_fn_inst = module_fn.zir_body_inst,
   7739         .preallocated_new_func = new_module_func.toOptional(),
   7740         .is_generic_instantiation = true,
   7741         .branch_quota = sema.branch_quota,
   7742         .branch_count = sema.branch_count,
   7743         .comptime_mutable_decls = sema.comptime_mutable_decls,
   7744     };
   7745     defer child_sema.deinit();
   7746 
   7747     var wip_captures = try WipCaptureScope.init(gpa, new_decl.src_scope);
   7748     defer wip_captures.deinit();
   7749 
   7750     var child_block: Block = .{
   7751         .parent = null,
   7752         .sema = &child_sema,
   7753         .src_decl = new_decl_index,
   7754         .namespace = namespace,
   7755         .wip_capture_scope = wip_captures.scope,
   7756         .instructions = .{},
   7757         .inlining = null,
   7758         .is_comptime = true,
   7759     };
   7760     defer {
   7761         child_block.instructions.deinit(gpa);
   7762         child_block.params.deinit(gpa);
   7763     }
   7764 
   7765     try child_sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
   7766 
   7767     var arg_i: usize = 0;
   7768     for (fn_info.param_body) |inst| {
   7769         const generic_func_ty_info = mod.typeToFunc(generic_func_ty).?;
   7770         var is_comptime = false;
   7771         var is_anytype = false;
   7772         switch (zir_tags[inst]) {
   7773             .param => {
   7774                 is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i)));
   7775             },
   7776             .param_comptime => {
   7777                 is_comptime = true;
   7778             },
   7779             .param_anytype => {
   7780                 is_anytype = true;
   7781                 is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i)));
   7782             },
   7783             .param_anytype_comptime => {
   7784                 is_anytype = true;
   7785                 is_comptime = true;
   7786             },
   7787             else => continue,
   7788         }
   7789         const arg = uncasted_args[arg_i];
   7790         if (is_comptime) {
   7791             const arg_val = (try sema.resolveMaybeUndefVal(arg)).?;
   7792             const child_arg = try child_sema.addConstant(arg_val);
   7793             child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
   7794         } else if (is_anytype) {
   7795             const arg_ty = sema.typeOf(arg);
   7796             if (try sema.typeRequiresComptime(arg_ty)) {
   7797                 const arg_val = sema.resolveConstValue(block, .unneeded, arg, "") catch |err| switch (err) {
   7798                     error.NeededSourceLocation => {
   7799                         const decl = mod.declPtr(block.src_decl);
   7800                         const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src);
   7801                         _ = try sema.resolveConstValue(block, arg_src, arg, "argument to parameter with comptime-only type must be comptime-known");
   7802                         unreachable;
   7803                     },
   7804                     else => |e| return e,
   7805                 };
   7806                 const child_arg = try child_sema.addConstant(arg_val);
   7807                 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
   7808             } else {
   7809                 // We insert into the map an instruction which is runtime-known
   7810                 // but has the type of the argument.
   7811                 const child_arg = try child_block.addInst(.{
   7812                     .tag = .arg,
   7813                     .data = .{ .arg = .{
   7814                         .ty = try child_sema.addType(arg_ty),
   7815                         .src_index = @as(u32, @intCast(arg_i)),
   7816                     } },
   7817                 });
   7818                 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
   7819             }
   7820         }
   7821         arg_i += 1;
   7822     }
   7823 
   7824     // Save the error trace as our first action in the function.
   7825     // If this is unnecessary after all, Liveness will clean it up for us.
   7826     const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block);
   7827     child_sema.error_return_trace_index_on_fn_entry = error_return_trace_index;
   7828     child_block.error_return_trace_index = error_return_trace_index;
   7829 
   7830     const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst);
   7831     const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable;
   7832     const new_func = new_func_val.getFunctionIndex(mod).unwrap().?;
   7833     assert(new_func == new_module_func);
   7834 
   7835     const monomorphed_args_index = @as(u32, @intCast(mod.monomorphed_func_keys.items.len));
   7836     const monomorphed_args = try mod.monomorphed_func_keys.addManyAsSlice(gpa, monomorphed_args_len);
   7837     var monomorphed_arg_i: u32 = 0;
   7838     try mod.monomorphed_funcs.ensureUnusedCapacityContext(gpa, monomorphed_args_len + 1, .{ .mod = mod });
   7839 
   7840     arg_i = 0;
   7841     for (fn_info.param_body) |inst| {
   7842         const generic_func_ty_info = mod.typeToFunc(generic_func_ty).?;
   7843         var is_comptime = false;
   7844         var is_anytype = false;
   7845         switch (zir_tags[inst]) {
   7846             .param => {
   7847                 is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i)));
   7848             },
   7849             .param_comptime => {
   7850                 is_comptime = true;
   7851             },
   7852             .param_anytype => {
   7853                 is_anytype = true;
   7854                 is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i)));
   7855             },
   7856             .param_anytype_comptime => {
   7857                 is_anytype = true;
   7858                 is_comptime = true;
   7859             },
   7860             else => continue,
   7861         }
   7862 
   7863         const param_ty = generic_func_ty_info.param_types[arg_i];
   7864         const is_generic = !is_anytype and param_ty == .generic_poison_type;
   7865 
   7866         const arg = child_sema.inst_map.get(inst).?;
   7867         const arg_ty = child_sema.typeOf(arg);
   7868 
   7869         if (is_generic) if (mod.monomorphed_funcs.fetchPutAssumeCapacityContext(.{
   7870             .func = module_fn_index,
   7871             .args_index = monomorphed_args_index,
   7872             .args_len = monomorphed_arg_i,
   7873         }, arg_ty.toIntern(), .{ .mod = mod })) |kv| assert(kv.value == arg_ty.toIntern());
   7874         if (!is_comptime and try sema.typeRequiresComptime(arg_ty)) is_comptime = true;
   7875 
   7876         if (is_comptime) {
   7877             const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(arg) catch unreachable).?;
   7878             monomorphed_args[monomorphed_arg_i] = arg_val.toIntern();
   7879             monomorphed_arg_i += 1;
   7880             child_sema.comptime_args[arg_i] = .{ .ty = arg_ty, .val = arg_val };
   7881         } else {
   7882             if (is_anytype or is_generic) {
   7883                 monomorphed_args[monomorphed_arg_i] = try mod.intern(.{ .undef = arg_ty.toIntern() });
   7884                 monomorphed_arg_i += 1;
   7885             }
   7886             child_sema.comptime_args[arg_i] = .{ .ty = arg_ty, .val = Value.generic_poison };
   7887         }
   7888 
   7889         arg_i += 1;
   7890     }
   7891 
   7892     try wip_captures.finalize();
   7893 
   7894     // Populate the Decl ty/val with the function and its type.
   7895     new_decl.ty = child_sema.typeOf(new_func_inst);
   7896     // If the call evaluated to a return type that requires comptime, never mind
   7897     // our generic instantiation. Instead we need to perform a comptime call.
   7898     const new_fn_info = mod.typeToFunc(new_decl.ty).?;
   7899     if (try sema.typeRequiresComptime(new_fn_info.return_type.toType())) {
   7900         return error.ComptimeReturn;
   7901     }
   7902     // Similarly, if the call evaluated to a generic type we need to instead
   7903     // call it inline.
   7904     if (new_fn_info.is_generic or new_fn_info.cc == .Inline) {
   7905         return error.GenericPoison;
   7906     }
   7907 
   7908     new_decl.val = (try mod.intern(.{ .func = .{
   7909         .ty = new_decl.ty.toIntern(),
   7910         .index = new_func,
   7911     } })).toValue();
   7912     new_decl.alignment = .none;
   7913     new_decl.has_tv = true;
   7914     new_decl.owns_tv = true;
   7915     new_decl.analysis = .complete;
   7916 
   7917     mod.monomorphed_funcs.putAssumeCapacityNoClobberContext(.{
   7918         .func = module_fn_index,
   7919         .args_index = monomorphed_args_index,
   7920         .args_len = monomorphed_arg_i,
   7921     }, new_decl.val.toIntern(), .{ .mod = mod });
   7922 
   7923     // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field
   7924     // will be populated, ensuring it will have `analyzeBody` called with the ZIR
   7925     // parameters mapped appropriately.
   7926     try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
   7927     return new_func;
   7928 }
   7929 
   7930 fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
   7931     const mod = sema.mod;
   7932     const tuple = switch (mod.intern_pool.indexToKey(ty.toIntern())) {
   7933         .anon_struct_type => |tuple| tuple,
   7934         else => return,
   7935     };
   7936     for (tuple.types, tuple.values) |field_ty, field_val| {
   7937         try sema.resolveTupleLazyValues(block, src, field_ty.toType());
   7938         if (field_val == .none) continue;
   7939         // TODO: mutate in intern pool
   7940         _ = try sema.resolveLazyValue(field_val.toValue());
   7941     }
   7942 }
   7943 
   7944 fn emitDbgInline(
   7945     sema: *Sema,
   7946     block: *Block,
   7947     old_func: Module.Fn.Index,
   7948     new_func: Module.Fn.Index,
   7949     new_func_ty: Type,
   7950     tag: Air.Inst.Tag,
   7951 ) CompileError!void {
   7952     const mod = sema.mod;
   7953     if (mod.comp.bin_file.options.strip) return;
   7954 
   7955     // Recursive inline call; no dbg_inline needed.
   7956     if (old_func == new_func) return;
   7957 
   7958     _ = try block.addInst(.{
   7959         .tag = tag,
   7960         .data = .{ .ty_fn = .{
   7961             .ty = try sema.addType(new_func_ty),
   7962             .func = new_func,
   7963         } },
   7964     });
   7965 }
   7966 
   7967 fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7968     const mod = sema.mod;
   7969     const int_type = sema.code.instructions.items(.data)[inst].int_type;
   7970     const ty = try mod.intType(int_type.signedness, int_type.bit_count);
   7971     return sema.addType(ty);
   7972 }
   7973 
   7974 fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7975     const tracy = trace(@src());
   7976     defer tracy.end();
   7977 
   7978     const mod = sema.mod;
   7979     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   7980     const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
   7981     const child_type = try sema.resolveType(block, operand_src, inst_data.operand);
   7982     if (child_type.zigTypeTag(mod) == .Opaque) {
   7983         return sema.fail(block, operand_src, "opaque type '{}' cannot be optional", .{child_type.fmt(mod)});
   7984     } else if (child_type.zigTypeTag(mod) == .Null) {
   7985         return sema.fail(block, operand_src, "type '{}' cannot be optional", .{child_type.fmt(mod)});
   7986     }
   7987     const opt_type = try mod.optionalType(child_type.toIntern());
   7988 
   7989     return sema.addType(opt_type);
   7990 }
   7991 
   7992 fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7993     const mod = sema.mod;
   7994     const bin = sema.code.instructions.items(.data)[inst].bin;
   7995     const indexable_ty = try sema.resolveType(block, .unneeded, bin.lhs);
   7996     assert(indexable_ty.isIndexable(mod)); // validated by a previous instruction
   7997     if (indexable_ty.zigTypeTag(mod) == .Struct) {
   7998         const elem_type = indexable_ty.structFieldType(@intFromEnum(bin.rhs), mod);
   7999         return sema.addType(elem_type);
   8000     } else {
   8001         const elem_type = indexable_ty.elemType2(mod);
   8002         return sema.addType(elem_type);
   8003     }
   8004 }
   8005 
   8006 fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8007     const mod = sema.mod;
   8008     const un_node = sema.code.instructions.items(.data)[inst].un_node;
   8009     const ptr_ty = try sema.resolveType(block, .unneeded, un_node.operand);
   8010     assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction
   8011     return sema.addType(ptr_ty.childType(mod));
   8012 }
   8013 
   8014 fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8015     const mod = sema.mod;
   8016     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   8017     const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   8018     const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
   8019     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8020     const len = @as(u32, @intCast(try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector length must be comptime-known")));
   8021     const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs);
   8022     try sema.checkVectorElemType(block, elem_type_src, elem_type);
   8023     const vector_type = try mod.vectorType(.{
   8024         .len = len,
   8025         .child = elem_type.toIntern(),
   8026     });
   8027     return sema.addType(vector_type);
   8028 }
   8029 
   8030 fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8031     const tracy = trace(@src());
   8032     defer tracy.end();
   8033 
   8034     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   8035     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8036     const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node };
   8037     const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node };
   8038     const len = try sema.resolveInt(block, len_src, extra.lhs, Type.usize, "array length must be comptime-known");
   8039     const elem_type = try sema.resolveType(block, elem_src, extra.rhs);
   8040     try sema.validateArrayElemType(block, elem_type, elem_src);
   8041     const array_ty = try sema.mod.arrayType(.{
   8042         .len = len,
   8043         .child = elem_type.toIntern(),
   8044     });
   8045 
   8046     return sema.addType(array_ty);
   8047 }
   8048 
   8049 fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8050     const tracy = trace(@src());
   8051     defer tracy.end();
   8052 
   8053     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   8054     const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data;
   8055     const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node };
   8056     const sentinel_src: LazySrcLoc = .{ .node_offset_array_type_sentinel = inst_data.src_node };
   8057     const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node };
   8058     const len = try sema.resolveInt(block, len_src, extra.len, Type.usize, "array length must be comptime-known");
   8059     const elem_type = try sema.resolveType(block, elem_src, extra.elem_type);
   8060     try sema.validateArrayElemType(block, elem_type, elem_src);
   8061     const uncasted_sentinel = try sema.resolveInst(extra.sentinel);
   8062     const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src);
   8063     const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel, "array sentinel value must be comptime-known");
   8064     const array_ty = try sema.mod.arrayType(.{
   8065         .len = len,
   8066         .sentinel = sentinel_val.toIntern(),
   8067         .child = elem_type.toIntern(),
   8068     });
   8069 
   8070     return sema.addType(array_ty);
   8071 }
   8072 
   8073 fn validateArrayElemType(sema: *Sema, block: *Block, elem_type: Type, elem_src: LazySrcLoc) !void {
   8074     const mod = sema.mod;
   8075     if (elem_type.zigTypeTag(mod) == .Opaque) {
   8076         return sema.fail(block, elem_src, "array of opaque type '{}' not allowed", .{elem_type.fmt(mod)});
   8077     } else if (elem_type.zigTypeTag(mod) == .NoReturn) {
   8078         return sema.fail(block, elem_src, "array of 'noreturn' not allowed", .{});
   8079     }
   8080 }
   8081 
   8082 fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8083     const tracy = trace(@src());
   8084     defer tracy.end();
   8085 
   8086     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   8087     if (true) {
   8088         return sema.failWithUseOfAsync(block, inst_data.src());
   8089     }
   8090     const mod = sema.mod;
   8091     const operand_src: LazySrcLoc = .{ .node_offset_anyframe_type = inst_data.src_node };
   8092     const return_type = try sema.resolveType(block, operand_src, inst_data.operand);
   8093     const anyframe_type = try mod.anyframeType(return_type);
   8094 
   8095     return sema.addType(anyframe_type);
   8096 }
   8097 
   8098 fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8099     const tracy = trace(@src());
   8100     defer tracy.end();
   8101 
   8102     const mod = sema.mod;
   8103     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   8104     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8105     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
   8106     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
   8107     const error_set = try sema.resolveType(block, lhs_src, extra.lhs);
   8108     const payload = try sema.resolveType(block, rhs_src, extra.rhs);
   8109 
   8110     if (error_set.zigTypeTag(mod) != .ErrorSet) {
   8111         return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{
   8112             error_set.fmt(mod),
   8113         });
   8114     }
   8115     try sema.validateErrorUnionPayloadType(block, payload, rhs_src);
   8116     const err_union_ty = try mod.errorUnionType(error_set, payload);
   8117     return sema.addType(err_union_ty);
   8118 }
   8119 
   8120 fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void {
   8121     const mod = sema.mod;
   8122     if (payload_ty.zigTypeTag(mod) == .Opaque) {
   8123         return sema.fail(block, payload_src, "error union with payload of opaque type '{}' not allowed", .{
   8124             payload_ty.fmt(mod),
   8125         });
   8126     } else if (payload_ty.zigTypeTag(mod) == .ErrorSet) {
   8127         return sema.fail(block, payload_src, "error union with payload of error set type '{}' not allowed", .{
   8128             payload_ty.fmt(mod),
   8129         });
   8130     }
   8131 }
   8132 
   8133 fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8134     _ = block;
   8135     const mod = sema.mod;
   8136     const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
   8137     const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
   8138     _ = try mod.getErrorValue(name);
   8139     // Create an error set type with only this error value, and return the value.
   8140     const error_set_type = try mod.singleErrorSetType(name);
   8141     return sema.addConstant((try mod.intern(.{ .err = .{
   8142         .ty = error_set_type.toIntern(),
   8143         .name = name,
   8144     } })).toValue());
   8145 }
   8146 
   8147 fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
   8148     const tracy = trace(@src());
   8149     defer tracy.end();
   8150 
   8151     const mod = sema.mod;
   8152     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   8153     const src = LazySrcLoc.nodeOffset(extra.node);
   8154     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   8155     const uncasted_operand = try sema.resolveInst(extra.operand);
   8156     const operand = try sema.coerce(block, Type.anyerror, uncasted_operand, operand_src);
   8157 
   8158     if (try sema.resolveMaybeUndefVal(operand)) |val| {
   8159         if (val.isUndef(mod)) {
   8160             return sema.addConstUndef(Type.err_int);
   8161         }
   8162         const err_name = mod.intern_pool.indexToKey(val.toIntern()).err.name;
   8163         return sema.addConstant(try mod.intValue(
   8164             Type.err_int,
   8165             try mod.getErrorValue(err_name),
   8166         ));
   8167     }
   8168 
   8169     const op_ty = sema.typeOf(uncasted_operand);
   8170     try sema.resolveInferredErrorSetTy(block, src, op_ty);
   8171     if (!op_ty.isAnyError(mod)) {
   8172         const names = op_ty.errorSetNames(mod);
   8173         switch (names.len) {
   8174             0 => return sema.addConstant(try mod.intValue(Type.err_int, 0)),
   8175             1 => {
   8176                 const int = @as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(names[0]).?));
   8177                 return sema.addIntUnsigned(Type.err_int, int);
   8178             },
   8179             else => {},
   8180         }
   8181     }
   8182 
   8183     try sema.requireRuntimeBlock(block, src, operand_src);
   8184     return block.addBitCast(Type.err_int, operand);
   8185 }
   8186 
   8187 fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
   8188     const tracy = trace(@src());
   8189     defer tracy.end();
   8190 
   8191     const mod = sema.mod;
   8192     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   8193     const src = LazySrcLoc.nodeOffset(extra.node);
   8194     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
   8195     const uncasted_operand = try sema.resolveInst(extra.operand);
   8196     const operand = try sema.coerce(block, Type.err_int, uncasted_operand, operand_src);
   8197 
   8198     if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| {
   8199         const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(mod));
   8200         if (int > mod.global_error_set.count() or int == 0)
   8201             return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int});
   8202         return sema.addConstant((try mod.intern(.{ .err = .{
   8203             .ty = .anyerror_type,
   8204             .name = mod.global_error_set.keys()[int],
   8205         } })).toValue());
   8206     }
   8207     try sema.requireRuntimeBlock(block, src, operand_src);
   8208     if (block.wantSafety()) {
   8209         const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand);
   8210         const zero_val = try sema.addConstant(try mod.intValue(Type.err_int, 0));
   8211         const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val);
   8212         const ok = try block.addBinOp(.bit_and, is_lt_len, is_non_zero);
   8213         try sema.addSafetyCheck(block, ok, .invalid_error_code);
   8214     }
   8215     return block.addInst(.{
   8216         .tag = .bitcast,
   8217         .data = .{ .ty_op = .{
   8218             .ty = Air.Inst.Ref.anyerror_type,
   8219             .operand = operand,
   8220         } },
   8221     });
   8222 }
   8223 
   8224 fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8225     const tracy = trace(@src());
   8226     defer tracy.end();
   8227 
   8228     const mod = sema.mod;
   8229     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   8230     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8231     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
   8232     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
   8233     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
   8234     const lhs = try sema.resolveInst(extra.lhs);
   8235     const rhs = try sema.resolveInst(extra.rhs);
   8236     if (sema.typeOf(lhs).zigTypeTag(mod) == .Bool and sema.typeOf(rhs).zigTypeTag(mod) == .Bool) {
   8237         const msg = msg: {
   8238             const msg = try sema.errMsg(block, lhs_src, "expected error set type, found 'bool'", .{});
   8239             errdefer msg.destroy(sema.gpa);
   8240             try sema.errNote(block, src, msg, "'||' merges error sets; 'or' performs boolean OR", .{});
   8241             break :msg msg;
   8242         };
   8243         return sema.failWithOwnedErrorMsg(msg);
   8244     }
   8245     const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs);
   8246     const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs);
   8247     if (lhs_ty.zigTypeTag(mod) != .ErrorSet)
   8248         return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{lhs_ty.fmt(mod)});
   8249     if (rhs_ty.zigTypeTag(mod) != .ErrorSet)
   8250         return sema.fail(block, rhs_src, "expected error set type, found '{}'", .{rhs_ty.fmt(mod)});
   8251 
   8252     // Anything merged with anyerror is anyerror.
   8253     if (lhs_ty.toIntern() == .anyerror_type or rhs_ty.toIntern() == .anyerror_type) {
   8254         return Air.Inst.Ref.anyerror_type;
   8255     }
   8256 
   8257     if (mod.typeToInferredErrorSetIndex(lhs_ty).unwrap()) |ies_index| {
   8258         try sema.resolveInferredErrorSet(block, src, ies_index);
   8259         // isAnyError might have changed from a false negative to a true positive after resolution.
   8260         if (lhs_ty.isAnyError(mod)) {
   8261             return Air.Inst.Ref.anyerror_type;
   8262         }
   8263     }
   8264     if (mod.typeToInferredErrorSetIndex(rhs_ty).unwrap()) |ies_index| {
   8265         try sema.resolveInferredErrorSet(block, src, ies_index);
   8266         // isAnyError might have changed from a false negative to a true positive after resolution.
   8267         if (rhs_ty.isAnyError(mod)) {
   8268             return Air.Inst.Ref.anyerror_type;
   8269         }
   8270     }
   8271 
   8272     const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty);
   8273     return sema.addType(err_set_ty);
   8274 }
   8275 
   8276 fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8277     _ = block;
   8278     const tracy = trace(@src());
   8279     defer tracy.end();
   8280 
   8281     const mod = sema.mod;
   8282     const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
   8283     const name = inst_data.get(sema.code);
   8284     return sema.addConstant((try mod.intern(.{
   8285         .enum_literal = try mod.intern_pool.getOrPutString(sema.gpa, name),
   8286     })).toValue());
   8287 }
   8288 
   8289 fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8290     const mod = sema.mod;
   8291     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   8292     const src = inst_data.src();
   8293     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   8294     const operand = try sema.resolveInst(inst_data.operand);
   8295     const operand_ty = sema.typeOf(operand);
   8296 
   8297     const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(mod)) {
   8298         .Enum => operand,
   8299         .Union => blk: {
   8300             const union_ty = try sema.resolveTypeFields(operand_ty);
   8301             const tag_ty = union_ty.unionTagType(mod) orelse {
   8302                 return sema.fail(
   8303                     block,
   8304                     operand_src,
   8305                     "untagged union '{}' cannot be converted to integer",
   8306                     .{src},
   8307                 );
   8308             };
   8309             break :blk try sema.unionToTag(block, tag_ty, operand, operand_src);
   8310         },
   8311         else => {
   8312             return sema.fail(block, operand_src, "expected enum or tagged union, found '{}'", .{
   8313                 operand_ty.fmt(mod),
   8314             });
   8315         },
   8316     };
   8317     const enum_tag_ty = sema.typeOf(enum_tag);
   8318 
   8319     const int_tag_ty = enum_tag_ty.intTagType(mod);
   8320 
   8321     if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| {
   8322         return sema.addConstant(try mod.getCoerced(opv, int_tag_ty));
   8323     }
   8324 
   8325     if (try sema.resolveMaybeUndefVal(enum_tag)) |enum_tag_val| {
   8326         const val = try enum_tag_val.intFromEnum(enum_tag_ty, mod);
   8327         return sema.addConstant(val);
   8328     }
   8329 
   8330     try sema.requireRuntimeBlock(block, src, operand_src);
   8331     return block.addBitCast(int_tag_ty, enum_tag);
   8332 }
   8333 
   8334 fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8335     const mod = sema.mod;
   8336     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   8337     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8338     const src = inst_data.src();
   8339     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   8340     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@enumFromInt");
   8341     const operand = try sema.resolveInst(extra.rhs);
   8342 
   8343     if (dest_ty.zigTypeTag(mod) != .Enum) {
   8344         return sema.fail(block, src, "expected enum, found '{}'", .{dest_ty.fmt(mod)});
   8345     }
   8346     _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
   8347 
   8348     if (try sema.resolveMaybeUndefVal(operand)) |int_val| {
   8349         if (dest_ty.isNonexhaustiveEnum(mod)) {
   8350             const int_tag_ty = dest_ty.intTagType(mod);
   8351             if (try sema.intFitsInType(int_val, int_tag_ty, null)) {
   8352                 return sema.addConstant(try mod.getCoerced(int_val, dest_ty));
   8353             }
   8354             const msg = msg: {
   8355                 const msg = try sema.errMsg(
   8356                     block,
   8357                     src,
   8358                     "int value '{}' out of range of non-exhaustive enum '{}'",
   8359                     .{ int_val.fmtValue(sema.typeOf(operand), mod), dest_ty.fmt(mod) },
   8360                 );
   8361                 errdefer msg.destroy(sema.gpa);
   8362                 try sema.addDeclaredHereNote(msg, dest_ty);
   8363                 break :msg msg;
   8364             };
   8365             return sema.failWithOwnedErrorMsg(msg);
   8366         }
   8367         if (int_val.isUndef(mod)) {
   8368             return sema.failWithUseOfUndef(block, operand_src);
   8369         }
   8370         if (!(try sema.enumHasInt(dest_ty, int_val))) {
   8371             const msg = msg: {
   8372                 const msg = try sema.errMsg(
   8373                     block,
   8374                     src,
   8375                     "enum '{}' has no tag with value '{}'",
   8376                     .{ dest_ty.fmt(mod), int_val.fmtValue(sema.typeOf(operand), mod) },
   8377                 );
   8378                 errdefer msg.destroy(sema.gpa);
   8379                 try sema.addDeclaredHereNote(msg, dest_ty);
   8380                 break :msg msg;
   8381             };
   8382             return sema.failWithOwnedErrorMsg(msg);
   8383         }
   8384         return sema.addConstant(try mod.getCoerced(int_val, dest_ty));
   8385     }
   8386 
   8387     if (try sema.typeHasOnePossibleValue(dest_ty)) |opv| {
   8388         const result = try sema.addConstant(opv);
   8389         // The operand is runtime-known but the result is comptime-known. In
   8390         // this case we still need a safety check.
   8391         // TODO add a safety check here. we can't use is_named_enum_value -
   8392         // it needs to convert the enum back to int and make sure it equals the operand int.
   8393         return result;
   8394     }
   8395 
   8396     try sema.requireRuntimeBlock(block, src, operand_src);
   8397     const result = try block.addTyOp(.intcast, dest_ty, operand);
   8398     if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum(mod) and
   8399         mod.backendSupportsFeature(.is_named_enum_value))
   8400     {
   8401         const ok = try block.addUnOp(.is_named_enum_value, result);
   8402         try sema.addSafetyCheck(block, ok, .invalid_enum_value);
   8403     }
   8404     return result;
   8405 }
   8406 
   8407 /// Pointer in, pointer out.
   8408 fn zirOptionalPayloadPtr(
   8409     sema: *Sema,
   8410     block: *Block,
   8411     inst: Zir.Inst.Index,
   8412     safety_check: bool,
   8413 ) CompileError!Air.Inst.Ref {
   8414     const tracy = trace(@src());
   8415     defer tracy.end();
   8416 
   8417     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   8418     const optional_ptr = try sema.resolveInst(inst_data.operand);
   8419     const src = inst_data.src();
   8420 
   8421     return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false);
   8422 }
   8423 
   8424 fn analyzeOptionalPayloadPtr(
   8425     sema: *Sema,
   8426     block: *Block,
   8427     src: LazySrcLoc,
   8428     optional_ptr: Air.Inst.Ref,
   8429     safety_check: bool,
   8430     initializing: bool,
   8431 ) CompileError!Air.Inst.Ref {
   8432     const mod = sema.mod;
   8433     const optional_ptr_ty = sema.typeOf(optional_ptr);
   8434     assert(optional_ptr_ty.zigTypeTag(mod) == .Pointer);
   8435 
   8436     const opt_type = optional_ptr_ty.childType(mod);
   8437     if (opt_type.zigTypeTag(mod) != .Optional) {
   8438         return sema.fail(block, src, "expected optional type, found '{}'", .{opt_type.fmt(mod)});
   8439     }
   8440 
   8441     const child_type = opt_type.optionalChild(mod);
   8442     const child_pointer = try mod.ptrType(.{
   8443         .child = child_type.toIntern(),
   8444         .flags = .{
   8445             .is_const = optional_ptr_ty.isConstPtr(mod),
   8446             .address_space = optional_ptr_ty.ptrAddressSpace(mod),
   8447         },
   8448     });
   8449 
   8450     if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| {
   8451         if (initializing) {
   8452             if (!ptr_val.isComptimeMutablePtr(mod)) {
   8453                 // If the pointer resulting from this function was stored at comptime,
   8454                 // the optional non-null bit would be set that way. But in this case,
   8455                 // we need to emit a runtime instruction to do it.
   8456                 _ = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
   8457             }
   8458             return sema.addConstant((try mod.intern(.{ .ptr = .{
   8459                 .ty = child_pointer.toIntern(),
   8460                 .addr = .{ .opt_payload = ptr_val.toIntern() },
   8461             } })).toValue());
   8462         }
   8463         if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| {
   8464             if (val.isNull(mod)) {
   8465                 return sema.fail(block, src, "unable to unwrap null", .{});
   8466             }
   8467             // The same Value represents the pointer to the optional and the payload.
   8468             return sema.addConstant((try mod.intern(.{ .ptr = .{
   8469                 .ty = child_pointer.toIntern(),
   8470                 .addr = .{ .opt_payload = ptr_val.toIntern() },
   8471             } })).toValue());
   8472         }
   8473     }
   8474 
   8475     try sema.requireRuntimeBlock(block, src, null);
   8476     if (safety_check and block.wantSafety()) {
   8477         const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);
   8478         try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
   8479     }
   8480     const air_tag: Air.Inst.Tag = if (initializing)
   8481         .optional_payload_ptr_set
   8482     else
   8483         .optional_payload_ptr;
   8484     return block.addTyOp(air_tag, child_pointer, optional_ptr);
   8485 }
   8486 
   8487 /// Value in, value out.
   8488 fn zirOptionalPayload(
   8489     sema: *Sema,
   8490     block: *Block,
   8491     inst: Zir.Inst.Index,
   8492     safety_check: bool,
   8493 ) CompileError!Air.Inst.Ref {
   8494     const tracy = trace(@src());
   8495     defer tracy.end();
   8496 
   8497     const mod = sema.mod;
   8498     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   8499     const src = inst_data.src();
   8500     const operand = try sema.resolveInst(inst_data.operand);
   8501     const operand_ty = sema.typeOf(operand);
   8502     const result_ty = switch (operand_ty.zigTypeTag(mod)) {
   8503         .Optional => operand_ty.optionalChild(mod),
   8504         .Pointer => t: {
   8505             if (operand_ty.ptrSize(mod) != .C) {
   8506                 return sema.failWithExpectedOptionalType(block, src, operand_ty);
   8507             }
   8508             // TODO https://github.com/ziglang/zig/issues/6597
   8509             if (true) break :t operand_ty;
   8510             const ptr_info = operand_ty.ptrInfo(mod);
   8511             break :t try mod.ptrType(.{
   8512                 .child = ptr_info.child,
   8513                 .flags = .{
   8514                     .alignment = ptr_info.flags.alignment,
   8515                     .is_const = ptr_info.flags.is_const,
   8516                     .is_volatile = ptr_info.flags.is_volatile,
   8517                     .is_allowzero = ptr_info.flags.is_allowzero,
   8518                     .address_space = ptr_info.flags.address_space,
   8519                 },
   8520             });
   8521         },
   8522         else => return sema.failWithExpectedOptionalType(block, src, operand_ty),
   8523     };
   8524 
   8525     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
   8526         return if (val.optionalValue(mod)) |payload|
   8527             sema.addConstant(payload)
   8528         else
   8529             sema.fail(block, src, "unable to unwrap null", .{});
   8530     }
   8531 
   8532     try sema.requireRuntimeBlock(block, src, null);
   8533     if (safety_check and block.wantSafety()) {
   8534         const is_non_null = try block.addUnOp(.is_non_null, operand);
   8535         try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
   8536     }
   8537     return block.addTyOp(.optional_payload, result_ty, operand);
   8538 }
   8539 
   8540 /// Value in, value out
   8541 fn zirErrUnionPayload(
   8542     sema: *Sema,
   8543     block: *Block,
   8544     inst: Zir.Inst.Index,
   8545 ) CompileError!Air.Inst.Ref {
   8546     const tracy = trace(@src());
   8547     defer tracy.end();
   8548 
   8549     const mod = sema.mod;
   8550     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   8551     const src = inst_data.src();
   8552     const operand = try sema.resolveInst(inst_data.operand);
   8553     const operand_src = src;
   8554     const err_union_ty = sema.typeOf(operand);
   8555     if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
   8556         return sema.fail(block, operand_src, "expected error union type, found '{}'", .{
   8557             err_union_ty.fmt(mod),
   8558         });
   8559     }
   8560     return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false);
   8561 }
   8562 
   8563 fn analyzeErrUnionPayload(
   8564     sema: *Sema,
   8565     block: *Block,
   8566     src: LazySrcLoc,
   8567     err_union_ty: Type,
   8568     operand: Air.Inst.Ref,
   8569     operand_src: LazySrcLoc,
   8570     safety_check: bool,
   8571 ) CompileError!Air.Inst.Ref {
   8572     const mod = sema.mod;
   8573     const payload_ty = err_union_ty.errorUnionPayload(mod);
   8574     if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
   8575         if (val.getErrorName(mod).unwrap()) |name| {
   8576             return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&mod.intern_pool)});
   8577         }
   8578         return sema.addConstant(
   8579             mod.intern_pool.indexToKey(val.toIntern()).error_union.val.payload.toValue(),
   8580         );
   8581     }
   8582 
   8583     try sema.requireRuntimeBlock(block, src, null);
   8584 
   8585     // If the error set has no fields then no safety check is needed.
   8586     if (safety_check and block.wantSafety() and
   8587         !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod))
   8588     {
   8589         try sema.panicUnwrapError(block, operand, .unwrap_errunion_err, .is_non_err);
   8590     }
   8591 
   8592     return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
   8593 }
   8594 
   8595 /// Pointer in, pointer out.
   8596 fn zirErrUnionPayloadPtr(
   8597     sema: *Sema,
   8598     block: *Block,
   8599     inst: Zir.Inst.Index,
   8600 ) CompileError!Air.Inst.Ref {
   8601     const tracy = trace(@src());
   8602     defer tracy.end();
   8603 
   8604     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   8605     const operand = try sema.resolveInst(inst_data.operand);
   8606     const src = inst_data.src();
   8607 
   8608     return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
   8609 }
   8610 
   8611 fn analyzeErrUnionPayloadPtr(
   8612     sema: *Sema,
   8613     block: *Block,
   8614     src: LazySrcLoc,
   8615     operand: Air.Inst.Ref,
   8616     safety_check: bool,
   8617     initializing: bool,
   8618 ) CompileError!Air.Inst.Ref {
   8619     const mod = sema.mod;
   8620     const operand_ty = sema.typeOf(operand);
   8621     assert(operand_ty.zigTypeTag(mod) == .Pointer);
   8622 
   8623     if (operand_ty.childType(mod).zigTypeTag(mod) != .ErrorUnion) {
   8624         return sema.fail(block, src, "expected error union type, found '{}'", .{
   8625             operand_ty.childType(mod).fmt(mod),
   8626         });
   8627     }
   8628 
   8629     const err_union_ty = operand_ty.childType(mod);
   8630     const payload_ty = err_union_ty.errorUnionPayload(mod);
   8631     const operand_pointer_ty = try mod.ptrType(.{
   8632         .child = payload_ty.toIntern(),
   8633         .flags = .{
   8634             .is_const = operand_ty.isConstPtr(mod),
   8635             .address_space = operand_ty.ptrAddressSpace(mod),
   8636         },
   8637     });
   8638 
   8639     if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| {
   8640         if (initializing) {
   8641             if (!ptr_val.isComptimeMutablePtr(mod)) {
   8642                 // If the pointer resulting from this function was stored at comptime,
   8643                 // the error union error code would be set that way. But in this case,
   8644                 // we need to emit a runtime instruction to do it.
   8645                 try sema.requireRuntimeBlock(block, src, null);
   8646                 _ = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
   8647             }
   8648             return sema.addConstant((try mod.intern(.{ .ptr = .{
   8649                 .ty = operand_pointer_ty.toIntern(),
   8650                 .addr = .{ .eu_payload = ptr_val.toIntern() },
   8651             } })).toValue());
   8652         }
   8653         if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| {
   8654             if (val.getErrorName(mod).unwrap()) |name| {
   8655                 return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&mod.intern_pool)});
   8656             }
   8657             return sema.addConstant((try mod.intern(.{ .ptr = .{
   8658                 .ty = operand_pointer_ty.toIntern(),
   8659                 .addr = .{ .eu_payload = ptr_val.toIntern() },
   8660             } })).toValue());
   8661         }
   8662     }
   8663 
   8664     try sema.requireRuntimeBlock(block, src, null);
   8665 
   8666     // If the error set has no fields then no safety check is needed.
   8667     if (safety_check and block.wantSafety() and
   8668         !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod))
   8669     {
   8670         try sema.panicUnwrapError(block, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
   8671     }
   8672 
   8673     const air_tag: Air.Inst.Tag = if (initializing)
   8674         .errunion_payload_ptr_set
   8675     else
   8676         .unwrap_errunion_payload_ptr;
   8677     return block.addTyOp(air_tag, operand_pointer_ty, operand);
   8678 }
   8679 
   8680 /// Value in, value out
   8681 fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8682     const tracy = trace(@src());
   8683     defer tracy.end();
   8684 
   8685     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   8686     const src = inst_data.src();
   8687     const operand = try sema.resolveInst(inst_data.operand);
   8688     return sema.analyzeErrUnionCode(block, src, operand);
   8689 }
   8690 
   8691 fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref {
   8692     const mod = sema.mod;
   8693     const operand_ty = sema.typeOf(operand);
   8694     if (operand_ty.zigTypeTag(mod) != .ErrorUnion) {
   8695         return sema.fail(block, src, "expected error union type, found '{}'", .{
   8696             operand_ty.fmt(mod),
   8697         });
   8698     }
   8699 
   8700     const result_ty = operand_ty.errorUnionSet(mod);
   8701 
   8702     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
   8703         return sema.addConstant((try mod.intern(.{ .err = .{
   8704             .ty = result_ty.toIntern(),
   8705             .name = mod.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
   8706         } })).toValue());
   8707     }
   8708 
   8709     try sema.requireRuntimeBlock(block, src, null);
   8710     return block.addTyOp(.unwrap_errunion_err, result_ty, operand);
   8711 }
   8712 
   8713 /// Pointer in, value out
   8714 fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8715     const tracy = trace(@src());
   8716     defer tracy.end();
   8717 
   8718     const mod = sema.mod;
   8719     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   8720     const src = inst_data.src();
   8721     const operand = try sema.resolveInst(inst_data.operand);
   8722     const operand_ty = sema.typeOf(operand);
   8723     assert(operand_ty.zigTypeTag(mod) == .Pointer);
   8724 
   8725     if (operand_ty.childType(mod).zigTypeTag(mod) != .ErrorUnion) {
   8726         return sema.fail(block, src, "expected error union type, found '{}'", .{
   8727             operand_ty.childType(mod).fmt(mod),
   8728         });
   8729     }
   8730 
   8731     const result_ty = operand_ty.childType(mod).errorUnionSet(mod);
   8732 
   8733     if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
   8734         if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {
   8735             assert(val.getErrorName(mod) != .none);
   8736             return sema.addConstant(val);
   8737         }
   8738     }
   8739 
   8740     try sema.requireRuntimeBlock(block, src, null);
   8741     return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand);
   8742 }
   8743 
   8744 fn zirFunc(
   8745     sema: *Sema,
   8746     block: *Block,
   8747     inst: Zir.Inst.Index,
   8748     inferred_error_set: bool,
   8749 ) CompileError!Air.Inst.Ref {
   8750     const tracy = trace(@src());
   8751     defer tracy.end();
   8752 
   8753     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   8754     const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
   8755     const target = sema.mod.getTarget();
   8756     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node };
   8757 
   8758     var extra_index = extra.end;
   8759 
   8760     const ret_ty: Type = switch (extra.data.ret_body_len) {
   8761         0 => Type.void,
   8762         1 => blk: {
   8763             const ret_ty_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
   8764             extra_index += 1;
   8765             if (sema.resolveType(block, ret_ty_src, ret_ty_ref)) |ret_ty| {
   8766                 break :blk ret_ty;
   8767             } else |err| switch (err) {
   8768                 error.GenericPoison => {
   8769                     break :blk Type.generic_poison;
   8770                 },
   8771                 else => |e| return e,
   8772             }
   8773         },
   8774         else => blk: {
   8775             const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len];
   8776             extra_index += ret_ty_body.len;
   8777 
   8778             const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type, "return type must be comptime-known");
   8779             break :blk ret_ty_val.toType();
   8780         },
   8781     };
   8782 
   8783     var src_locs: Zir.Inst.Func.SrcLocs = undefined;
   8784     const has_body = extra.data.body_len != 0;
   8785     if (has_body) {
   8786         extra_index += extra.data.body_len;
   8787         src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
   8788     }
   8789 
   8790     // If this instruction has a body it means it's the type of the `owner_decl`
   8791     // otherwise it's a function type without a `callconv` attribute and should
   8792     // never be `.C`.
   8793     // NOTE: revisit when doing #1717
   8794     const cc: std.builtin.CallingConvention = if (sema.owner_decl.is_exported and has_body)
   8795         .C
   8796     else
   8797         .Unspecified;
   8798 
   8799     return sema.funcCommon(
   8800         block,
   8801         inst_data.src_node,
   8802         inst,
   8803         .none,
   8804         target_util.defaultAddressSpace(target, .function),
   8805         FuncLinkSection.default,
   8806         cc,
   8807         ret_ty,
   8808         false,
   8809         inferred_error_set,
   8810         false,
   8811         has_body,
   8812         src_locs,
   8813         null,
   8814         0,
   8815         false,
   8816     );
   8817 }
   8818 
   8819 fn resolveGenericBody(
   8820     sema: *Sema,
   8821     block: *Block,
   8822     src: LazySrcLoc,
   8823     body: []const Zir.Inst.Index,
   8824     func_inst: Zir.Inst.Index,
   8825     dest_ty: Type,
   8826     reason: []const u8,
   8827 ) !Value {
   8828     assert(body.len != 0);
   8829 
   8830     const err = err: {
   8831         // Make sure any nested param instructions don't clobber our work.
   8832         const prev_params = block.params;
   8833         block.params = .{};
   8834         defer {
   8835             block.params.deinit(sema.gpa);
   8836             block.params = prev_params;
   8837         }
   8838 
   8839         const uncasted = sema.resolveBody(block, body, func_inst) catch |err| break :err err;
   8840         const result = sema.coerce(block, dest_ty, uncasted, src) catch |err| break :err err;
   8841         const val = sema.resolveConstValue(block, src, result, reason) catch |err| break :err err;
   8842         return val;
   8843     };
   8844     switch (err) {
   8845         error.GenericPoison => {
   8846             if (dest_ty.toIntern() == .type_type) {
   8847                 return Value.generic_poison_type;
   8848             } else {
   8849                 return Value.generic_poison;
   8850             }
   8851         },
   8852         else => |e| return e,
   8853     }
   8854 }
   8855 
   8856 /// Given a library name, examines if the library name should end up in
   8857 /// `link.File.Options.system_libs` table (for example, libc is always
   8858 /// specified via dedicated flag `link.File.Options.link_libc` instead),
   8859 /// and puts it there if it doesn't exist.
   8860 /// It also dupes the library name which can then be saved as part of the
   8861 /// respective `Decl` (either `ExternFn` or `Var`).
   8862 /// The liveness of the duped library name is tied to liveness of `Module`.
   8863 /// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`).
   8864 fn handleExternLibName(
   8865     sema: *Sema,
   8866     block: *Block,
   8867     src_loc: LazySrcLoc,
   8868     lib_name: []const u8,
   8869 ) CompileError![:0]u8 {
   8870     blk: {
   8871         const mod = sema.mod;
   8872         const comp = mod.comp;
   8873         const target = mod.getTarget();
   8874         log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});
   8875         if (target_util.is_libc_lib_name(target, lib_name)) {
   8876             if (!comp.bin_file.options.link_libc and !comp.bin_file.options.parent_compilation_link_libc) {
   8877                 return sema.fail(
   8878                     block,
   8879                     src_loc,
   8880                     "dependency on libc must be explicitly specified in the build command",
   8881                     .{},
   8882                 );
   8883             }
   8884             comp.bin_file.options.link_libc = true;
   8885             break :blk;
   8886         }
   8887         if (target_util.is_libcpp_lib_name(target, lib_name)) {
   8888             if (!comp.bin_file.options.link_libcpp) {
   8889                 return sema.fail(
   8890                     block,
   8891                     src_loc,
   8892                     "dependency on libc++ must be explicitly specified in the build command",
   8893                     .{},
   8894                 );
   8895             }
   8896             comp.bin_file.options.link_libcpp = true;
   8897             break :blk;
   8898         }
   8899         if (mem.eql(u8, lib_name, "unwind")) {
   8900             comp.bin_file.options.link_libunwind = true;
   8901             break :blk;
   8902         }
   8903         if (!target.isWasm() and !comp.bin_file.options.pic) {
   8904             return sema.fail(
   8905                 block,
   8906                 src_loc,
   8907                 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by '-l{s}' or '-fPIC'.",
   8908                 .{ lib_name, lib_name },
   8909             );
   8910         }
   8911         comp.addLinkLib(lib_name) catch |err| {
   8912             return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{
   8913                 lib_name, @errorName(err),
   8914             });
   8915         };
   8916     }
   8917     return sema.gpa.dupeZ(u8, lib_name);
   8918 }
   8919 
   8920 const FuncLinkSection = union(enum) {
   8921     generic,
   8922     default,
   8923     explicit: InternPool.NullTerminatedString,
   8924 };
   8925 
   8926 fn funcCommon(
   8927     sema: *Sema,
   8928     block: *Block,
   8929     src_node_offset: i32,
   8930     func_inst: Zir.Inst.Index,
   8931     /// null means generic poison
   8932     alignment: ?Alignment,
   8933     /// null means generic poison
   8934     address_space: ?std.builtin.AddressSpace,
   8935     /// outer null means generic poison; inner null means default link section
   8936     section: FuncLinkSection,
   8937     /// null means generic poison
   8938     cc: ?std.builtin.CallingConvention,
   8939     /// this might be Type.generic_poison
   8940     bare_return_type: Type,
   8941     var_args: bool,
   8942     inferred_error_set: bool,
   8943     is_extern: bool,
   8944     has_body: bool,
   8945     src_locs: Zir.Inst.Func.SrcLocs,
   8946     opt_lib_name: ?[]const u8,
   8947     noalias_bits: u32,
   8948     is_noinline: bool,
   8949 ) CompileError!Air.Inst.Ref {
   8950     const mod = sema.mod;
   8951     const gpa = sema.gpa;
   8952     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
   8953     const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset };
   8954     const func_src = LazySrcLoc.nodeOffset(src_node_offset);
   8955 
   8956     var is_generic = bare_return_type.isGenericPoison() or
   8957         alignment == null or
   8958         address_space == null or
   8959         section == .generic or
   8960         cc == null;
   8961 
   8962     if (var_args) {
   8963         if (is_generic) {
   8964             return sema.fail(block, func_src, "generic function cannot be variadic", .{});
   8965         }
   8966         if (cc.? != .C) {
   8967             return sema.fail(block, cc_src, "variadic function must have 'C' calling convention", .{});
   8968         }
   8969     }
   8970 
   8971     var destroy_fn_on_error = false;
   8972     const new_func_index = new_func: {
   8973         if (!has_body) break :new_func undefined;
   8974         if (sema.comptime_args_fn_inst == func_inst) {
   8975             const new_func_index = sema.preallocated_new_func.unwrap().?;
   8976             sema.preallocated_new_func = .none; // take ownership
   8977             break :new_func new_func_index;
   8978         }
   8979         destroy_fn_on_error = true;
   8980         var new_func: Module.Fn = undefined;
   8981         // Set this here so that the inferred return type can be printed correctly if it appears in an error.
   8982         new_func.owner_decl = sema.owner_decl_index;
   8983         const new_func_index = try mod.createFunc(new_func);
   8984         break :new_func new_func_index;
   8985     };
   8986     errdefer if (destroy_fn_on_error) mod.destroyFunc(new_func_index);
   8987 
   8988     const target = mod.getTarget();
   8989     const fn_ty: Type = fn_ty: {
   8990         // In the case of generic calling convention, or generic alignment, we use
   8991         // default values which are only meaningful for the generic function, *not*
   8992         // the instantiation, which can depend on comptime parameters.
   8993         // Related proposal: https://github.com/ziglang/zig/issues/11834
   8994         const cc_resolved = cc orelse .Unspecified;
   8995         const param_types = try sema.arena.alloc(InternPool.Index, block.params.items.len);
   8996         var comptime_bits: u32 = 0;
   8997         for (param_types, block.params.items, 0..) |*dest_param_ty, param, i| {
   8998             const is_noalias = blk: {
   8999                 const index = std.math.cast(u5, i) orelse break :blk false;
   9000                 break :blk @as(u1, @truncate(noalias_bits >> index)) != 0;
   9001             };
   9002             dest_param_ty.* = param.ty.toIntern();
   9003             sema.analyzeParameter(
   9004                 block,
   9005                 .unneeded,
   9006                 param,
   9007                 &comptime_bits,
   9008                 i,
   9009                 &is_generic,
   9010                 cc_resolved,
   9011                 has_body,
   9012                 is_noalias,
   9013             ) catch |err| switch (err) {
   9014                 error.NeededSourceLocation => {
   9015                     const decl = mod.declPtr(block.src_decl);
   9016                     try sema.analyzeParameter(
   9017                         block,
   9018                         Module.paramSrc(src_node_offset, mod, decl, i),
   9019                         param,
   9020                         &comptime_bits,
   9021                         i,
   9022                         &is_generic,
   9023                         cc_resolved,
   9024                         has_body,
   9025                         is_noalias,
   9026                     );
   9027                     unreachable;
   9028                 },
   9029                 else => |e| return e,
   9030             };
   9031         }
   9032 
   9033         var ret_ty_requires_comptime = false;
   9034         const ret_poison = if (sema.typeRequiresComptime(bare_return_type)) |ret_comptime| rp: {
   9035             ret_ty_requires_comptime = ret_comptime;
   9036             break :rp bare_return_type.isGenericPoison();
   9037         } else |err| switch (err) {
   9038             error.GenericPoison => rp: {
   9039                 is_generic = true;
   9040                 break :rp true;
   9041             },
   9042             else => |e| return e,
   9043         };
   9044 
   9045         const return_type: Type = if (!inferred_error_set or ret_poison)
   9046             bare_return_type
   9047         else blk: {
   9048             try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src);
   9049             const ies_index = try mod.intern_pool.createInferredErrorSet(gpa, .{
   9050                 .func = new_func_index,
   9051             });
   9052             const error_set_ty = try mod.intern(.{ .inferred_error_set_type = ies_index });
   9053             break :blk try mod.errorUnionType(error_set_ty.toType(), bare_return_type);
   9054         };
   9055 
   9056         if (!return_type.isValidReturnType(mod)) {
   9057             const opaque_str = if (return_type.zigTypeTag(mod) == .Opaque) "opaque " else "";
   9058             const msg = msg: {
   9059                 const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{
   9060                     opaque_str, return_type.fmt(mod),
   9061                 });
   9062                 errdefer msg.destroy(gpa);
   9063 
   9064                 try sema.addDeclaredHereNote(msg, return_type);
   9065                 break :msg msg;
   9066             };
   9067             return sema.failWithOwnedErrorMsg(msg);
   9068         }
   9069         if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved) and
   9070             !try sema.validateExternType(return_type, .ret_ty))
   9071         {
   9072             const msg = msg: {
   9073                 const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{
   9074                     return_type.fmt(mod), @tagName(cc_resolved),
   9075                 });
   9076                 errdefer msg.destroy(gpa);
   9077 
   9078                 const src_decl = mod.declPtr(block.src_decl);
   9079                 try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl, mod), return_type, .ret_ty);
   9080 
   9081                 try sema.addDeclaredHereNote(msg, return_type);
   9082                 break :msg msg;
   9083             };
   9084             return sema.failWithOwnedErrorMsg(msg);
   9085         }
   9086 
   9087         // If the return type is comptime-only but not dependent on parameters then all parameter types also need to be comptime
   9088         if (!sema.is_generic_instantiation and has_body and ret_ty_requires_comptime) comptime_check: {
   9089             for (block.params.items) |param| {
   9090                 if (!param.is_comptime) break;
   9091             } else break :comptime_check;
   9092 
   9093             const msg = try sema.errMsg(
   9094                 block,
   9095                 ret_ty_src,
   9096                 "function with comptime-only return type '{}' requires all parameters to be comptime",
   9097                 .{return_type.fmt(mod)},
   9098             );
   9099             try sema.explainWhyTypeIsComptime(msg, ret_ty_src.toSrcLoc(sema.owner_decl, mod), return_type);
   9100 
   9101             const tags = sema.code.instructions.items(.tag);
   9102             const data = sema.code.instructions.items(.data);
   9103             const param_body = sema.code.getParamBody(func_inst);
   9104             for (block.params.items, 0..) |param, i| {
   9105                 if (!param.is_comptime) {
   9106                     const param_index = param_body[i];
   9107                     const param_src = switch (tags[param_index]) {
   9108                         .param => data[param_index].pl_tok.src(),
   9109                         .param_anytype => data[param_index].str_tok.src(),
   9110                         else => unreachable,
   9111                     };
   9112                     if (param.name.len != 0) {
   9113                         try sema.errNote(block, param_src, msg, "param '{s}' is required to be comptime", .{param.name});
   9114                     } else {
   9115                         try sema.errNote(block, param_src, msg, "param is required to be comptime", .{});
   9116                     }
   9117                 }
   9118             }
   9119             return sema.failWithOwnedErrorMsg(msg);
   9120         }
   9121 
   9122         const arch = mod.getTarget().cpu.arch;
   9123         if (switch (cc_resolved) {
   9124             .Unspecified, .C, .Naked, .Async, .Inline => null,
   9125             .Interrupt => switch (arch) {
   9126                 .x86, .x86_64, .avr, .msp430 => null,
   9127                 else => @as([]const u8, "x86, x86_64, AVR, and MSP430"),
   9128             },
   9129             .Signal => switch (arch) {
   9130                 .avr => null,
   9131                 else => @as([]const u8, "AVR"),
   9132             },
   9133             .Stdcall, .Fastcall, .Thiscall => switch (arch) {
   9134                 .x86 => null,
   9135                 else => @as([]const u8, "x86"),
   9136             },
   9137             .Vectorcall => switch (arch) {
   9138                 .x86, .aarch64, .aarch64_be, .aarch64_32 => null,
   9139                 else => @as([]const u8, "x86 and AArch64"),
   9140             },
   9141             .APCS, .AAPCS, .AAPCSVFP => switch (arch) {
   9142                 .arm, .armeb, .aarch64, .aarch64_be, .aarch64_32, .thumb, .thumbeb => null,
   9143                 else => @as([]const u8, "ARM"),
   9144             },
   9145             .SysV, .Win64 => switch (arch) {
   9146                 .x86_64 => null,
   9147                 else => @as([]const u8, "x86_64"),
   9148             },
   9149             .Kernel => switch (arch) {
   9150                 .nvptx, .nvptx64, .amdgcn, .spirv32, .spirv64 => null,
   9151                 else => @as([]const u8, "nvptx, amdgcn and SPIR-V"),
   9152             },
   9153         }) |allowed_platform| {
   9154             return sema.fail(block, cc_src, "callconv '{s}' is only available on {s}, not {s}", .{
   9155                 @tagName(cc_resolved),
   9156                 allowed_platform,
   9157                 @tagName(arch),
   9158             });
   9159         }
   9160 
   9161         if (cc_resolved == .Inline and is_noinline) {
   9162             return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'Inline'", .{});
   9163         }
   9164         if (is_generic and sema.no_partial_func_ty) return error.GenericPoison;
   9165         is_generic = is_generic or comptime_bits != 0 or ret_ty_requires_comptime;
   9166 
   9167         if (!is_generic and sema.wantErrorReturnTracing(return_type)) {
   9168             // Make sure that StackTrace's fields are resolved so that the backend can
   9169             // lower this fn type.
   9170             const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
   9171             _ = try sema.resolveTypeFields(unresolved_stack_trace_ty);
   9172         }
   9173 
   9174         break :fn_ty try mod.funcType(.{
   9175             .param_types = param_types,
   9176             .noalias_bits = noalias_bits,
   9177             .comptime_bits = comptime_bits,
   9178             .return_type = return_type.toIntern(),
   9179             .cc = cc_resolved,
   9180             .cc_is_generic = cc == null,
   9181             .alignment = alignment orelse .none,
   9182             .align_is_generic = alignment == null,
   9183             .section_is_generic = section == .generic,
   9184             .addrspace_is_generic = address_space == null,
   9185             .is_var_args = var_args,
   9186             .is_generic = is_generic,
   9187             .is_noinline = is_noinline,
   9188         });
   9189     };
   9190 
   9191     sema.owner_decl.@"linksection" = switch (section) {
   9192         .generic => .none,
   9193         .default => .none,
   9194         .explicit => |section_name| section_name.toOptional(),
   9195     };
   9196     sema.owner_decl.alignment = alignment orelse .none;
   9197     sema.owner_decl.@"addrspace" = address_space orelse .generic;
   9198 
   9199     if (is_extern) {
   9200         return sema.addConstant((try mod.intern(.{ .extern_func = .{
   9201             .ty = fn_ty.toIntern(),
   9202             .decl = sema.owner_decl_index,
   9203             .lib_name = if (opt_lib_name) |lib_name| (try mod.intern_pool.getOrPutString(
   9204                 gpa,
   9205                 try sema.handleExternLibName(block, .{
   9206                     .node_offset_lib_name = src_node_offset,
   9207                 }, lib_name),
   9208             )).toOptional() else .none,
   9209         } })).toValue());
   9210     }
   9211 
   9212     if (!has_body) {
   9213         return sema.addType(fn_ty);
   9214     }
   9215 
   9216     const is_inline = fn_ty.fnCallingConvention(mod) == .Inline;
   9217     const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .none;
   9218 
   9219     const comptime_args: ?[*]TypedValue = if (sema.comptime_args_fn_inst == func_inst) blk: {
   9220         break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr;
   9221     } else null;
   9222 
   9223     const new_func = mod.funcPtr(new_func_index);
   9224     const hash = new_func.hash;
   9225     const generic_owner_decl = if (comptime_args == null) .none else new_func.generic_owner_decl;
   9226     new_func.* = .{
   9227         .state = anal_state,
   9228         .zir_body_inst = func_inst,
   9229         .owner_decl = sema.owner_decl_index,
   9230         .generic_owner_decl = generic_owner_decl,
   9231         .comptime_args = comptime_args,
   9232         .hash = hash,
   9233         .lbrace_line = src_locs.lbrace_line,
   9234         .rbrace_line = src_locs.rbrace_line,
   9235         .lbrace_column = @as(u16, @truncate(src_locs.columns)),
   9236         .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),
   9237         .branch_quota = default_branch_quota,
   9238         .is_noinline = is_noinline,
   9239     };
   9240     return sema.addConstant((try mod.intern(.{ .func = .{
   9241         .ty = fn_ty.toIntern(),
   9242         .index = new_func_index,
   9243     } })).toValue());
   9244 }
   9245 
   9246 fn analyzeParameter(
   9247     sema: *Sema,
   9248     block: *Block,
   9249     param_src: LazySrcLoc,
   9250     param: Block.Param,
   9251     comptime_bits: *u32,
   9252     i: usize,
   9253     is_generic: *bool,
   9254     cc: std.builtin.CallingConvention,
   9255     has_body: bool,
   9256     is_noalias: bool,
   9257 ) !void {
   9258     const mod = sema.mod;
   9259     const requires_comptime = try sema.typeRequiresComptime(param.ty);
   9260     if (param.is_comptime or requires_comptime) {
   9261         comptime_bits.* |= @as(u32, 1) << @as(u5, @intCast(i)); // TODO: handle cast error
   9262     }
   9263     const this_generic = param.ty.isGenericPoison();
   9264     is_generic.* = is_generic.* or this_generic;
   9265     const target = mod.getTarget();
   9266     if (param.is_comptime and !target_util.fnCallConvAllowsZigTypes(target, cc)) {
   9267         return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
   9268     }
   9269     if (this_generic and !sema.no_partial_func_ty and !target_util.fnCallConvAllowsZigTypes(target, cc)) {
   9270         return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
   9271     }
   9272     if (!param.ty.isValidParamType(mod)) {
   9273         const opaque_str = if (param.ty.zigTypeTag(mod) == .Opaque) "opaque " else "";
   9274         const msg = msg: {
   9275             const msg = try sema.errMsg(block, param_src, "parameter of {s}type '{}' not allowed", .{
   9276                 opaque_str, param.ty.fmt(mod),
   9277             });
   9278             errdefer msg.destroy(sema.gpa);
   9279 
   9280             try sema.addDeclaredHereNote(msg, param.ty);
   9281             break :msg msg;
   9282         };
   9283         return sema.failWithOwnedErrorMsg(msg);
   9284     }
   9285     if (!this_generic and !target_util.fnCallConvAllowsZigTypes(target, cc) and !try sema.validateExternType(param.ty, .param_ty)) {
   9286         const msg = msg: {
   9287             const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{
   9288                 param.ty.fmt(mod), @tagName(cc),
   9289             });
   9290             errdefer msg.destroy(sema.gpa);
   9291 
   9292             const src_decl = mod.declPtr(block.src_decl);
   9293             try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl, mod), param.ty, .param_ty);
   9294 
   9295             try sema.addDeclaredHereNote(msg, param.ty);
   9296             break :msg msg;
   9297         };
   9298         return sema.failWithOwnedErrorMsg(msg);
   9299     }
   9300     if (!sema.is_generic_instantiation and requires_comptime and !param.is_comptime and has_body) {
   9301         const msg = msg: {
   9302             const msg = try sema.errMsg(block, param_src, "parameter of type '{}' must be declared comptime", .{
   9303                 param.ty.fmt(mod),
   9304             });
   9305             errdefer msg.destroy(sema.gpa);
   9306 
   9307             const src_decl = mod.declPtr(block.src_decl);
   9308             try sema.explainWhyTypeIsComptime(msg, param_src.toSrcLoc(src_decl, mod), param.ty);
   9309 
   9310             try sema.addDeclaredHereNote(msg, param.ty);
   9311             break :msg msg;
   9312         };
   9313         return sema.failWithOwnedErrorMsg(msg);
   9314     }
   9315     if (!sema.is_generic_instantiation and !this_generic and is_noalias and
   9316         !(param.ty.zigTypeTag(mod) == .Pointer or param.ty.isPtrLikeOptional(mod)))
   9317     {
   9318         return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{});
   9319     }
   9320 }
   9321 
   9322 fn zirParam(
   9323     sema: *Sema,
   9324     block: *Block,
   9325     inst: Zir.Inst.Index,
   9326     comptime_syntax: bool,
   9327 ) CompileError!void {
   9328     const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
   9329     const src = inst_data.src();
   9330     const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
   9331     const param_name = sema.code.nullTerminatedString(extra.data.name);
   9332     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
   9333 
   9334     // We could be in a generic function instantiation, or we could be evaluating a generic
   9335     // function without any comptime args provided.
   9336     const param_ty = param_ty: {
   9337         const err = err: {
   9338             // Make sure any nested param instructions don't clobber our work.
   9339             const prev_params = block.params;
   9340             const prev_preallocated_new_func = sema.preallocated_new_func;
   9341             const prev_no_partial_func_type = sema.no_partial_func_ty;
   9342             block.params = .{};
   9343             sema.preallocated_new_func = .none;
   9344             sema.no_partial_func_ty = true;
   9345             defer {
   9346                 block.params.deinit(sema.gpa);
   9347                 block.params = prev_params;
   9348                 sema.preallocated_new_func = prev_preallocated_new_func;
   9349                 sema.no_partial_func_ty = prev_no_partial_func_type;
   9350             }
   9351 
   9352             if (sema.resolveBody(block, body, inst)) |param_ty_inst| {
   9353                 if (sema.analyzeAsType(block, src, param_ty_inst)) |param_ty| {
   9354                     break :param_ty param_ty;
   9355                 } else |err| break :err err;
   9356             } else |err| break :err err;
   9357         };
   9358         switch (err) {
   9359             error.GenericPoison => {
   9360                 if (sema.inst_map.get(inst)) |_| {
   9361                     // A generic function is about to evaluate to another generic function.
   9362                     // Return an error instead.
   9363                     return error.GenericPoison;
   9364                 }
   9365                 // The type is not available until the generic instantiation.
   9366                 // We result the param instruction with a poison value and
   9367                 // insert an anytype parameter.
   9368                 try block.params.append(sema.gpa, .{
   9369                     .ty = Type.generic_poison,
   9370                     .is_comptime = comptime_syntax,
   9371                     .name = param_name,
   9372                 });
   9373                 sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison);
   9374                 return;
   9375             },
   9376             else => |e| return e,
   9377         }
   9378     };
   9379     const is_comptime = sema.typeRequiresComptime(param_ty) catch |err| switch (err) {
   9380         error.GenericPoison => {
   9381             if (sema.inst_map.get(inst)) |_| {
   9382                 // A generic function is about to evaluate to another generic function.
   9383                 // Return an error instead.
   9384                 return error.GenericPoison;
   9385             }
   9386             // The type is not available until the generic instantiation.
   9387             // We result the param instruction with a poison value and
   9388             // insert an anytype parameter.
   9389             try block.params.append(sema.gpa, .{
   9390                 .ty = Type.generic_poison,
   9391                 .is_comptime = comptime_syntax,
   9392                 .name = param_name,
   9393             });
   9394             sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison);
   9395             return;
   9396         },
   9397         else => |e| return e,
   9398     } or comptime_syntax;
   9399     if (sema.inst_map.get(inst)) |arg| {
   9400         if (is_comptime and sema.preallocated_new_func != .none) {
   9401             // We have a comptime value for this parameter so it should be elided from the
   9402             // function type of the function instruction in this block.
   9403             const coerced_arg = sema.coerce(block, param_ty, arg, .unneeded) catch |err| switch (err) {
   9404                 error.NeededSourceLocation => {
   9405                     // We are instantiating a generic function and a comptime arg
   9406                     // cannot be coerced to the param type, but since we don't
   9407                     // have the callee source location return `GenericPoison`
   9408                     // so that the instantiation is failed and the coercion
   9409                     // is handled by comptime call logic instead.
   9410                     assert(sema.is_generic_instantiation);
   9411                     return error.GenericPoison;
   9412                 },
   9413                 else => return err,
   9414             };
   9415             sema.inst_map.putAssumeCapacity(inst, coerced_arg);
   9416             return;
   9417         }
   9418         // Even though a comptime argument is provided, the generic function wants to treat
   9419         // this as a runtime parameter.
   9420         assert(sema.inst_map.remove(inst));
   9421     }
   9422 
   9423     if (sema.preallocated_new_func != .none) {
   9424         if (try sema.typeHasOnePossibleValue(param_ty)) |opv| {
   9425             // In this case we are instantiating a generic function call with a non-comptime
   9426             // non-anytype parameter that ended up being a one-possible-type.
   9427             // We don't want the parameter to be part of the instantiated function type.
   9428             const result = try sema.addConstant(opv);
   9429             sema.inst_map.putAssumeCapacity(inst, result);
   9430             return;
   9431         }
   9432     }
   9433 
   9434     try block.params.append(sema.gpa, .{
   9435         .ty = param_ty,
   9436         .is_comptime = comptime_syntax,
   9437         .name = param_name,
   9438     });
   9439 
   9440     if (is_comptime) {
   9441         // If this is a comptime parameter we can add a constant generic_poison
   9442         // since this is also a generic parameter.
   9443         const result = try sema.addConstant(Value.generic_poison);
   9444         sema.inst_map.putAssumeCapacityNoClobber(inst, result);
   9445     } else {
   9446         // Otherwise we need a dummy runtime instruction.
   9447         const result_index = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
   9448         try sema.air_instructions.append(sema.gpa, .{
   9449             .tag = .alloc,
   9450             .data = .{ .ty = param_ty },
   9451         });
   9452         const result = Air.indexToRef(result_index);
   9453         sema.inst_map.putAssumeCapacityNoClobber(inst, result);
   9454     }
   9455 }
   9456 
   9457 fn zirParamAnytype(
   9458     sema: *Sema,
   9459     block: *Block,
   9460     inst: Zir.Inst.Index,
   9461     comptime_syntax: bool,
   9462 ) CompileError!void {
   9463     const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
   9464     const param_name = inst_data.get(sema.code);
   9465 
   9466     if (sema.inst_map.get(inst)) |air_ref| {
   9467         const param_ty = sema.typeOf(air_ref);
   9468         if (comptime_syntax or try sema.typeRequiresComptime(param_ty)) {
   9469             // We have a comptime value for this parameter so it should be elided from the
   9470             // function type of the function instruction in this block.
   9471             return;
   9472         }
   9473         if (null != try sema.typeHasOnePossibleValue(param_ty)) {
   9474             return;
   9475         }
   9476         // The map is already populated but we do need to add a runtime parameter.
   9477         try block.params.append(sema.gpa, .{
   9478             .ty = param_ty,
   9479             .is_comptime = false,
   9480             .name = param_name,
   9481         });
   9482         return;
   9483     }
   9484 
   9485     // We are evaluating a generic function without any comptime args provided.
   9486 
   9487     try block.params.append(sema.gpa, .{
   9488         .ty = Type.generic_poison,
   9489         .is_comptime = comptime_syntax,
   9490         .name = param_name,
   9491     });
   9492     sema.inst_map.putAssumeCapacity(inst, .generic_poison);
   9493 }
   9494 
   9495 fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9496     const tracy = trace(@src());
   9497     defer tracy.end();
   9498 
   9499     const bin_inst = sema.code.instructions.items(.data)[inst].bin;
   9500     return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs, false);
   9501 }
   9502 
   9503 fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9504     const tracy = trace(@src());
   9505     defer tracy.end();
   9506 
   9507     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   9508     const src = inst_data.src();
   9509     const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
   9510     sema.src = src;
   9511     return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false);
   9512 }
   9513 
   9514 fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9515     const tracy = trace(@src());
   9516     defer tracy.end();
   9517 
   9518     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   9519     const src = inst_data.src();
   9520     const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
   9521     return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true);
   9522 }
   9523 
   9524 fn analyzeAs(
   9525     sema: *Sema,
   9526     block: *Block,
   9527     src: LazySrcLoc,
   9528     zir_dest_type: Zir.Inst.Ref,
   9529     zir_operand: Zir.Inst.Ref,
   9530     no_cast_to_comptime_int: bool,
   9531 ) CompileError!Air.Inst.Ref {
   9532     const mod = sema.mod;
   9533     const operand = try sema.resolveInst(zir_operand);
   9534     if (zir_dest_type == .var_args_param_type) return operand;
   9535     const dest_ty = sema.resolveType(block, src, zir_dest_type) catch |err| switch (err) {
   9536         error.GenericPoison => return operand,
   9537         else => |e| return e,
   9538     };
   9539     if (dest_ty.zigTypeTag(mod) == .NoReturn) {
   9540         return sema.fail(block, src, "cannot cast to noreturn", .{});
   9541     }
   9542     const is_ret = if (Zir.refToIndex(zir_dest_type)) |ptr_index|
   9543         sema.code.instructions.items(.tag)[ptr_index] == .ret_type
   9544     else
   9545         false;
   9546     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) {
   9547         error.NotCoercible => unreachable,
   9548         else => |e| return e,
   9549     };
   9550 }
   9551 
   9552 fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9553     const tracy = trace(@src());
   9554     defer tracy.end();
   9555 
   9556     const mod = sema.mod;
   9557     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
   9558     const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   9559     const ptr = try sema.resolveInst(inst_data.operand);
   9560     const ptr_ty = sema.typeOf(ptr);
   9561     if (!ptr_ty.isPtrAtRuntime(mod)) {
   9562         return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(mod)});
   9563     }
   9564     if (try sema.resolveMaybeUndefValIntable(ptr)) |ptr_val| {
   9565         return sema.addConstant(
   9566             try mod.intValue(Type.usize, (try ptr_val.getUnsignedIntAdvanced(mod, sema)).?),
   9567         );
   9568     }
   9569     try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);
   9570     return block.addUnOp(.int_from_ptr, ptr);
   9571 }
   9572 
   9573 fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9574     const tracy = trace(@src());
   9575     defer tracy.end();
   9576 
   9577     const mod = sema.mod;
   9578     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   9579     const src = inst_data.src();
   9580     const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
   9581     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   9582     const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start));
   9583     const object = try sema.resolveInst(extra.lhs);
   9584     return sema.fieldVal(block, src, object, field_name, field_name_src);
   9585 }
   9586 
   9587 fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index, initializing: bool) CompileError!Air.Inst.Ref {
   9588     const tracy = trace(@src());
   9589     defer tracy.end();
   9590 
   9591     const mod = sema.mod;
   9592     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   9593     const src = inst_data.src();
   9594     const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
   9595     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   9596     const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start));
   9597     const object_ptr = try sema.resolveInst(extra.lhs);
   9598     return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, initializing);
   9599 }
   9600 
   9601 fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9602     const tracy = trace(@src());
   9603     defer tracy.end();
   9604 
   9605     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   9606     const src = inst_data.src();
   9607     const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
   9608     const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
   9609     const object = try sema.resolveInst(extra.lhs);
   9610     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, "field name must be comptime-known");
   9611     return sema.fieldVal(block, src, object, field_name, field_name_src);
   9612 }
   9613 
   9614 fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9615     const tracy = trace(@src());
   9616     defer tracy.end();
   9617 
   9618     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   9619     const src = inst_data.src();
   9620     const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
   9621     const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
   9622     const object_ptr = try sema.resolveInst(extra.lhs);
   9623     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, "field name must be comptime-known");
   9624     return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
   9625 }
   9626 
   9627 fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9628     const tracy = trace(@src());
   9629     defer tracy.end();
   9630 
   9631     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   9632     const src = inst_data.src();
   9633     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   9634     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   9635 
   9636     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast");
   9637     const operand = try sema.resolveInst(extra.rhs);
   9638 
   9639     return sema.intCast(block, inst_data.src(), dest_ty, src, operand, operand_src, true);
   9640 }
   9641 
   9642 fn intCast(
   9643     sema: *Sema,
   9644     block: *Block,
   9645     src: LazySrcLoc,
   9646     dest_ty: Type,
   9647     dest_ty_src: LazySrcLoc,
   9648     operand: Air.Inst.Ref,
   9649     operand_src: LazySrcLoc,
   9650     runtime_safety: bool,
   9651 ) CompileError!Air.Inst.Ref {
   9652     const mod = sema.mod;
   9653     const operand_ty = sema.typeOf(operand);
   9654     const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src);
   9655     const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
   9656 
   9657     if (try sema.isComptimeKnown(operand)) {
   9658         return sema.coerce(block, dest_ty, operand, operand_src);
   9659     } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
   9660         return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});
   9661     }
   9662 
   9663     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src);
   9664     const is_vector = dest_ty.zigTypeTag(mod) == .Vector;
   9665 
   9666     if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| {
   9667         // requirement: intCast(u0, input) iff input == 0
   9668         if (runtime_safety and block.wantSafety()) {
   9669             try sema.requireRuntimeBlock(block, src, operand_src);
   9670             const wanted_info = dest_scalar_ty.intInfo(mod);
   9671             const wanted_bits = wanted_info.bits;
   9672 
   9673             if (wanted_bits == 0) {
   9674                 const ok = if (is_vector) ok: {
   9675                     const zeros = try sema.splat(operand_ty, try mod.intValue(operand_scalar_ty, 0));
   9676                     const zero_inst = try sema.addConstant(zeros);
   9677                     const is_in_range = try block.addCmpVector(operand, zero_inst, .eq);
   9678                     const all_in_range = try block.addInst(.{
   9679                         .tag = .reduce,
   9680                         .data = .{ .reduce = .{ .operand = is_in_range, .operation = .And } },
   9681                     });
   9682                     break :ok all_in_range;
   9683                 } else ok: {
   9684                     const zero_inst = try sema.addConstant(try mod.intValue(operand_ty, 0));
   9685                     const is_in_range = try block.addBinOp(.cmp_lte, operand, zero_inst);
   9686                     break :ok is_in_range;
   9687                 };
   9688                 try sema.addSafetyCheck(block, ok, .cast_truncated_data);
   9689             }
   9690         }
   9691 
   9692         return sema.addConstant(opv);
   9693     }
   9694 
   9695     try sema.requireRuntimeBlock(block, src, operand_src);
   9696     if (runtime_safety and block.wantSafety()) {
   9697         const actual_info = operand_scalar_ty.intInfo(mod);
   9698         const wanted_info = dest_scalar_ty.intInfo(mod);
   9699         const actual_bits = actual_info.bits;
   9700         const wanted_bits = wanted_info.bits;
   9701         const actual_value_bits = actual_bits - @intFromBool(actual_info.signedness == .signed);
   9702         const wanted_value_bits = wanted_bits - @intFromBool(wanted_info.signedness == .signed);
   9703 
   9704         // range shrinkage
   9705         // requirement: int value fits into target type
   9706         if (wanted_value_bits < actual_value_bits) {
   9707             const dest_max_val_scalar = try dest_scalar_ty.maxIntScalar(mod, operand_scalar_ty);
   9708             const dest_max_val = try sema.splat(operand_ty, dest_max_val_scalar);
   9709             const dest_max = try sema.addConstant(dest_max_val);
   9710             const diff = try block.addBinOp(.sub_wrap, dest_max, operand);
   9711 
   9712             if (actual_info.signedness == .signed) {
   9713                 // Reinterpret the sign-bit as part of the value. This will make
   9714                 // negative differences (`operand` > `dest_max`) appear too big.
   9715                 const unsigned_operand_ty = try mod.intType(.unsigned, actual_bits);
   9716                 const diff_unsigned = try block.addBitCast(unsigned_operand_ty, diff);
   9717 
   9718                 // If the destination type is signed, then we need to double its
   9719                 // range to account for negative values.
   9720                 const dest_range_val = if (wanted_info.signedness == .signed) range_val: {
   9721                     const one = try mod.intValue(unsigned_operand_ty, 1);
   9722                     const range_minus_one = try dest_max_val.shl(one, unsigned_operand_ty, sema.arena, mod);
   9723                     break :range_val try sema.intAdd(range_minus_one, one, unsigned_operand_ty, undefined);
   9724                 } else try mod.getCoerced(dest_max_val, unsigned_operand_ty);
   9725                 const dest_range = try sema.addConstant(dest_range_val);
   9726 
   9727                 const ok = if (is_vector) ok: {
   9728                     const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte);
   9729                     const all_in_range = try block.addInst(.{
   9730                         .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
   9731                         .data = .{ .reduce = .{
   9732                             .operand = is_in_range,
   9733                             .operation = .And,
   9734                         } },
   9735                     });
   9736                     break :ok all_in_range;
   9737                 } else ok: {
   9738                     const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range);
   9739                     break :ok is_in_range;
   9740                 };
   9741                 // TODO negative_to_unsigned?
   9742                 try sema.addSafetyCheck(block, ok, .cast_truncated_data);
   9743             } else {
   9744                 const ok = if (is_vector) ok: {
   9745                     const is_in_range = try block.addCmpVector(diff, dest_max, .lte);
   9746                     const all_in_range = try block.addInst(.{
   9747                         .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
   9748                         .data = .{ .reduce = .{
   9749                             .operand = is_in_range,
   9750                             .operation = .And,
   9751                         } },
   9752                     });
   9753                     break :ok all_in_range;
   9754                 } else ok: {
   9755                     const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max);
   9756                     break :ok is_in_range;
   9757                 };
   9758                 try sema.addSafetyCheck(block, ok, .cast_truncated_data);
   9759             }
   9760         } else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) {
   9761             // no shrinkage, yes sign loss
   9762             // requirement: signed to unsigned >= 0
   9763             const ok = if (is_vector) ok: {
   9764                 const scalar_zero = try mod.intValue(operand_scalar_ty, 0);
   9765                 const zero_val = try sema.splat(operand_ty, scalar_zero);
   9766                 const zero_inst = try sema.addConstant(zero_val);
   9767                 const is_in_range = try block.addCmpVector(operand, zero_inst, .gte);
   9768                 const all_in_range = try block.addInst(.{
   9769                     .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
   9770                     .data = .{ .reduce = .{
   9771                         .operand = is_in_range,
   9772                         .operation = .And,
   9773                     } },
   9774                 });
   9775                 break :ok all_in_range;
   9776             } else ok: {
   9777                 const zero_inst = try sema.addConstant(try mod.intValue(operand_ty, 0));
   9778                 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);
   9779                 break :ok is_in_range;
   9780             };
   9781             try sema.addSafetyCheck(block, ok, .negative_to_unsigned);
   9782         }
   9783     }
   9784     return block.addTyOp(.intcast, dest_ty, operand);
   9785 }
   9786 
   9787 fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9788     const tracy = trace(@src());
   9789     defer tracy.end();
   9790 
   9791     const mod = sema.mod;
   9792     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   9793     const src = inst_data.src();
   9794     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   9795     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   9796 
   9797     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@bitCast");
   9798     const operand = try sema.resolveInst(extra.rhs);
   9799     const operand_ty = sema.typeOf(operand);
   9800     switch (dest_ty.zigTypeTag(mod)) {
   9801         .AnyFrame,
   9802         .ComptimeFloat,
   9803         .ComptimeInt,
   9804         .EnumLiteral,
   9805         .ErrorSet,
   9806         .ErrorUnion,
   9807         .Fn,
   9808         .Frame,
   9809         .NoReturn,
   9810         .Null,
   9811         .Opaque,
   9812         .Optional,
   9813         .Type,
   9814         .Undefined,
   9815         .Void,
   9816         => return sema.fail(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)}),
   9817 
   9818         .Enum => {
   9819             const msg = msg: {
   9820                 const msg = try sema.errMsg(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)});
   9821                 errdefer msg.destroy(sema.gpa);
   9822                 switch (operand_ty.zigTypeTag(mod)) {
   9823                     .Int, .ComptimeInt => try sema.errNote(block, src, msg, "use @enumFromInt to cast from '{}'", .{operand_ty.fmt(mod)}),
   9824                     else => {},
   9825                 }
   9826 
   9827                 break :msg msg;
   9828             };
   9829             return sema.failWithOwnedErrorMsg(msg);
   9830         },
   9831 
   9832         .Pointer => {
   9833             const msg = msg: {
   9834                 const msg = try sema.errMsg(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)});
   9835                 errdefer msg.destroy(sema.gpa);
   9836                 switch (operand_ty.zigTypeTag(mod)) {
   9837                     .Int, .ComptimeInt => try sema.errNote(block, src, msg, "use @ptrFromInt to cast from '{}'", .{operand_ty.fmt(mod)}),
   9838                     .Pointer => try sema.errNote(block, src, msg, "use @ptrCast to cast from '{}'", .{operand_ty.fmt(mod)}),
   9839                     else => {},
   9840                 }
   9841 
   9842                 break :msg msg;
   9843             };
   9844             return sema.failWithOwnedErrorMsg(msg);
   9845         },
   9846         .Struct, .Union => if (dest_ty.containerLayout(mod) == .Auto) {
   9847             const container = switch (dest_ty.zigTypeTag(mod)) {
   9848                 .Struct => "struct",
   9849                 .Union => "union",
   9850                 else => unreachable,
   9851             };
   9852             return sema.fail(block, src, "cannot @bitCast to '{}'; {s} does not have a guaranteed in-memory layout", .{
   9853                 dest_ty.fmt(mod), container,
   9854             });
   9855         },
   9856 
   9857         .Array,
   9858         .Bool,
   9859         .Float,
   9860         .Int,
   9861         .Vector,
   9862         => {},
   9863     }
   9864     switch (operand_ty.zigTypeTag(mod)) {
   9865         .AnyFrame,
   9866         .ComptimeFloat,
   9867         .ComptimeInt,
   9868         .EnumLiteral,
   9869         .ErrorSet,
   9870         .ErrorUnion,
   9871         .Fn,
   9872         .Frame,
   9873         .NoReturn,
   9874         .Null,
   9875         .Opaque,
   9876         .Optional,
   9877         .Type,
   9878         .Undefined,
   9879         .Void,
   9880         => return sema.fail(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)}),
   9881 
   9882         .Enum => {
   9883             const msg = msg: {
   9884                 const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)});
   9885                 errdefer msg.destroy(sema.gpa);
   9886                 switch (dest_ty.zigTypeTag(mod)) {
   9887                     .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @intFromEnum to cast to '{}'", .{dest_ty.fmt(mod)}),
   9888                     else => {},
   9889                 }
   9890 
   9891                 break :msg msg;
   9892             };
   9893             return sema.failWithOwnedErrorMsg(msg);
   9894         },
   9895         .Pointer => {
   9896             const msg = msg: {
   9897                 const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)});
   9898                 errdefer msg.destroy(sema.gpa);
   9899                 switch (dest_ty.zigTypeTag(mod)) {
   9900                     .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @intFromPtr to cast to '{}'", .{dest_ty.fmt(mod)}),
   9901                     .Pointer => try sema.errNote(block, operand_src, msg, "use @ptrCast to cast to '{}'", .{dest_ty.fmt(mod)}),
   9902                     else => {},
   9903                 }
   9904 
   9905                 break :msg msg;
   9906             };
   9907             return sema.failWithOwnedErrorMsg(msg);
   9908         },
   9909         .Struct, .Union => if (operand_ty.containerLayout(mod) == .Auto) {
   9910             const container = switch (operand_ty.zigTypeTag(mod)) {
   9911                 .Struct => "struct",
   9912                 .Union => "union",
   9913                 else => unreachable,
   9914             };
   9915             return sema.fail(block, operand_src, "cannot @bitCast from '{}'; {s} does not have a guaranteed in-memory layout", .{
   9916                 operand_ty.fmt(mod), container,
   9917             });
   9918         },
   9919 
   9920         .Array,
   9921         .Bool,
   9922         .Float,
   9923         .Int,
   9924         .Vector,
   9925         => {},
   9926     }
   9927     return sema.bitCast(block, dest_ty, operand, inst_data.src(), operand_src);
   9928 }
   9929 
   9930 fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9931     const tracy = trace(@src());
   9932     defer tracy.end();
   9933 
   9934     const mod = sema.mod;
   9935     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   9936     const src = inst_data.src();
   9937     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
   9938     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   9939 
   9940     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast");
   9941     const operand = try sema.resolveInst(extra.rhs);
   9942 
   9943     const target = mod.getTarget();
   9944     const dest_is_comptime_float = switch (dest_ty.zigTypeTag(mod)) {
   9945         .ComptimeFloat => true,
   9946         .Float => false,
   9947         else => return sema.fail(
   9948             block,
   9949             src,
   9950             "expected float type, found '{}'",
   9951             .{dest_ty.fmt(mod)},
   9952         ),
   9953     };
   9954 
   9955     const operand_ty = sema.typeOf(operand);
   9956     switch (operand_ty.zigTypeTag(mod)) {
   9957         .ComptimeFloat, .Float, .ComptimeInt => {},
   9958         else => return sema.fail(
   9959             block,
   9960             operand_src,
   9961             "expected float type, found '{}'",
   9962             .{operand_ty.fmt(mod)},
   9963         ),
   9964     }
   9965 
   9966     if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
   9967         return sema.addConstant(try operand_val.floatCast(dest_ty, mod));
   9968     }
   9969     if (dest_is_comptime_float) {
   9970         return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{});
   9971     }
   9972     const src_bits = operand_ty.floatBits(target);
   9973     const dst_bits = dest_ty.floatBits(target);
   9974     if (dst_bits >= src_bits) {
   9975         return sema.coerce(block, dest_ty, operand, operand_src);
   9976     }
   9977     try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
   9978     return block.addTyOp(.fptrunc, dest_ty, operand);
   9979 }
   9980 
   9981 fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9982     const tracy = trace(@src());
   9983     defer tracy.end();
   9984 
   9985     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   9986     const src = inst_data.src();
   9987     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   9988     const array = try sema.resolveInst(extra.lhs);
   9989     const elem_index = try sema.resolveInst(extra.rhs);
   9990     return sema.elemVal(block, src, array, elem_index, src, false);
   9991 }
   9992 
   9993 fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9994     const tracy = trace(@src());
   9995     defer tracy.end();
   9996 
   9997     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
   9998     const src = inst_data.src();
   9999     const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
  10000     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10001     const array = try sema.resolveInst(extra.lhs);
  10002     const elem_index = try sema.resolveInst(extra.rhs);
  10003     return sema.elemVal(block, src, array, elem_index, elem_index_src, true);
  10004 }
  10005 
  10006 fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10007     const tracy = trace(@src());
  10008     defer tracy.end();
  10009 
  10010     const mod = sema.mod;
  10011     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  10012     const src = inst_data.src();
  10013     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10014     const array_ptr = try sema.resolveInst(extra.lhs);
  10015     const elem_index = try sema.resolveInst(extra.rhs);
  10016     const indexable_ty = sema.typeOf(array_ptr);
  10017     if (indexable_ty.zigTypeTag(mod) != .Pointer) {
  10018         const capture_src: LazySrcLoc = .{ .for_capture_from_input = inst_data.src_node };
  10019         const msg = msg: {
  10020             const msg = try sema.errMsg(block, capture_src, "pointer capture of non pointer type '{}'", .{
  10021                 indexable_ty.fmt(mod),
  10022             });
  10023             errdefer msg.destroy(sema.gpa);
  10024             if (indexable_ty.isIndexable(mod)) {
  10025                 try sema.errNote(block, src, msg, "consider using '&' here", .{});
  10026             }
  10027             break :msg msg;
  10028         };
  10029         return sema.failWithOwnedErrorMsg(msg);
  10030     }
  10031     return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false);
  10032 }
  10033 
  10034 fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10035     const tracy = trace(@src());
  10036     defer tracy.end();
  10037 
  10038     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  10039     const src = inst_data.src();
  10040     const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
  10041     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10042     const array_ptr = try sema.resolveInst(extra.lhs);
  10043     const elem_index = try sema.resolveInst(extra.rhs);
  10044     return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true);
  10045 }
  10046 
  10047 fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10048     const tracy = trace(@src());
  10049     defer tracy.end();
  10050 
  10051     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  10052     const src = inst_data.src();
  10053     const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
  10054     const array_ptr = try sema.resolveInst(extra.ptr);
  10055     const elem_index = try sema.addIntUnsigned(Type.usize, extra.index);
  10056     return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true);
  10057 }
  10058 
  10059 fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10060     const tracy = trace(@src());
  10061     defer tracy.end();
  10062 
  10063     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  10064     const src = inst_data.src();
  10065     const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
  10066     const array_ptr = try sema.resolveInst(extra.lhs);
  10067     const start = try sema.resolveInst(extra.start);
  10068     const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
  10069     const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
  10070     const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
  10071 
  10072     return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src, false);
  10073 }
  10074 
  10075 fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10076     const tracy = trace(@src());
  10077     defer tracy.end();
  10078 
  10079     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  10080     const src = inst_data.src();
  10081     const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;
  10082     const array_ptr = try sema.resolveInst(extra.lhs);
  10083     const start = try sema.resolveInst(extra.start);
  10084     const end = try sema.resolveInst(extra.end);
  10085     const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
  10086     const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
  10087     const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
  10088 
  10089     return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src, false);
  10090 }
  10091 
  10092 fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10093     const tracy = trace(@src());
  10094     defer tracy.end();
  10095 
  10096     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  10097     const src = inst_data.src();
  10098     const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node };
  10099     const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;
  10100     const array_ptr = try sema.resolveInst(extra.lhs);
  10101     const start = try sema.resolveInst(extra.start);
  10102     const end: Air.Inst.Ref = if (extra.end == .none) .none else try sema.resolveInst(extra.end);
  10103     const sentinel = try sema.resolveInst(extra.sentinel);
  10104     const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
  10105     const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
  10106     const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
  10107 
  10108     return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false);
  10109 }
  10110 
  10111 fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10112     const tracy = trace(@src());
  10113     defer tracy.end();
  10114 
  10115     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  10116     const src = inst_data.src();
  10117     const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
  10118     const array_ptr = try sema.resolveInst(extra.lhs);
  10119     const start = try sema.resolveInst(extra.start);
  10120     const len = try sema.resolveInst(extra.len);
  10121     const sentinel = if (extra.sentinel == .none) .none else try sema.resolveInst(extra.sentinel);
  10122     const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
  10123     const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset };
  10124     const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
  10125     const sentinel_src: LazySrcLoc = if (sentinel == .none)
  10126         .unneeded
  10127     else
  10128         .{ .node_offset_slice_sentinel = inst_data.src_node };
  10129 
  10130     return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
  10131 }
  10132 
  10133 /// Holds common data used when analyzing or resolving switch prong bodies,
  10134 /// including setting up captures.
  10135 const SwitchProngAnalysis = struct {
  10136     sema: *Sema,
  10137     /// The block containing the `switch_block` itself.
  10138     parent_block: *Block,
  10139     /// The raw switch operand value (*not* the condition). Always defined.
  10140     operand: Air.Inst.Ref,
  10141     /// May be `undefined` if no prong has a by-ref capture.
  10142     operand_ptr: Air.Inst.Ref,
  10143     /// The switch condition value. For unions, `operand` is the union and `cond` is its tag.
  10144     cond: Air.Inst.Ref,
  10145     /// If this switch is on an error set, this is the type to assign to the
  10146     /// `else` prong. If `null`, the prong should be unreachable.
  10147     else_error_ty: ?Type,
  10148     /// The index of the `switch_block` instruction itself.
  10149     switch_block_inst: Zir.Inst.Index,
  10150     /// The dummy index into which inline tag captures should be placed. May be
  10151     /// undefined if no prong has a tag capture.
  10152     tag_capture_inst: Zir.Inst.Index,
  10153 
  10154     /// Resolve a switch prong which is determined at comptime to have no peers.
  10155     /// Uses `resolveBlockBody`. Sets up captures as needed.
  10156     fn resolveProngComptime(
  10157         spa: SwitchProngAnalysis,
  10158         child_block: *Block,
  10159         prong_type: enum { normal, special },
  10160         prong_body: []const Zir.Inst.Index,
  10161         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  10162         /// Must use the `scalar_capture`, `special_capture`, or `multi_capture` union field.
  10163         raw_capture_src: Module.SwitchProngSrc,
  10164         /// The set of all values which can reach this prong. May be undefined
  10165         /// if the prong is special or contains ranges.
  10166         case_vals: []const Air.Inst.Ref,
  10167         /// The inline capture of this prong. If this is not an inline prong,
  10168         /// this is `.none`.
  10169         inline_case_capture: Air.Inst.Ref,
  10170         /// Whether this prong has an inline tag capture. If `true`, then
  10171         /// `inline_case_capture` cannot be `.none`.
  10172         has_tag_capture: bool,
  10173         merges: *Block.Merges,
  10174     ) CompileError!Air.Inst.Ref {
  10175         const sema = spa.sema;
  10176         const src = sema.code.instructions.items(.data)[spa.switch_block_inst].pl_node.src();
  10177 
  10178         if (has_tag_capture) {
  10179             const tag_ref = try spa.analyzeTagCapture(child_block, raw_capture_src, inline_case_capture);
  10180             sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
  10181         }
  10182         defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
  10183 
  10184         switch (capture) {
  10185             .none => {
  10186                 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
  10187             },
  10188 
  10189             .by_val, .by_ref => {
  10190                 const capture_ref = try spa.analyzeCapture(
  10191                     child_block,
  10192                     capture == .by_ref,
  10193                     prong_type == .special,
  10194                     raw_capture_src,
  10195                     case_vals,
  10196                     inline_case_capture,
  10197                 );
  10198 
  10199                 if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) {
  10200                     // This prong should be unreachable!
  10201                     return Air.Inst.Ref.unreachable_value;
  10202                 }
  10203 
  10204                 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
  10205                 defer assert(sema.inst_map.remove(spa.switch_block_inst));
  10206 
  10207                 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
  10208             },
  10209         }
  10210     }
  10211 
  10212     /// Analyze a switch prong which may have peers at runtime.
  10213     /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed.
  10214     fn analyzeProngRuntime(
  10215         spa: SwitchProngAnalysis,
  10216         case_block: *Block,
  10217         prong_type: enum { normal, special },
  10218         prong_body: []const Zir.Inst.Index,
  10219         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  10220         /// Must use the `scalar`, `special`, or `multi_capture` union field.
  10221         raw_capture_src: Module.SwitchProngSrc,
  10222         /// The set of all values which can reach this prong. May be undefined
  10223         /// if the prong is special or contains ranges.
  10224         case_vals: []const Air.Inst.Ref,
  10225         /// The inline capture of this prong. If this is not an inline prong,
  10226         /// this is `.none`.
  10227         inline_case_capture: Air.Inst.Ref,
  10228         /// Whether this prong has an inline tag capture. If `true`, then
  10229         /// `inline_case_capture` cannot be `.none`.
  10230         has_tag_capture: bool,
  10231     ) CompileError!void {
  10232         const sema = spa.sema;
  10233 
  10234         if (has_tag_capture) {
  10235             const tag_ref = try spa.analyzeTagCapture(case_block, raw_capture_src, inline_case_capture);
  10236             sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
  10237         }
  10238         defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
  10239 
  10240         switch (capture) {
  10241             .none => {
  10242                 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
  10243             },
  10244 
  10245             .by_val, .by_ref => {
  10246                 const capture_ref = try spa.analyzeCapture(
  10247                     case_block,
  10248                     capture == .by_ref,
  10249                     prong_type == .special,
  10250                     raw_capture_src,
  10251                     case_vals,
  10252                     inline_case_capture,
  10253                 );
  10254 
  10255                 if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) {
  10256                     // No need to analyze any further, the prong is unreachable
  10257                     return;
  10258                 }
  10259 
  10260                 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
  10261                 defer assert(sema.inst_map.remove(spa.switch_block_inst));
  10262 
  10263                 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
  10264             },
  10265         }
  10266     }
  10267 
  10268     fn analyzeTagCapture(
  10269         spa: SwitchProngAnalysis,
  10270         block: *Block,
  10271         raw_capture_src: Module.SwitchProngSrc,
  10272         inline_case_capture: Air.Inst.Ref,
  10273     ) CompileError!Air.Inst.Ref {
  10274         const sema = spa.sema;
  10275         const mod = sema.mod;
  10276         const operand_ty = sema.typeOf(spa.operand);
  10277         if (operand_ty.zigTypeTag(mod) != .Union) {
  10278             const zir_datas = sema.code.instructions.items(.data);
  10279             const switch_node_offset = zir_datas[spa.switch_block_inst].pl_node.src_node;
  10280             const raw_tag_capture_src: Module.SwitchProngSrc = switch (raw_capture_src) {
  10281                 .scalar_capture => |i| .{ .scalar_tag_capture = i },
  10282                 .multi_capture => |i| .{ .multi_tag_capture = i },
  10283                 .special_capture => .special_tag_capture,
  10284                 else => unreachable,
  10285             };
  10286             const capture_src = raw_tag_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none);
  10287             const msg = msg: {
  10288                 const msg = try sema.errMsg(block, capture_src, "cannot capture tag of non-union type '{}'", .{
  10289                     operand_ty.fmt(mod),
  10290                 });
  10291                 errdefer msg.destroy(sema.gpa);
  10292                 try sema.addDeclaredHereNote(msg, operand_ty);
  10293                 break :msg msg;
  10294             };
  10295             return sema.failWithOwnedErrorMsg(msg);
  10296         }
  10297         assert(inline_case_capture != .none);
  10298         return inline_case_capture;
  10299     }
  10300 
  10301     fn analyzeCapture(
  10302         spa: SwitchProngAnalysis,
  10303         block: *Block,
  10304         capture_byref: bool,
  10305         is_special_prong: bool,
  10306         raw_capture_src: Module.SwitchProngSrc,
  10307         case_vals: []const Air.Inst.Ref,
  10308         inline_case_capture: Air.Inst.Ref,
  10309     ) CompileError!Air.Inst.Ref {
  10310         const sema = spa.sema;
  10311         const mod = sema.mod;
  10312 
  10313         const zir_datas = sema.code.instructions.items(.data);
  10314         const switch_node_offset = zir_datas[spa.switch_block_inst].pl_node.src_node;
  10315 
  10316         const operand_ty = sema.typeOf(spa.operand);
  10317         const operand_ptr_ty = if (capture_byref) sema.typeOf(spa.operand_ptr) else undefined;
  10318         const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_node_offset };
  10319 
  10320         if (inline_case_capture != .none) {
  10321             const item_val = sema.resolveConstValue(block, .unneeded, inline_case_capture, "") catch unreachable;
  10322             if (operand_ty.zigTypeTag(mod) == .Union) {
  10323                 const field_index = @as(u32, @intCast(operand_ty.unionTagFieldIndex(item_val, mod).?));
  10324                 const union_obj = mod.typeToUnion(operand_ty).?;
  10325                 const field_ty = union_obj.fields.values()[field_index].ty;
  10326                 if (capture_byref) {
  10327                     const ptr_field_ty = try mod.ptrType(.{
  10328                         .child = field_ty.toIntern(),
  10329                         .flags = .{
  10330                             .is_const = !operand_ptr_ty.ptrIsMutable(mod),
  10331                             .is_volatile = operand_ptr_ty.isVolatilePtr(mod),
  10332                             .address_space = operand_ptr_ty.ptrAddressSpace(mod),
  10333                         },
  10334                     });
  10335                     if (try sema.resolveDefinedValue(block, sema.src, spa.operand_ptr)) |union_ptr| {
  10336                         return sema.addConstant(
  10337                             (try mod.intern(.{ .ptr = .{
  10338                                 .ty = ptr_field_ty.toIntern(),
  10339                                 .addr = .{ .field = .{
  10340                                     .base = union_ptr.toIntern(),
  10341                                     .index = field_index,
  10342                                 } },
  10343                             } })).toValue(),
  10344                         );
  10345                     }
  10346                     return block.addStructFieldPtr(spa.operand_ptr, field_index, ptr_field_ty);
  10347                 } else {
  10348                     if (try sema.resolveDefinedValue(block, sema.src, spa.operand)) |union_val| {
  10349                         const tag_and_val = mod.intern_pool.indexToKey(union_val.toIntern()).un;
  10350                         return sema.addConstant(tag_and_val.val.toValue());
  10351                     }
  10352                     return block.addStructFieldVal(spa.operand, field_index, field_ty);
  10353                 }
  10354             } else if (capture_byref) {
  10355                 return sema.addConstantMaybeRef(block, operand_ty, item_val, true);
  10356             } else {
  10357                 return inline_case_capture;
  10358             }
  10359         }
  10360 
  10361         if (is_special_prong) {
  10362             if (capture_byref) {
  10363                 return spa.operand_ptr;
  10364             }
  10365 
  10366             switch (operand_ty.zigTypeTag(mod)) {
  10367                 .ErrorSet => if (spa.else_error_ty) |ty| {
  10368                     return sema.bitCast(block, ty, spa.operand, operand_src, null);
  10369                 } else {
  10370                     try block.addUnreachable(false);
  10371                     return Air.Inst.Ref.unreachable_value;
  10372                 },
  10373                 else => return spa.operand,
  10374             }
  10375         }
  10376 
  10377         switch (operand_ty.zigTypeTag(mod)) {
  10378             .Union => {
  10379                 const union_obj = mod.typeToUnion(operand_ty).?;
  10380                 const first_item_val = sema.resolveConstValue(block, .unneeded, case_vals[0], "") catch unreachable;
  10381 
  10382                 const first_field_index = @as(u32, @intCast(operand_ty.unionTagFieldIndex(first_item_val, mod).?));
  10383                 const first_field = union_obj.fields.values()[first_field_index];
  10384 
  10385                 const field_tys = try sema.arena.alloc(Type, case_vals.len);
  10386                 for (case_vals, field_tys) |item, *field_ty| {
  10387                     const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable;
  10388                     const field_idx = @as(u32, @intCast(operand_ty.unionTagFieldIndex(item_val, sema.mod).?));
  10389                     field_ty.* = union_obj.fields.values()[field_idx].ty;
  10390                 }
  10391 
  10392                 // Fast path: if all the operands are the same type already, we don't need to hit
  10393                 // PTR! This will also allow us to emit simpler code.
  10394                 const same_types = for (field_tys[1..]) |field_ty| {
  10395                     if (!field_ty.eql(field_tys[0], sema.mod)) break false;
  10396                 } else true;
  10397 
  10398                 const capture_ty = if (same_types) field_tys[0] else capture_ty: {
  10399                     // We need values to run PTR on, so make a bunch of undef constants.
  10400                     const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
  10401                     for (dummy_captures, field_tys) |*dummy, field_ty| {
  10402                         dummy.* = try sema.addConstUndef(field_ty);
  10403                     }
  10404 
  10405                     const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
  10406                     @memset(case_srcs, .unneeded);
  10407 
  10408                     break :capture_ty sema.resolvePeerTypes(block, .unneeded, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
  10409                         error.NeededSourceLocation => {
  10410                             // This must be a multi-prong so this must be a `multi_capture` src
  10411                             const multi_idx = raw_capture_src.multi_capture;
  10412                             const src_decl_ptr = sema.mod.declPtr(block.src_decl);
  10413                             for (case_srcs, 0..) |*case_src, i| {
  10414                                 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @as(u32, @intCast(i)) } };
  10415                                 case_src.* = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
  10416                             }
  10417                             const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
  10418                             _ = sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err1| switch (err1) {
  10419                                 error.AnalysisFail => {
  10420                                     const msg = sema.err orelse return error.AnalysisFail;
  10421                                     try sema.reparentOwnedErrorMsg(block, capture_src, msg, "capture group with incompatible types", .{});
  10422                                     return error.AnalysisFail;
  10423                                 },
  10424                                 else => |e| return e,
  10425                             };
  10426                             unreachable;
  10427                         },
  10428                         else => |e| return e,
  10429                     };
  10430                 };
  10431 
  10432                 // By-reference captures have some further restrictions which make them easier to emit
  10433                 if (capture_byref) {
  10434                     const operand_ptr_info = operand_ptr_ty.ptrInfo(mod);
  10435                     const capture_ptr_ty = try mod.ptrType(.{
  10436                         .child = capture_ty.toIntern(),
  10437                         .flags = .{
  10438                             // TODO: alignment!
  10439                             .is_const = operand_ptr_info.flags.is_const,
  10440                             .is_volatile = operand_ptr_info.flags.is_volatile,
  10441                             .address_space = operand_ptr_info.flags.address_space,
  10442                         },
  10443                     });
  10444 
  10445                     // By-ref captures of hetereogeneous types are only allowed if each field
  10446                     // pointer type is in-memory coercible to the capture pointer type.
  10447                     if (!same_types) {
  10448                         for (field_tys, 0..) |field_ty, i| {
  10449                             const field_ptr_ty = try mod.ptrType(.{
  10450                                 .child = field_ty.toIntern(),
  10451                                 .flags = .{
  10452                                     // TODO: alignment!
  10453                                     .is_const = operand_ptr_info.flags.is_const,
  10454                                     .is_volatile = operand_ptr_info.flags.is_volatile,
  10455                                     .address_space = operand_ptr_info.flags.address_space,
  10456                                 },
  10457                             });
  10458                             if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
  10459                                 const multi_idx = raw_capture_src.multi_capture;
  10460                                 const src_decl_ptr = sema.mod.declPtr(block.src_decl);
  10461                                 const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
  10462                                 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @as(u32, @intCast(i)) } };
  10463                                 const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
  10464                                 const msg = msg: {
  10465                                     const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{});
  10466                                     errdefer msg.destroy(sema.gpa);
  10467                                     try sema.errNote(block, case_src, msg, "pointer type child '{}' cannot cast into resolved pointer type child '{}'", .{
  10468                                         field_ty.fmt(sema.mod),
  10469                                         capture_ty.fmt(sema.mod),
  10470                                     });
  10471                                     try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{});
  10472                                     break :msg msg;
  10473                                 };
  10474                                 return sema.failWithOwnedErrorMsg(msg);
  10475                             }
  10476                         }
  10477                     }
  10478 
  10479                     if (try sema.resolveDefinedValue(block, operand_src, spa.operand_ptr)) |op_ptr_val| {
  10480                         if (op_ptr_val.isUndef(mod)) return sema.addConstUndef(capture_ptr_ty);
  10481                         return sema.addConstant(
  10482                             (try mod.intern(.{ .ptr = .{
  10483                                 .ty = capture_ptr_ty.toIntern(),
  10484                                 .addr = .{ .field = .{
  10485                                     .base = op_ptr_val.toIntern(),
  10486                                     .index = first_field_index,
  10487                                 } },
  10488                             } })).toValue(),
  10489                         );
  10490                     }
  10491 
  10492                     try sema.requireRuntimeBlock(block, operand_src, null);
  10493                     return block.addStructFieldPtr(spa.operand_ptr, first_field_index, capture_ptr_ty);
  10494                 }
  10495 
  10496                 if (try sema.resolveDefinedValue(block, operand_src, spa.operand)) |operand_val| {
  10497                     if (operand_val.isUndef(mod)) return sema.addConstUndef(capture_ty);
  10498                     const union_val = mod.intern_pool.indexToKey(operand_val.toIntern()).un;
  10499                     if (union_val.tag.toValue().isUndef(mod)) return sema.addConstUndef(capture_ty);
  10500                     const uncoerced = try sema.addConstant(union_val.val.toValue());
  10501                     return sema.coerce(block, capture_ty, uncoerced, operand_src);
  10502                 }
  10503 
  10504                 try sema.requireRuntimeBlock(block, operand_src, null);
  10505 
  10506                 if (same_types) {
  10507                     return block.addStructFieldVal(spa.operand, first_field_index, capture_ty);
  10508                 }
  10509 
  10510                 // We may have to emit a switch block which coerces the operand to the capture type.
  10511                 // If we can, try to avoid that using in-memory coercions.
  10512                 const first_non_imc = in_mem: {
  10513                     for (field_tys, 0..) |field_ty, i| {
  10514                         if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
  10515                             break :in_mem i;
  10516                         }
  10517                     }
  10518                     // All fields are in-memory coercible to the resolved type!
  10519                     // Just take the first field and bitcast the result.
  10520                     const uncoerced = try block.addStructFieldVal(spa.operand, first_field_index, first_field.ty);
  10521                     return block.addBitCast(capture_ty, uncoerced);
  10522                 };
  10523 
  10524                 // By-val capture with heterogeneous types which are not all in-memory coercible to
  10525                 // the resolved capture type. We finally have to fall back to the ugly method.
  10526 
  10527                 // However, let's first track which operands are in-memory coercible. There may well
  10528                 // be several, and we can squash all of these cases into the same switch prong using
  10529                 // a simple bitcast. We'll make this the 'else' prong.
  10530 
  10531                 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_tys.len);
  10532                 in_mem_coercible.unset(first_non_imc);
  10533                 {
  10534                     const next = first_non_imc + 1;
  10535                     for (field_tys[next..], next..) |field_ty, i| {
  10536                         if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
  10537                             in_mem_coercible.unset(i);
  10538                         }
  10539                     }
  10540                 }
  10541 
  10542                 const capture_block_inst = try block.addInstAsIndex(.{
  10543                     .tag = .block,
  10544                     .data = .{
  10545                         .ty_pl = .{
  10546                             .ty = try sema.addType(capture_ty),
  10547                             .payload = undefined, // updated below
  10548                         },
  10549                     },
  10550                 });
  10551 
  10552                 const prong_count = field_tys.len - in_mem_coercible.count();
  10553 
  10554                 const estimated_extra = prong_count * 6; // 2 for Case, 1 item, probably 3 insts
  10555                 var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra);
  10556                 defer cases_extra.deinit();
  10557 
  10558                 {
  10559                     // Non-bitcast cases
  10560                     var it = in_mem_coercible.iterator(.{ .kind = .unset });
  10561                     while (it.next()) |idx| {
  10562                         var coerce_block = block.makeSubBlock();
  10563                         defer coerce_block.instructions.deinit(sema.gpa);
  10564 
  10565                         const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @as(u32, @intCast(idx)), field_tys[idx]);
  10566                         const coerced = sema.coerce(&coerce_block, capture_ty, uncoerced, .unneeded) catch |err| switch (err) {
  10567                             error.NeededSourceLocation => {
  10568                                 const multi_idx = raw_capture_src.multi_capture;
  10569                                 const src_decl_ptr = sema.mod.declPtr(block.src_decl);
  10570                                 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @as(u32, @intCast(idx)) } };
  10571                                 const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
  10572                                 _ = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);
  10573                                 unreachable;
  10574                             },
  10575                             else => |e| return e,
  10576                         };
  10577                         _ = try coerce_block.addBr(capture_block_inst, coerced);
  10578 
  10579                         try cases_extra.ensureUnusedCapacity(3 + coerce_block.instructions.items.len);
  10580                         cases_extra.appendAssumeCapacity(1); // items_len
  10581                         cases_extra.appendAssumeCapacity(@as(u32, @intCast(coerce_block.instructions.items.len))); // body_len
  10582                         cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item
  10583                         cases_extra.appendSliceAssumeCapacity(coerce_block.instructions.items); // body
  10584                     }
  10585                 }
  10586                 const else_body_len = len: {
  10587                     // 'else' prong uses a bitcast
  10588                     var coerce_block = block.makeSubBlock();
  10589                     defer coerce_block.instructions.deinit(sema.gpa);
  10590 
  10591                     const first_imc = in_mem_coercible.findFirstSet().?;
  10592                     const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @as(u32, @intCast(first_imc)), field_tys[first_imc]);
  10593                     const coerced = try coerce_block.addBitCast(capture_ty, uncoerced);
  10594                     _ = try coerce_block.addBr(capture_block_inst, coerced);
  10595 
  10596                     try cases_extra.appendSlice(coerce_block.instructions.items);
  10597                     break :len coerce_block.instructions.items.len;
  10598                 };
  10599 
  10600                 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).Struct.fields.len +
  10601                     cases_extra.items.len +
  10602                     @typeInfo(Air.Block).Struct.fields.len +
  10603                     1);
  10604 
  10605                 const switch_br_inst = @as(u32, @intCast(sema.air_instructions.len));
  10606                 try sema.air_instructions.append(sema.gpa, .{
  10607                     .tag = .switch_br,
  10608                     .data = .{ .pl_op = .{
  10609                         .operand = spa.cond,
  10610                         .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
  10611                             .cases_len = @as(u32, @intCast(prong_count)),
  10612                             .else_body_len = @as(u32, @intCast(else_body_len)),
  10613                         }),
  10614                     } },
  10615                 });
  10616                 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
  10617 
  10618                 // Set up block body
  10619                 sema.air_instructions.items(.data)[capture_block_inst].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{
  10620                     .body_len = 1,
  10621                 });
  10622                 sema.air_extra.appendAssumeCapacity(switch_br_inst);
  10623 
  10624                 return Air.indexToRef(capture_block_inst);
  10625             },
  10626             .ErrorSet => {
  10627                 if (capture_byref) {
  10628                     const capture_src = raw_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none);
  10629                     return sema.fail(
  10630                         block,
  10631                         capture_src,
  10632                         "error set cannot be captured by reference",
  10633                         .{},
  10634                     );
  10635                 }
  10636 
  10637                 if (case_vals.len == 1) {
  10638                     const item_val = sema.resolveConstValue(block, .unneeded, case_vals[0], "") catch unreachable;
  10639                     const item_ty = try mod.singleErrorSetType(item_val.getErrorName(mod).unwrap().?);
  10640                     return sema.bitCast(block, item_ty, spa.operand, operand_src, null);
  10641                 }
  10642 
  10643                 var names: Module.Fn.InferredErrorSet.NameMap = .{};
  10644                 try names.ensureUnusedCapacity(sema.arena, case_vals.len);
  10645                 for (case_vals) |err| {
  10646                     const err_val = sema.resolveConstValue(block, .unneeded, err, "") catch unreachable;
  10647                     names.putAssumeCapacityNoClobber(err_val.getErrorName(mod).unwrap().?, {});
  10648                 }
  10649                 const error_ty = try mod.errorSetFromUnsortedNames(names.keys());
  10650                 return sema.bitCast(block, error_ty, spa.operand, operand_src, null);
  10651             },
  10652             else => {
  10653                 // In this case the capture value is just the passed-through value
  10654                 // of the switch condition.
  10655                 if (capture_byref) {
  10656                     return spa.operand_ptr;
  10657                 } else {
  10658                     return spa.operand;
  10659                 }
  10660             },
  10661         }
  10662     }
  10663 };
  10664 
  10665 fn switchCond(
  10666     sema: *Sema,
  10667     block: *Block,
  10668     src: LazySrcLoc,
  10669     operand: Air.Inst.Ref,
  10670 ) CompileError!Air.Inst.Ref {
  10671     const mod = sema.mod;
  10672     const operand_ty = sema.typeOf(operand);
  10673     switch (operand_ty.zigTypeTag(mod)) {
  10674         .Type,
  10675         .Void,
  10676         .Bool,
  10677         .Int,
  10678         .Float,
  10679         .ComptimeFloat,
  10680         .ComptimeInt,
  10681         .EnumLiteral,
  10682         .Pointer,
  10683         .Fn,
  10684         .ErrorSet,
  10685         .Enum,
  10686         => {
  10687             if (operand_ty.isSlice(mod)) {
  10688                 return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(mod)});
  10689             }
  10690             if ((try sema.typeHasOnePossibleValue(operand_ty))) |opv| {
  10691                 return sema.addConstant(opv);
  10692             }
  10693             return operand;
  10694         },
  10695 
  10696         .Union => {
  10697             const union_ty = try sema.resolveTypeFields(operand_ty);
  10698             const enum_ty = union_ty.unionTagType(mod) orelse {
  10699                 const msg = msg: {
  10700                     const msg = try sema.errMsg(block, src, "switch on union with no attached enum", .{});
  10701                     errdefer msg.destroy(sema.gpa);
  10702                     if (union_ty.declSrcLocOrNull(mod)) |union_src| {
  10703                         try mod.errNoteNonLazy(union_src, msg, "consider 'union(enum)' here", .{});
  10704                     }
  10705                     break :msg msg;
  10706                 };
  10707                 return sema.failWithOwnedErrorMsg(msg);
  10708             };
  10709             return sema.unionToTag(block, enum_ty, operand, src);
  10710         },
  10711 
  10712         .ErrorUnion,
  10713         .NoReturn,
  10714         .Array,
  10715         .Struct,
  10716         .Undefined,
  10717         .Null,
  10718         .Optional,
  10719         .Opaque,
  10720         .Vector,
  10721         .Frame,
  10722         .AnyFrame,
  10723         => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(mod)}),
  10724     }
  10725 }
  10726 
  10727 const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, Module.SwitchProngSrc);
  10728 
  10729 fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref {
  10730     const tracy = trace(@src());
  10731     defer tracy.end();
  10732 
  10733     const mod = sema.mod;
  10734     const gpa = sema.gpa;
  10735     const ip = &mod.intern_pool;
  10736     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  10737     const src = inst_data.src();
  10738     const src_node_offset = inst_data.src_node;
  10739     const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
  10740     const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
  10741     const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
  10742 
  10743     const raw_operand: struct { val: Air.Inst.Ref, ptr: Air.Inst.Ref } = blk: {
  10744         const maybe_ptr = try sema.resolveInst(extra.data.operand);
  10745         if (operand_is_ref) {
  10746             const val = try sema.analyzeLoad(block, src, maybe_ptr, operand_src);
  10747             break :blk .{ .val = val, .ptr = maybe_ptr };
  10748         } else {
  10749             break :blk .{ .val = maybe_ptr, .ptr = undefined };
  10750         }
  10751     };
  10752 
  10753     const operand = try sema.switchCond(block, operand_src, raw_operand.val);
  10754 
  10755     // AstGen guarantees that the instruction immediately preceding
  10756     // switch_block(_ref) is a dbg_stmt
  10757     const cond_dbg_node_index = inst - 1;
  10758 
  10759     var header_extra_index: usize = extra.end;
  10760 
  10761     const scalar_cases_len = extra.data.bits.scalar_cases_len;
  10762     const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
  10763         const multi_cases_len = sema.code.extra[header_extra_index];
  10764         header_extra_index += 1;
  10765         break :blk multi_cases_len;
  10766     } else 0;
  10767 
  10768     const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
  10769         const tag_capture_inst = sema.code.extra[header_extra_index];
  10770         header_extra_index += 1;
  10771         // SwitchProngAnalysis wants inst_map to have space for the tag capture.
  10772         // Note that the normal capture is referred to via the switch block
  10773         // index, which there is already necessarily space for.
  10774         try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst});
  10775         break :blk tag_capture_inst;
  10776     } else undefined;
  10777 
  10778     var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
  10779     defer case_vals.deinit(gpa);
  10780 
  10781     const Special = struct {
  10782         body: []const Zir.Inst.Index,
  10783         end: usize,
  10784         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  10785         is_inline: bool,
  10786         has_tag_capture: bool,
  10787     };
  10788 
  10789     const special_prong = extra.data.bits.specialProng();
  10790     const special: Special = switch (special_prong) {
  10791         .none => .{
  10792             .body = &.{},
  10793             .end = header_extra_index,
  10794             .capture = .none,
  10795             .is_inline = false,
  10796             .has_tag_capture = false,
  10797         },
  10798         .under, .@"else" => blk: {
  10799             const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[header_extra_index]));
  10800             const extra_body_start = header_extra_index + 1;
  10801             break :blk .{
  10802                 .body = sema.code.extra[extra_body_start..][0..info.body_len],
  10803                 .end = extra_body_start + info.body_len,
  10804                 .capture = info.capture,
  10805                 .is_inline = info.is_inline,
  10806                 .has_tag_capture = info.has_tag_capture,
  10807             };
  10808         },
  10809     };
  10810 
  10811     const maybe_union_ty = sema.typeOf(raw_operand.val);
  10812     const union_originally = maybe_union_ty.zigTypeTag(mod) == .Union;
  10813 
  10814     // Duplicate checking variables later also used for `inline else`.
  10815     var seen_enum_fields: []?Module.SwitchProngSrc = &.{};
  10816     var seen_errors = SwitchErrorSet.init(gpa);
  10817     var range_set = RangeSet.init(gpa, mod);
  10818     var true_count: u8 = 0;
  10819     var false_count: u8 = 0;
  10820 
  10821     defer {
  10822         range_set.deinit();
  10823         gpa.free(seen_enum_fields);
  10824         seen_errors.deinit();
  10825     }
  10826 
  10827     var empty_enum = false;
  10828 
  10829     const operand_ty = sema.typeOf(operand);
  10830     const err_set = operand_ty.zigTypeTag(mod) == .ErrorSet;
  10831 
  10832     var else_error_ty: ?Type = null;
  10833 
  10834     // Validate usage of '_' prongs.
  10835     if (special_prong == .under and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) {
  10836         const msg = msg: {
  10837             const msg = try sema.errMsg(
  10838                 block,
  10839                 src,
  10840                 "'_' prong only allowed when switching on non-exhaustive enums",
  10841                 .{},
  10842             );
  10843             errdefer msg.destroy(gpa);
  10844             try sema.errNote(
  10845                 block,
  10846                 special_prong_src,
  10847                 msg,
  10848                 "'_' prong here",
  10849                 .{},
  10850             );
  10851             break :msg msg;
  10852         };
  10853         return sema.failWithOwnedErrorMsg(msg);
  10854     }
  10855 
  10856     // Validate for duplicate items, missing else prong, and invalid range.
  10857     switch (operand_ty.zigTypeTag(mod)) {
  10858         .Union => unreachable, // handled in zirSwitchCond
  10859         .Enum => {
  10860             seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount(mod));
  10861             empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum(mod);
  10862             @memset(seen_enum_fields, null);
  10863             // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
  10864 
  10865             var extra_index: usize = special.end;
  10866             {
  10867                 var scalar_i: u32 = 0;
  10868                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  10869                     const item_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  10870                     extra_index += 1;
  10871                     const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  10872                     extra_index += 1 + info.body_len;
  10873 
  10874                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
  10875                         block,
  10876                         seen_enum_fields,
  10877                         &range_set,
  10878                         item_ref,
  10879                         operand_ty,
  10880                         src_node_offset,
  10881                         .{ .scalar = scalar_i },
  10882                     ));
  10883                 }
  10884             }
  10885             {
  10886                 var multi_i: u32 = 0;
  10887                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  10888                     const items_len = sema.code.extra[extra_index];
  10889                     extra_index += 1;
  10890                     const ranges_len = sema.code.extra[extra_index];
  10891                     extra_index += 1;
  10892                     const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  10893                     extra_index += 1;
  10894                     const items = sema.code.refSlice(extra_index, items_len);
  10895                     extra_index += items_len + info.body_len;
  10896 
  10897                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  10898                     for (items, 0..) |item_ref, item_i| {
  10899                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
  10900                             block,
  10901                             seen_enum_fields,
  10902                             &range_set,
  10903                             item_ref,
  10904                             operand_ty,
  10905                             src_node_offset,
  10906                             .{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } },
  10907                         ));
  10908                     }
  10909 
  10910                     try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
  10911                 }
  10912             }
  10913             const all_tags_handled = for (seen_enum_fields) |seen_src| {
  10914                 if (seen_src == null) break false;
  10915             } else true;
  10916 
  10917             if (special_prong == .@"else") {
  10918                 if (all_tags_handled and !operand_ty.isNonexhaustiveEnum(mod)) return sema.fail(
  10919                     block,
  10920                     special_prong_src,
  10921                     "unreachable else prong; all cases already handled",
  10922                     .{},
  10923                 );
  10924             } else if (!all_tags_handled) {
  10925                 const msg = msg: {
  10926                     const msg = try sema.errMsg(
  10927                         block,
  10928                         src,
  10929                         "switch must handle all possibilities",
  10930                         .{},
  10931                     );
  10932                     errdefer msg.destroy(sema.gpa);
  10933                     for (seen_enum_fields, 0..) |seen_src, i| {
  10934                         if (seen_src != null) continue;
  10935 
  10936                         const field_name = operand_ty.enumFieldName(i, mod);
  10937                         try sema.addFieldErrNote(
  10938                             operand_ty,
  10939                             i,
  10940                             msg,
  10941                             "unhandled enumeration value: '{}'",
  10942                             .{field_name.fmt(&mod.intern_pool)},
  10943                         );
  10944                     }
  10945                     try mod.errNoteNonLazy(
  10946                         operand_ty.declSrcLoc(mod),
  10947                         msg,
  10948                         "enum '{}' declared here",
  10949                         .{operand_ty.fmt(mod)},
  10950                     );
  10951                     break :msg msg;
  10952                 };
  10953                 return sema.failWithOwnedErrorMsg(msg);
  10954             } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum(mod) and !union_originally) {
  10955                 return sema.fail(
  10956                     block,
  10957                     src,
  10958                     "switch on non-exhaustive enum must include 'else' or '_' prong",
  10959                     .{},
  10960                 );
  10961             }
  10962         },
  10963         .ErrorSet => {
  10964             var extra_index: usize = special.end;
  10965             {
  10966                 var scalar_i: u32 = 0;
  10967                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  10968                     const item_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  10969                     extra_index += 1;
  10970                     const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  10971                     extra_index += 1 + info.body_len;
  10972 
  10973                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
  10974                         block,
  10975                         &seen_errors,
  10976                         item_ref,
  10977                         operand_ty,
  10978                         src_node_offset,
  10979                         .{ .scalar = scalar_i },
  10980                     ));
  10981                 }
  10982             }
  10983             {
  10984                 var multi_i: u32 = 0;
  10985                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  10986                     const items_len = sema.code.extra[extra_index];
  10987                     extra_index += 1;
  10988                     const ranges_len = sema.code.extra[extra_index];
  10989                     extra_index += 1;
  10990                     const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  10991                     extra_index += 1;
  10992                     const items = sema.code.refSlice(extra_index, items_len);
  10993                     extra_index += items_len + info.body_len;
  10994 
  10995                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  10996                     for (items, 0..) |item_ref, item_i| {
  10997                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
  10998                             block,
  10999                             &seen_errors,
  11000                             item_ref,
  11001                             operand_ty,
  11002                             src_node_offset,
  11003                             .{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } },
  11004                         ));
  11005                     }
  11006 
  11007                     try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
  11008                 }
  11009             }
  11010 
  11011             try sema.resolveInferredErrorSetTy(block, src, operand_ty);
  11012 
  11013             if (operand_ty.isAnyError(mod)) {
  11014                 if (special_prong != .@"else") {
  11015                     return sema.fail(
  11016                         block,
  11017                         src,
  11018                         "else prong required when switching on type 'anyerror'",
  11019                         .{},
  11020                     );
  11021                 }
  11022                 else_error_ty = Type.anyerror;
  11023             } else else_validation: {
  11024                 var maybe_msg: ?*Module.ErrorMsg = null;
  11025                 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
  11026 
  11027                 for (operand_ty.errorSetNames(mod)) |error_name| {
  11028                     if (!seen_errors.contains(error_name) and special_prong != .@"else") {
  11029                         const msg = maybe_msg orelse blk: {
  11030                             maybe_msg = try sema.errMsg(
  11031                                 block,
  11032                                 src,
  11033                                 "switch must handle all possibilities",
  11034                                 .{},
  11035                             );
  11036                             break :blk maybe_msg.?;
  11037                         };
  11038 
  11039                         try sema.errNote(
  11040                             block,
  11041                             src,
  11042                             msg,
  11043                             "unhandled error value: 'error.{}'",
  11044                             .{error_name.fmt(ip)},
  11045                         );
  11046                     }
  11047                 }
  11048 
  11049                 if (maybe_msg) |msg| {
  11050                     maybe_msg = null;
  11051                     try sema.addDeclaredHereNote(msg, operand_ty);
  11052                     return sema.failWithOwnedErrorMsg(msg);
  11053                 }
  11054 
  11055                 if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames(mod).len) {
  11056                     // In order to enable common patterns for generic code allow simple else bodies
  11057                     // else => unreachable,
  11058                     // else => return,
  11059                     // else => |e| return e,
  11060                     // even if all the possible errors were already handled.
  11061                     const tags = sema.code.instructions.items(.tag);
  11062                     for (special.body) |else_inst| switch (tags[else_inst]) {
  11063                         .dbg_block_begin,
  11064                         .dbg_block_end,
  11065                         .dbg_stmt,
  11066                         .dbg_var_val,
  11067                         .ret_type,
  11068                         .as_node,
  11069                         .ret_node,
  11070                         .@"unreachable",
  11071                         .@"defer",
  11072                         .defer_err_code,
  11073                         .err_union_code,
  11074                         .ret_err_value_code,
  11075                         .restore_err_ret_index,
  11076                         .is_non_err,
  11077                         .ret_is_non_err,
  11078                         .condbr,
  11079                         => {},
  11080                         else => break,
  11081                     } else break :else_validation;
  11082 
  11083                     return sema.fail(
  11084                         block,
  11085                         special_prong_src,
  11086                         "unreachable else prong; all cases already handled",
  11087                         .{},
  11088                     );
  11089                 }
  11090 
  11091                 const error_names = operand_ty.errorSetNames(mod);
  11092                 var names: Module.Fn.InferredErrorSet.NameMap = .{};
  11093                 try names.ensureUnusedCapacity(sema.arena, error_names.len);
  11094                 for (error_names) |error_name| {
  11095                     if (seen_errors.contains(error_name)) continue;
  11096 
  11097                     names.putAssumeCapacityNoClobber(error_name, {});
  11098                 }
  11099                 // No need to keep the hash map metadata correct; here we
  11100                 // extract the (sorted) keys only.
  11101                 else_error_ty = try mod.errorSetFromUnsortedNames(names.keys());
  11102             }
  11103         },
  11104         .Int, .ComptimeInt => {
  11105             var extra_index: usize = special.end;
  11106             {
  11107                 var scalar_i: u32 = 0;
  11108                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11109                     const item_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  11110                     extra_index += 1;
  11111                     const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  11112                     extra_index += 1 + info.body_len;
  11113 
  11114                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
  11115                         block,
  11116                         &range_set,
  11117                         item_ref,
  11118                         operand_ty,
  11119                         src_node_offset,
  11120                         .{ .scalar = scalar_i },
  11121                     ));
  11122                 }
  11123             }
  11124             {
  11125                 var multi_i: u32 = 0;
  11126                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11127                     const items_len = sema.code.extra[extra_index];
  11128                     extra_index += 1;
  11129                     const ranges_len = sema.code.extra[extra_index];
  11130                     extra_index += 1;
  11131                     const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  11132                     extra_index += 1;
  11133                     const items = sema.code.refSlice(extra_index, items_len);
  11134                     extra_index += items_len;
  11135 
  11136                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11137                     for (items, 0..) |item_ref, item_i| {
  11138                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
  11139                             block,
  11140                             &range_set,
  11141                             item_ref,
  11142                             operand_ty,
  11143                             src_node_offset,
  11144                             .{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } },
  11145                         ));
  11146                     }
  11147 
  11148                     try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len);
  11149                     var range_i: u32 = 0;
  11150                     while (range_i < ranges_len) : (range_i += 1) {
  11151                         const item_first = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  11152                         extra_index += 1;
  11153                         const item_last = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  11154                         extra_index += 1;
  11155 
  11156                         const vals = try sema.validateSwitchRange(
  11157                             block,
  11158                             &range_set,
  11159                             item_first,
  11160                             item_last,
  11161                             operand_ty,
  11162                             src_node_offset,
  11163                             .{ .range = .{ .prong = multi_i, .item = range_i } },
  11164                         );
  11165                         case_vals.appendAssumeCapacity(vals[0]);
  11166                         case_vals.appendAssumeCapacity(vals[1]);
  11167                     }
  11168 
  11169                     extra_index += info.body_len;
  11170                 }
  11171             }
  11172 
  11173             check_range: {
  11174                 if (operand_ty.zigTypeTag(mod) == .Int) {
  11175                     const min_int = try operand_ty.minInt(mod, operand_ty);
  11176                     const max_int = try operand_ty.maxInt(mod, operand_ty);
  11177                     if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {
  11178                         if (special_prong == .@"else") {
  11179                             return sema.fail(
  11180                                 block,
  11181                                 special_prong_src,
  11182                                 "unreachable else prong; all cases already handled",
  11183                                 .{},
  11184                             );
  11185                         }
  11186                         break :check_range;
  11187                     }
  11188                 }
  11189                 if (special_prong != .@"else") {
  11190                     return sema.fail(
  11191                         block,
  11192                         src,
  11193                         "switch must handle all possibilities",
  11194                         .{},
  11195                     );
  11196                 }
  11197             }
  11198         },
  11199         .Bool => {
  11200             var extra_index: usize = special.end;
  11201             {
  11202                 var scalar_i: u32 = 0;
  11203                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11204                     const item_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  11205                     extra_index += 1;
  11206                     const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  11207                     extra_index += 1 + info.body_len;
  11208 
  11209                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
  11210                         block,
  11211                         &true_count,
  11212                         &false_count,
  11213                         item_ref,
  11214                         src_node_offset,
  11215                         .{ .scalar = scalar_i },
  11216                     ));
  11217                 }
  11218             }
  11219             {
  11220                 var multi_i: u32 = 0;
  11221                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11222                     const items_len = sema.code.extra[extra_index];
  11223                     extra_index += 1;
  11224                     const ranges_len = sema.code.extra[extra_index];
  11225                     extra_index += 1;
  11226                     const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  11227                     extra_index += 1;
  11228                     const items = sema.code.refSlice(extra_index, items_len);
  11229                     extra_index += items_len + info.body_len;
  11230 
  11231                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11232                     for (items, 0..) |item_ref, item_i| {
  11233                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
  11234                             block,
  11235                             &true_count,
  11236                             &false_count,
  11237                             item_ref,
  11238                             src_node_offset,
  11239                             .{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } },
  11240                         ));
  11241                     }
  11242 
  11243                     try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
  11244                 }
  11245             }
  11246             switch (special_prong) {
  11247                 .@"else" => {
  11248                     if (true_count + false_count == 2) {
  11249                         return sema.fail(
  11250                             block,
  11251                             special_prong_src,
  11252                             "unreachable else prong; all cases already handled",
  11253                             .{},
  11254                         );
  11255                     }
  11256                 },
  11257                 .under, .none => {
  11258                     if (true_count + false_count < 2) {
  11259                         return sema.fail(
  11260                             block,
  11261                             src,
  11262                             "switch must handle all possibilities",
  11263                             .{},
  11264                         );
  11265                     }
  11266                 },
  11267             }
  11268         },
  11269         .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
  11270             if (special_prong != .@"else") {
  11271                 return sema.fail(
  11272                     block,
  11273                     src,
  11274                     "else prong required when switching on type '{}'",
  11275                     .{operand_ty.fmt(mod)},
  11276                 );
  11277             }
  11278 
  11279             var seen_values = ValueSrcMap{};
  11280             defer seen_values.deinit(gpa);
  11281 
  11282             var extra_index: usize = special.end;
  11283             {
  11284                 var scalar_i: u32 = 0;
  11285                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11286                     const item_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  11287                     extra_index += 1;
  11288                     const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  11289                     extra_index += 1;
  11290                     extra_index += info.body_len;
  11291 
  11292                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
  11293                         block,
  11294                         &seen_values,
  11295                         item_ref,
  11296                         operand_ty,
  11297                         src_node_offset,
  11298                         .{ .scalar = scalar_i },
  11299                     ));
  11300                 }
  11301             }
  11302             {
  11303                 var multi_i: u32 = 0;
  11304                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11305                     const items_len = sema.code.extra[extra_index];
  11306                     extra_index += 1;
  11307                     const ranges_len = sema.code.extra[extra_index];
  11308                     extra_index += 1;
  11309                     const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  11310                     extra_index += 1;
  11311                     const items = sema.code.refSlice(extra_index, items_len);
  11312                     extra_index += items_len + info.body_len;
  11313 
  11314                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11315                     for (items, 0..) |item_ref, item_i| {
  11316                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
  11317                             block,
  11318                             &seen_values,
  11319                             item_ref,
  11320                             operand_ty,
  11321                             src_node_offset,
  11322                             .{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } },
  11323                         ));
  11324                     }
  11325 
  11326                     try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
  11327                 }
  11328             }
  11329         },
  11330 
  11331         .ErrorUnion,
  11332         .NoReturn,
  11333         .Array,
  11334         .Struct,
  11335         .Undefined,
  11336         .Null,
  11337         .Optional,
  11338         .Opaque,
  11339         .Vector,
  11340         .Frame,
  11341         .AnyFrame,
  11342         .ComptimeFloat,
  11343         .Float,
  11344         => return sema.fail(block, operand_src, "invalid switch operand type '{}'", .{
  11345             operand_ty.fmt(mod),
  11346         }),
  11347     }
  11348 
  11349     const spa: SwitchProngAnalysis = .{
  11350         .sema = sema,
  11351         .parent_block = block,
  11352         .operand = raw_operand.val,
  11353         .operand_ptr = raw_operand.ptr,
  11354         .cond = operand,
  11355         .else_error_ty = else_error_ty,
  11356         .switch_block_inst = inst,
  11357         .tag_capture_inst = tag_capture_inst,
  11358     };
  11359 
  11360     const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
  11361     try sema.air_instructions.append(gpa, .{
  11362         .tag = .block,
  11363         .data = undefined,
  11364     });
  11365     var label: Block.Label = .{
  11366         .zir_block = inst,
  11367         .merges = .{
  11368             .src_locs = .{},
  11369             .results = .{},
  11370             .br_list = .{},
  11371             .block_inst = block_inst,
  11372         },
  11373     };
  11374 
  11375     var child_block: Block = .{
  11376         .parent = block,
  11377         .sema = sema,
  11378         .src_decl = block.src_decl,
  11379         .namespace = block.namespace,
  11380         .wip_capture_scope = block.wip_capture_scope,
  11381         .instructions = .{},
  11382         .label = &label,
  11383         .inlining = block.inlining,
  11384         .is_comptime = block.is_comptime,
  11385         .comptime_reason = block.comptime_reason,
  11386         .is_typeof = block.is_typeof,
  11387         .c_import_buf = block.c_import_buf,
  11388         .runtime_cond = block.runtime_cond,
  11389         .runtime_loop = block.runtime_loop,
  11390         .runtime_index = block.runtime_index,
  11391         .error_return_trace_index = block.error_return_trace_index,
  11392     };
  11393     const merges = &child_block.label.?.merges;
  11394     defer child_block.instructions.deinit(gpa);
  11395     defer merges.deinit(gpa);
  11396 
  11397     if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| {
  11398         const resolved_operand_val = try sema.resolveLazyValue(operand_val);
  11399         var extra_index: usize = special.end;
  11400         {
  11401             var scalar_i: usize = 0;
  11402             while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11403                 extra_index += 1;
  11404                 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  11405                 extra_index += 1;
  11406                 const body = sema.code.extra[extra_index..][0..info.body_len];
  11407                 extra_index += info.body_len;
  11408 
  11409                 const item = case_vals.items[scalar_i];
  11410                 const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable;
  11411                 if (operand_val.eql(item_val, operand_ty, sema.mod)) {
  11412                     if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
  11413                     return spa.resolveProngComptime(
  11414                         &child_block,
  11415                         .normal,
  11416                         body,
  11417                         info.capture,
  11418                         .{ .scalar_capture = @as(u32, @intCast(scalar_i)) },
  11419                         &.{item},
  11420                         if (info.is_inline) operand else .none,
  11421                         info.has_tag_capture,
  11422                         merges,
  11423                     );
  11424                 }
  11425             }
  11426         }
  11427         {
  11428             var multi_i: usize = 0;
  11429             var case_val_idx: usize = scalar_cases_len;
  11430             while (multi_i < multi_cases_len) : (multi_i += 1) {
  11431                 const items_len = sema.code.extra[extra_index];
  11432                 extra_index += 1;
  11433                 const ranges_len = sema.code.extra[extra_index];
  11434                 extra_index += 1;
  11435                 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  11436                 extra_index += 1 + items_len;
  11437                 const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..info.body_len];
  11438 
  11439                 const items = case_vals.items[case_val_idx..][0..items_len];
  11440                 case_val_idx += items_len;
  11441 
  11442                 for (items) |item| {
  11443                     // Validation above ensured these will succeed.
  11444                     const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable;
  11445                     if (operand_val.eql(item_val, operand_ty, sema.mod)) {
  11446                         if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
  11447                         return spa.resolveProngComptime(
  11448                             &child_block,
  11449                             .normal,
  11450                             body,
  11451                             info.capture,
  11452                             .{ .multi_capture = @as(u32, @intCast(multi_i)) },
  11453                             items,
  11454                             if (info.is_inline) operand else .none,
  11455                             info.has_tag_capture,
  11456                             merges,
  11457                         );
  11458                     }
  11459                 }
  11460 
  11461                 var range_i: usize = 0;
  11462                 while (range_i < ranges_len) : (range_i += 1) {
  11463                     const range_items = case_vals.items[case_val_idx..][0..2];
  11464                     extra_index += 2;
  11465                     case_val_idx += 2;
  11466 
  11467                     // Validation above ensured these will succeed.
  11468                     const first_val = sema.resolveConstValue(&child_block, .unneeded, range_items[0], "") catch unreachable;
  11469                     const last_val = sema.resolveConstValue(&child_block, .unneeded, range_items[1], "") catch unreachable;
  11470                     if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and
  11471                         (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty)))
  11472                     {
  11473                         if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
  11474                         return spa.resolveProngComptime(
  11475                             &child_block,
  11476                             .normal,
  11477                             body,
  11478                             info.capture,
  11479                             .{ .multi_capture = @as(u32, @intCast(multi_i)) },
  11480                             undefined, // case_vals may be undefined for ranges
  11481                             if (info.is_inline) operand else .none,
  11482                             info.has_tag_capture,
  11483                             merges,
  11484                         );
  11485                     }
  11486                 }
  11487 
  11488                 extra_index += info.body_len;
  11489             }
  11490         }
  11491         if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, special.body, operand);
  11492         if (empty_enum) {
  11493             return Air.Inst.Ref.void_value;
  11494         }
  11495 
  11496         return spa.resolveProngComptime(
  11497             &child_block,
  11498             .special,
  11499             special.body,
  11500             special.capture,
  11501             .special_capture,
  11502             undefined, // case_vals may be undefined for special prongs
  11503             if (special.is_inline) operand else .none,
  11504             special.has_tag_capture,
  11505             merges,
  11506         );
  11507     }
  11508 
  11509     if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
  11510         if (empty_enum) {
  11511             return Air.Inst.Ref.void_value;
  11512         }
  11513         if (special_prong == .none) {
  11514             return sema.fail(block, src, "switch must handle all possibilities", .{});
  11515         }
  11516         if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) {
  11517             return Air.Inst.Ref.unreachable_value;
  11518         }
  11519         if (mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and
  11520             (!operand_ty.isNonexhaustiveEnum(mod) or union_originally))
  11521         {
  11522             try sema.zirDbgStmt(block, cond_dbg_node_index);
  11523             const ok = try block.addUnOp(.is_named_enum_value, operand);
  11524             try sema.addSafetyCheck(block, ok, .corrupt_switch);
  11525         }
  11526 
  11527         return spa.resolveProngComptime(
  11528             &child_block,
  11529             .special,
  11530             special.body,
  11531             special.capture,
  11532             .special_capture,
  11533             undefined, // case_vals may be undefined for special prongs
  11534             .none,
  11535             false,
  11536             merges,
  11537         );
  11538     }
  11539 
  11540     if (child_block.is_comptime) {
  11541         _ = sema.resolveConstValue(&child_block, operand_src, operand, "condition in comptime switch must be comptime-known") catch |err| {
  11542             if (err == error.AnalysisFail and child_block.comptime_reason != null) try child_block.comptime_reason.?.explain(sema, sema.err);
  11543             return err;
  11544         };
  11545         unreachable;
  11546     }
  11547 
  11548     const estimated_cases_extra = (scalar_cases_len + multi_cases_len) *
  11549         @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2;
  11550     var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra);
  11551     defer cases_extra.deinit(gpa);
  11552 
  11553     var case_block = child_block.makeSubBlock();
  11554     case_block.runtime_loop = null;
  11555     case_block.runtime_cond = operand_src;
  11556     case_block.runtime_index.increment();
  11557     defer case_block.instructions.deinit(gpa);
  11558 
  11559     var extra_index: usize = special.end;
  11560 
  11561     var scalar_i: usize = 0;
  11562     while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11563         extra_index += 1;
  11564         const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  11565         extra_index += 1;
  11566         const body = sema.code.extra[extra_index..][0..info.body_len];
  11567         extra_index += info.body_len;
  11568 
  11569         var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope);
  11570         defer wip_captures.deinit();
  11571 
  11572         case_block.instructions.shrinkRetainingCapacity(0);
  11573         case_block.wip_capture_scope = wip_captures.scope;
  11574 
  11575         const item = case_vals.items[scalar_i];
  11576         // `item` is already guaranteed to be constant known.
  11577 
  11578         const analyze_body = if (union_originally) blk: {
  11579             const item_val = sema.resolveConstLazyValue(block, .unneeded, item, "") catch unreachable;
  11580             const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
  11581             break :blk field_ty.zigTypeTag(mod) != .NoReturn;
  11582         } else true;
  11583 
  11584         if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
  11585             // nothing to do here
  11586         } else if (analyze_body) {
  11587             try spa.analyzeProngRuntime(
  11588                 &case_block,
  11589                 .normal,
  11590                 body,
  11591                 info.capture,
  11592                 .{ .scalar_capture = @as(u32, @intCast(scalar_i)) },
  11593                 &.{item},
  11594                 if (info.is_inline) item else .none,
  11595                 info.has_tag_capture,
  11596             );
  11597         } else {
  11598             _ = try case_block.addNoOp(.unreach);
  11599         }
  11600 
  11601         try wip_captures.finalize();
  11602 
  11603         try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  11604         cases_extra.appendAssumeCapacity(1); // items_len
  11605         cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len)));
  11606         cases_extra.appendAssumeCapacity(@intFromEnum(item));
  11607         cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
  11608     }
  11609 
  11610     var is_first = true;
  11611     var prev_cond_br: Air.Inst.Index = undefined;
  11612     var first_else_body: []const Air.Inst.Index = &.{};
  11613     defer gpa.free(first_else_body);
  11614     var prev_then_body: []const Air.Inst.Index = &.{};
  11615     defer gpa.free(prev_then_body);
  11616 
  11617     var cases_len = scalar_cases_len;
  11618     var case_val_idx: usize = scalar_cases_len;
  11619     var multi_i: u32 = 0;
  11620     while (multi_i < multi_cases_len) : (multi_i += 1) {
  11621         const items_len = sema.code.extra[extra_index];
  11622         extra_index += 1;
  11623         const ranges_len = sema.code.extra[extra_index];
  11624         extra_index += 1;
  11625         const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index]));
  11626         extra_index += 1 + items_len;
  11627 
  11628         const items = case_vals.items[case_val_idx..][0..items_len];
  11629         case_val_idx += items_len;
  11630 
  11631         case_block.instructions.shrinkRetainingCapacity(0);
  11632         case_block.wip_capture_scope = child_block.wip_capture_scope;
  11633 
  11634         // Generate all possible cases as scalar prongs.
  11635         if (info.is_inline) {
  11636             const body_start = extra_index + 2 * ranges_len;
  11637             const body = sema.code.extra[body_start..][0..info.body_len];
  11638             var emit_bb = false;
  11639 
  11640             var range_i: u32 = 0;
  11641             while (range_i < ranges_len) : (range_i += 1) {
  11642                 const range_items = case_vals.items[case_val_idx..][0..2];
  11643                 extra_index += 2;
  11644                 case_val_idx += 2;
  11645 
  11646                 const item_first_ref = range_items[0];
  11647                 const item_last_ref = range_items[1];
  11648 
  11649                 var item = sema.resolveConstValue(block, .unneeded, item_first_ref, undefined) catch unreachable;
  11650                 const item_last = sema.resolveConstValue(block, .unneeded, item_last_ref, undefined) catch unreachable;
  11651 
  11652                 while (item.compareScalar(.lte, item_last, operand_ty, mod)) : ({
  11653                     // Previous validation has resolved any possible lazy values.
  11654                     item = sema.intAddScalar(item, try mod.intValue(operand_ty, 1), operand_ty) catch |err| switch (err) {
  11655                         error.Overflow => unreachable,
  11656                         else => |e| return e,
  11657                     };
  11658                 }) {
  11659                     cases_len += 1;
  11660 
  11661                     const item_ref = try sema.addConstant(item);
  11662 
  11663                     case_block.instructions.shrinkRetainingCapacity(0);
  11664                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  11665 
  11666                     if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
  11667                         error.NeededSourceLocation => {
  11668                             const case_src = Module.SwitchProngSrc{ .range = .{ .prong = multi_i, .item = range_i } };
  11669                             const decl = mod.declPtr(case_block.src_decl);
  11670                             try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none));
  11671                             unreachable;
  11672                         },
  11673                         else => return err,
  11674                     };
  11675                     emit_bb = true;
  11676 
  11677                     try spa.analyzeProngRuntime(
  11678                         &case_block,
  11679                         .normal,
  11680                         body,
  11681                         info.capture,
  11682                         .{ .multi_capture = multi_i },
  11683                         undefined, // case_vals may be undefined for ranges
  11684                         item_ref,
  11685                         info.has_tag_capture,
  11686                     );
  11687 
  11688                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  11689                     cases_extra.appendAssumeCapacity(1); // items_len
  11690                     cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len)));
  11691                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  11692                     cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
  11693 
  11694                     if (item.compareScalar(.eq, item_last, operand_ty, mod)) break;
  11695                 }
  11696             }
  11697 
  11698             for (items, 0..) |item, item_i| {
  11699                 cases_len += 1;
  11700 
  11701                 case_block.instructions.shrinkRetainingCapacity(0);
  11702                 case_block.wip_capture_scope = child_block.wip_capture_scope;
  11703 
  11704                 const analyze_body = if (union_originally) blk: {
  11705                     const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
  11706                     const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
  11707                     break :blk field_ty.zigTypeTag(mod) != .NoReturn;
  11708                 } else true;
  11709 
  11710                 if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
  11711                     error.NeededSourceLocation => {
  11712                         const case_src = Module.SwitchProngSrc{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } };
  11713                         const decl = mod.declPtr(case_block.src_decl);
  11714                         try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none));
  11715                         unreachable;
  11716                     },
  11717                     else => return err,
  11718                 };
  11719                 emit_bb = true;
  11720 
  11721                 if (analyze_body) {
  11722                     try spa.analyzeProngRuntime(
  11723                         &case_block,
  11724                         .normal,
  11725                         body,
  11726                         info.capture,
  11727                         .{ .multi_capture = multi_i },
  11728                         &.{item},
  11729                         item,
  11730                         info.has_tag_capture,
  11731                     );
  11732                 } else {
  11733                     _ = try case_block.addNoOp(.unreach);
  11734                 }
  11735 
  11736                 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  11737                 cases_extra.appendAssumeCapacity(1); // items_len
  11738                 cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len)));
  11739                 cases_extra.appendAssumeCapacity(@intFromEnum(item));
  11740                 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
  11741             }
  11742 
  11743             extra_index += info.body_len;
  11744             continue;
  11745         }
  11746 
  11747         var any_ok: Air.Inst.Ref = .none;
  11748 
  11749         // If there are any ranges, we have to put all the items into the
  11750         // else prong. Otherwise, we can take advantage of multiple items
  11751         // mapping to the same body.
  11752         if (ranges_len == 0) {
  11753             cases_len += 1;
  11754 
  11755             const analyze_body = if (union_originally)
  11756                 for (items) |item| {
  11757                     const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable;
  11758                     const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
  11759                     if (field_ty.zigTypeTag(mod) != .NoReturn) break true;
  11760                 } else false
  11761             else
  11762                 true;
  11763 
  11764             const body = sema.code.extra[extra_index..][0..info.body_len];
  11765             extra_index += info.body_len;
  11766             if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
  11767                 // nothing to do here
  11768             } else if (analyze_body) {
  11769                 try spa.analyzeProngRuntime(
  11770                     &case_block,
  11771                     .normal,
  11772                     body,
  11773                     info.capture,
  11774                     .{ .multi_capture = multi_i },
  11775                     items,
  11776                     .none,
  11777                     false,
  11778                 );
  11779             } else {
  11780                 _ = try case_block.addNoOp(.unreach);
  11781             }
  11782 
  11783             try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len +
  11784                 case_block.instructions.items.len);
  11785 
  11786             cases_extra.appendAssumeCapacity(@as(u32, @intCast(items.len)));
  11787             cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len)));
  11788 
  11789             for (items) |item| {
  11790                 cases_extra.appendAssumeCapacity(@intFromEnum(item));
  11791             }
  11792 
  11793             cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
  11794         } else {
  11795             for (items) |item| {
  11796                 const cmp_ok = try case_block.addBinOp(if (case_block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, item);
  11797                 if (any_ok != .none) {
  11798                     any_ok = try case_block.addBinOp(.bool_or, any_ok, cmp_ok);
  11799                 } else {
  11800                     any_ok = cmp_ok;
  11801                 }
  11802             }
  11803 
  11804             var range_i: usize = 0;
  11805             while (range_i < ranges_len) : (range_i += 1) {
  11806                 const range_items = case_vals.items[case_val_idx..][0..2];
  11807                 extra_index += 2;
  11808                 case_val_idx += 2;
  11809 
  11810                 const item_first = range_items[0];
  11811                 const item_last = range_items[1];
  11812 
  11813                 // operand >= first and operand <= last
  11814                 const range_first_ok = try case_block.addBinOp(
  11815                     if (case_block.float_mode == .Optimized) .cmp_gte_optimized else .cmp_gte,
  11816                     operand,
  11817                     item_first,
  11818                 );
  11819                 const range_last_ok = try case_block.addBinOp(
  11820                     if (case_block.float_mode == .Optimized) .cmp_lte_optimized else .cmp_lte,
  11821                     operand,
  11822                     item_last,
  11823                 );
  11824                 const range_ok = try case_block.addBinOp(
  11825                     .bool_and,
  11826                     range_first_ok,
  11827                     range_last_ok,
  11828                 );
  11829                 if (any_ok != .none) {
  11830                     any_ok = try case_block.addBinOp(.bool_or, any_ok, range_ok);
  11831                 } else {
  11832                     any_ok = range_ok;
  11833                 }
  11834             }
  11835 
  11836             const new_cond_br = try case_block.addInstAsIndex(.{ .tag = .cond_br, .data = .{
  11837                 .pl_op = .{
  11838                     .operand = any_ok,
  11839                     .payload = undefined,
  11840                 },
  11841             } });
  11842             var cond_body = try case_block.instructions.toOwnedSlice(gpa);
  11843             defer gpa.free(cond_body);
  11844 
  11845             var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope);
  11846             defer wip_captures.deinit();
  11847 
  11848             case_block.instructions.shrinkRetainingCapacity(0);
  11849             case_block.wip_capture_scope = wip_captures.scope;
  11850 
  11851             const body = sema.code.extra[extra_index..][0..info.body_len];
  11852             extra_index += info.body_len;
  11853             if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
  11854                 // nothing to do here
  11855             } else {
  11856                 try spa.analyzeProngRuntime(
  11857                     &case_block,
  11858                     .normal,
  11859                     body,
  11860                     info.capture,
  11861                     .{ .multi_capture = multi_i },
  11862                     items,
  11863                     .none,
  11864                     false,
  11865                 );
  11866             }
  11867 
  11868             try wip_captures.finalize();
  11869 
  11870             if (is_first) {
  11871                 is_first = false;
  11872                 first_else_body = cond_body;
  11873                 cond_body = &.{};
  11874             } else {
  11875                 try sema.air_extra.ensureUnusedCapacity(
  11876                     gpa,
  11877                     @typeInfo(Air.CondBr).Struct.fields.len + prev_then_body.len + cond_body.len,
  11878                 );
  11879 
  11880                 sema.air_instructions.items(.data)[prev_cond_br].pl_op.payload =
  11881                     sema.addExtraAssumeCapacity(Air.CondBr{
  11882                     .then_body_len = @as(u32, @intCast(prev_then_body.len)),
  11883                     .else_body_len = @as(u32, @intCast(cond_body.len)),
  11884                 });
  11885                 sema.air_extra.appendSliceAssumeCapacity(prev_then_body);
  11886                 sema.air_extra.appendSliceAssumeCapacity(cond_body);
  11887             }
  11888             gpa.free(prev_then_body);
  11889             prev_then_body = try case_block.instructions.toOwnedSlice(gpa);
  11890             prev_cond_br = new_cond_br;
  11891         }
  11892     }
  11893 
  11894     var final_else_body: []const Air.Inst.Index = &.{};
  11895     if (special.body.len != 0 or !is_first or case_block.wantSafety()) {
  11896         var emit_bb = false;
  11897         if (special.is_inline) switch (operand_ty.zigTypeTag(mod)) {
  11898             .Enum => {
  11899                 if (operand_ty.isNonexhaustiveEnum(mod) and !union_originally) {
  11900                     return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
  11901                         operand_ty.fmt(mod),
  11902                     });
  11903                 }
  11904                 for (seen_enum_fields, 0..) |f, i| {
  11905                     if (f != null) continue;
  11906                     cases_len += 1;
  11907 
  11908                     const item_val = try mod.enumValueFieldIndex(operand_ty, @as(u32, @intCast(i)));
  11909                     const item_ref = try sema.addConstant(item_val);
  11910 
  11911                     case_block.instructions.shrinkRetainingCapacity(0);
  11912                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  11913 
  11914                     const analyze_body = if (union_originally) blk: {
  11915                         const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
  11916                         break :blk field_ty.zigTypeTag(mod) != .NoReturn;
  11917                     } else true;
  11918 
  11919                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  11920                     emit_bb = true;
  11921 
  11922                     if (analyze_body) {
  11923                         try spa.analyzeProngRuntime(
  11924                             &case_block,
  11925                             .special,
  11926                             special.body,
  11927                             special.capture,
  11928                             .special_capture,
  11929                             &.{item_ref},
  11930                             item_ref,
  11931                             special.has_tag_capture,
  11932                         );
  11933                     } else {
  11934                         _ = try case_block.addNoOp(.unreach);
  11935                     }
  11936 
  11937                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  11938                     cases_extra.appendAssumeCapacity(1); // items_len
  11939                     cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len)));
  11940                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  11941                     cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
  11942                 }
  11943             },
  11944             .ErrorSet => {
  11945                 if (operand_ty.isAnyError(mod)) {
  11946                     return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
  11947                         operand_ty.fmt(mod),
  11948                     });
  11949                 }
  11950                 for (0..operand_ty.errorSetNames(mod).len) |i| {
  11951                     const error_name = operand_ty.errorSetNames(mod)[i];
  11952                     if (seen_errors.contains(error_name)) continue;
  11953                     cases_len += 1;
  11954 
  11955                     const item_val = try mod.intern(.{ .err = .{
  11956                         .ty = operand_ty.toIntern(),
  11957                         .name = error_name,
  11958                     } });
  11959                     const item_ref = try sema.addConstant(item_val.toValue());
  11960 
  11961                     case_block.instructions.shrinkRetainingCapacity(0);
  11962                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  11963 
  11964                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  11965                     emit_bb = true;
  11966 
  11967                     try spa.analyzeProngRuntime(
  11968                         &case_block,
  11969                         .special,
  11970                         special.body,
  11971                         special.capture,
  11972                         .special_capture,
  11973                         &.{item_ref},
  11974                         item_ref,
  11975                         special.has_tag_capture,
  11976                     );
  11977 
  11978                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  11979                     cases_extra.appendAssumeCapacity(1); // items_len
  11980                     cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len)));
  11981                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  11982                     cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
  11983                 }
  11984             },
  11985             .Int => {
  11986                 var it = try RangeSetUnhandledIterator.init(sema, operand_ty, range_set);
  11987                 while (try it.next()) |cur| {
  11988                     cases_len += 1;
  11989 
  11990                     const item_ref = try sema.addConstant(cur.toValue());
  11991 
  11992                     case_block.instructions.shrinkRetainingCapacity(0);
  11993                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  11994 
  11995                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  11996                     emit_bb = true;
  11997 
  11998                     try spa.analyzeProngRuntime(
  11999                         &case_block,
  12000                         .special,
  12001                         special.body,
  12002                         special.capture,
  12003                         .special_capture,
  12004                         &.{item_ref},
  12005                         item_ref,
  12006                         special.has_tag_capture,
  12007                     );
  12008 
  12009                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  12010                     cases_extra.appendAssumeCapacity(1); // items_len
  12011                     cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len)));
  12012                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12013                     cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
  12014                 }
  12015             },
  12016             .Bool => {
  12017                 if (true_count == 0) {
  12018                     cases_len += 1;
  12019 
  12020                     case_block.instructions.shrinkRetainingCapacity(0);
  12021                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  12022 
  12023                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12024                     emit_bb = true;
  12025 
  12026                     try spa.analyzeProngRuntime(
  12027                         &case_block,
  12028                         .special,
  12029                         special.body,
  12030                         special.capture,
  12031                         .special_capture,
  12032                         &.{Air.Inst.Ref.bool_true},
  12033                         Air.Inst.Ref.bool_true,
  12034                         special.has_tag_capture,
  12035                     );
  12036 
  12037                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  12038                     cases_extra.appendAssumeCapacity(1); // items_len
  12039                     cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len)));
  12040                     cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true));
  12041                     cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
  12042                 }
  12043                 if (false_count == 0) {
  12044                     cases_len += 1;
  12045 
  12046                     case_block.instructions.shrinkRetainingCapacity(0);
  12047                     case_block.wip_capture_scope = child_block.wip_capture_scope;
  12048 
  12049                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12050                     emit_bb = true;
  12051 
  12052                     try spa.analyzeProngRuntime(
  12053                         &case_block,
  12054                         .special,
  12055                         special.body,
  12056                         special.capture,
  12057                         .special_capture,
  12058                         &.{Air.Inst.Ref.bool_false},
  12059                         Air.Inst.Ref.bool_false,
  12060                         special.has_tag_capture,
  12061                     );
  12062 
  12063                     try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
  12064                     cases_extra.appendAssumeCapacity(1); // items_len
  12065                     cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len)));
  12066                     cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false));
  12067                     cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
  12068                 }
  12069             },
  12070             else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
  12071                 operand_ty.fmt(mod),
  12072             }),
  12073         };
  12074 
  12075         var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope);
  12076         defer wip_captures.deinit();
  12077 
  12078         case_block.instructions.shrinkRetainingCapacity(0);
  12079         case_block.wip_capture_scope = wip_captures.scope;
  12080 
  12081         if (mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and
  12082             operand_ty.zigTypeTag(mod) == .Enum and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally))
  12083         {
  12084             try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
  12085             const ok = try case_block.addUnOp(.is_named_enum_value, operand);
  12086             try sema.addSafetyCheck(&case_block, ok, .corrupt_switch);
  12087         }
  12088 
  12089         const analyze_body = if (union_originally and !special.is_inline)
  12090             for (seen_enum_fields, 0..) |seen_field, index| {
  12091                 if (seen_field != null) continue;
  12092                 const union_obj = mod.typeToUnion(maybe_union_ty).?;
  12093                 const field_ty = union_obj.fields.values()[index].ty;
  12094                 if (field_ty.zigTypeTag(mod) != .NoReturn) break true;
  12095             } else false
  12096         else
  12097             true;
  12098         if (special.body.len != 0 and err_set and
  12099             try sema.maybeErrorUnwrap(&case_block, special.body, operand))
  12100         {
  12101             // nothing to do here
  12102         } else if (special.body.len != 0 and analyze_body and !special.is_inline) {
  12103             try spa.analyzeProngRuntime(
  12104                 &case_block,
  12105                 .special,
  12106                 special.body,
  12107                 special.capture,
  12108                 .special_capture,
  12109                 undefined, // case_vals may be undefined for special prongs
  12110                 .none,
  12111                 false,
  12112             );
  12113         } else {
  12114             // We still need a terminator in this block, but we have proven
  12115             // that it is unreachable.
  12116             if (case_block.wantSafety()) {
  12117                 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
  12118                 try sema.safetyPanic(&case_block, .corrupt_switch);
  12119             } else {
  12120                 _ = try case_block.addNoOp(.unreach);
  12121             }
  12122         }
  12123 
  12124         try wip_captures.finalize();
  12125 
  12126         if (is_first) {
  12127             final_else_body = case_block.instructions.items;
  12128         } else {
  12129             try sema.air_extra.ensureUnusedCapacity(gpa, prev_then_body.len +
  12130                 @typeInfo(Air.CondBr).Struct.fields.len + case_block.instructions.items.len);
  12131 
  12132             sema.air_instructions.items(.data)[prev_cond_br].pl_op.payload =
  12133                 sema.addExtraAssumeCapacity(Air.CondBr{
  12134                 .then_body_len = @as(u32, @intCast(prev_then_body.len)),
  12135                 .else_body_len = @as(u32, @intCast(case_block.instructions.items.len)),
  12136             });
  12137             sema.air_extra.appendSliceAssumeCapacity(prev_then_body);
  12138             sema.air_extra.appendSliceAssumeCapacity(case_block.instructions.items);
  12139             final_else_body = first_else_body;
  12140         }
  12141     }
  12142 
  12143     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len +
  12144         cases_extra.items.len + final_else_body.len);
  12145 
  12146     _ = try child_block.addInst(.{ .tag = .switch_br, .data = .{ .pl_op = .{
  12147         .operand = operand,
  12148         .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
  12149             .cases_len = @as(u32, @intCast(cases_len)),
  12150             .else_body_len = @as(u32, @intCast(final_else_body.len)),
  12151         }),
  12152     } } });
  12153     sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
  12154     sema.air_extra.appendSliceAssumeCapacity(final_else_body);
  12155 
  12156     return sema.analyzeBlockBody(block, src, &child_block, merges);
  12157 }
  12158 
  12159 const RangeSetUnhandledIterator = struct {
  12160     mod: *Module,
  12161     cur: ?InternPool.Index,
  12162     max: InternPool.Index,
  12163     range_i: usize,
  12164     ranges: []const RangeSet.Range,
  12165     limbs: []math.big.Limb,
  12166 
  12167     const preallocated_limbs = math.big.int.calcTwosCompLimbCount(128);
  12168 
  12169     fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator {
  12170         const mod = sema.mod;
  12171         const int_type = mod.intern_pool.indexToKey(ty.toIntern()).int_type;
  12172         const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits);
  12173         return .{
  12174             .mod = mod,
  12175             .cur = (try ty.minInt(mod, ty)).toIntern(),
  12176             .max = (try ty.maxInt(mod, ty)).toIntern(),
  12177             .range_i = 0,
  12178             .ranges = range_set.ranges.items,
  12179             .limbs = if (needed_limbs > preallocated_limbs)
  12180                 try sema.arena.alloc(math.big.Limb, needed_limbs)
  12181             else
  12182                 &.{},
  12183         };
  12184     }
  12185 
  12186     fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index {
  12187         if (val == it.max) return null;
  12188         const int = it.mod.intern_pool.indexToKey(val).int;
  12189 
  12190         switch (int.storage) {
  12191             inline .u64, .i64 => |val_int| {
  12192                 const next_int = @addWithOverflow(val_int, 1);
  12193                 if (next_int[1] == 0)
  12194                     return (try it.mod.intValue(int.ty.toType(), next_int[0])).toIntern();
  12195             },
  12196             .big_int => {},
  12197             .lazy_align, .lazy_size => unreachable,
  12198         }
  12199 
  12200         var val_space: InternPool.Key.Int.Storage.BigIntSpace = undefined;
  12201         const val_bigint = int.storage.toBigInt(&val_space);
  12202 
  12203         var result_limbs: [preallocated_limbs]math.big.Limb = undefined;
  12204         var result_bigint = math.big.int.Mutable.init(
  12205             if (it.limbs.len > 0) it.limbs else &result_limbs,
  12206             0,
  12207         );
  12208 
  12209         result_bigint.addScalar(val_bigint, 1);
  12210         return (try it.mod.intValue_big(int.ty.toType(), result_bigint.toConst())).toIntern();
  12211     }
  12212 
  12213     fn next(it: *RangeSetUnhandledIterator) !?InternPool.Index {
  12214         var cur = it.cur orelse return null;
  12215         while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) {
  12216             defer it.range_i += 1;
  12217             cur = (try it.addOne(it.ranges[it.range_i].last)) orelse {
  12218                 it.cur = null;
  12219                 return null;
  12220             };
  12221         }
  12222         it.cur = try it.addOne(cur);
  12223         return cur;
  12224     }
  12225 };
  12226 
  12227 const ResolvedSwitchItem = struct {
  12228     ref: Air.Inst.Ref,
  12229     val: InternPool.Index,
  12230 };
  12231 fn resolveSwitchItemVal(
  12232     sema: *Sema,
  12233     block: *Block,
  12234     item_ref: Zir.Inst.Ref,
  12235     /// Coerce `item_ref` to this type.
  12236     coerce_ty: Type,
  12237     switch_node_offset: i32,
  12238     switch_prong_src: Module.SwitchProngSrc,
  12239     range_expand: Module.SwitchProngSrc.RangeExpand,
  12240 ) CompileError!ResolvedSwitchItem {
  12241     const mod = sema.mod;
  12242     const uncoerced_item = try sema.resolveInst(item_ref);
  12243 
  12244     // Constructing a LazySrcLoc is costly because we only have the switch AST node.
  12245     // Only if we know for sure we need to report a compile error do we resolve the
  12246     // full source locations.
  12247 
  12248     const item = sema.coerce(block, coerce_ty, uncoerced_item, .unneeded) catch |err| switch (err) {
  12249         error.NeededSourceLocation => {
  12250             const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand);
  12251             _ = try sema.coerce(block, coerce_ty, uncoerced_item, src);
  12252             unreachable;
  12253         },
  12254         else => |e| return e,
  12255     };
  12256 
  12257     const maybe_lazy = sema.resolveConstValue(block, .unneeded, item, "") catch |err| switch (err) {
  12258         error.NeededSourceLocation => {
  12259             const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand);
  12260             _ = try sema.resolveConstValue(block, src, item, "switch prong values must be comptime-known");
  12261             unreachable;
  12262         },
  12263         else => |e| return e,
  12264     };
  12265 
  12266     const val = try sema.resolveLazyValue(maybe_lazy);
  12267     const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: {
  12268         break :blk try sema.addConstant(val);
  12269     } else item;
  12270 
  12271     return .{ .ref = new_item, .val = val.toIntern() };
  12272 }
  12273 
  12274 fn validateSwitchRange(
  12275     sema: *Sema,
  12276     block: *Block,
  12277     range_set: *RangeSet,
  12278     first_ref: Zir.Inst.Ref,
  12279     last_ref: Zir.Inst.Ref,
  12280     operand_ty: Type,
  12281     src_node_offset: i32,
  12282     switch_prong_src: Module.SwitchProngSrc,
  12283 ) CompileError![2]Air.Inst.Ref {
  12284     const mod = sema.mod;
  12285     const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, src_node_offset, switch_prong_src, .first);
  12286     const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, src_node_offset, switch_prong_src, .last);
  12287     if (try first.val.toValue().compareAll(.gt, last.val.toValue(), operand_ty, mod)) {
  12288         const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), src_node_offset, .first);
  12289         return sema.fail(block, src, "range start value is greater than the end value", .{});
  12290     }
  12291     const maybe_prev_src = try range_set.add(first.val, last.val, switch_prong_src);
  12292     try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
  12293     return .{ first.ref, last.ref };
  12294 }
  12295 
  12296 fn validateSwitchItemInt(
  12297     sema: *Sema,
  12298     block: *Block,
  12299     range_set: *RangeSet,
  12300     item_ref: Zir.Inst.Ref,
  12301     operand_ty: Type,
  12302     src_node_offset: i32,
  12303     switch_prong_src: Module.SwitchProngSrc,
  12304 ) CompileError!Air.Inst.Ref {
  12305     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
  12306     const maybe_prev_src = try range_set.add(item.val, item.val, switch_prong_src);
  12307     try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
  12308     return item.ref;
  12309 }
  12310 
  12311 fn validateSwitchItemEnum(
  12312     sema: *Sema,
  12313     block: *Block,
  12314     seen_fields: []?Module.SwitchProngSrc,
  12315     range_set: *RangeSet,
  12316     item_ref: Zir.Inst.Ref,
  12317     operand_ty: Type,
  12318     src_node_offset: i32,
  12319     switch_prong_src: Module.SwitchProngSrc,
  12320 ) CompileError!Air.Inst.Ref {
  12321     const ip = &sema.mod.intern_pool;
  12322     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
  12323     const int = ip.indexToKey(item.val).enum_tag.int;
  12324     const field_index = ip.indexToKey(ip.typeOf(item.val)).enum_type.tagValueIndex(ip, int) orelse {
  12325         const maybe_prev_src = try range_set.add(int, int, switch_prong_src);
  12326         try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
  12327         return item.ref;
  12328     };
  12329     const maybe_prev_src = seen_fields[field_index];
  12330     seen_fields[field_index] = switch_prong_src;
  12331     try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
  12332     return item.ref;
  12333 }
  12334 
  12335 fn validateSwitchItemError(
  12336     sema: *Sema,
  12337     block: *Block,
  12338     seen_errors: *SwitchErrorSet,
  12339     item_ref: Zir.Inst.Ref,
  12340     operand_ty: Type,
  12341     src_node_offset: i32,
  12342     switch_prong_src: Module.SwitchProngSrc,
  12343 ) CompileError!Air.Inst.Ref {
  12344     const ip = &sema.mod.intern_pool;
  12345     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
  12346     const error_name = ip.indexToKey(item.val).err.name;
  12347     const maybe_prev_src = if (try seen_errors.fetchPut(error_name, switch_prong_src)) |prev|
  12348         prev.value
  12349     else
  12350         null;
  12351     try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
  12352     return item.ref;
  12353 }
  12354 
  12355 fn validateSwitchDupe(
  12356     sema: *Sema,
  12357     block: *Block,
  12358     maybe_prev_src: ?Module.SwitchProngSrc,
  12359     switch_prong_src: Module.SwitchProngSrc,
  12360     src_node_offset: i32,
  12361 ) CompileError!void {
  12362     const prev_prong_src = maybe_prev_src orelse return;
  12363     const mod = sema.mod;
  12364     const block_src_decl = mod.declPtr(block.src_decl);
  12365     const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none);
  12366     const prev_src = prev_prong_src.resolve(mod, block_src_decl, src_node_offset, .none);
  12367     const msg = msg: {
  12368         const msg = try sema.errMsg(
  12369             block,
  12370             src,
  12371             "duplicate switch value",
  12372             .{},
  12373         );
  12374         errdefer msg.destroy(sema.gpa);
  12375         try sema.errNote(
  12376             block,
  12377             prev_src,
  12378             msg,
  12379             "previous value here",
  12380             .{},
  12381         );
  12382         break :msg msg;
  12383     };
  12384     return sema.failWithOwnedErrorMsg(msg);
  12385 }
  12386 
  12387 fn validateSwitchItemBool(
  12388     sema: *Sema,
  12389     block: *Block,
  12390     true_count: *u8,
  12391     false_count: *u8,
  12392     item_ref: Zir.Inst.Ref,
  12393     src_node_offset: i32,
  12394     switch_prong_src: Module.SwitchProngSrc,
  12395 ) CompileError!Air.Inst.Ref {
  12396     const mod = sema.mod;
  12397     const item = try sema.resolveSwitchItemVal(block, item_ref, Type.bool, src_node_offset, switch_prong_src, .none);
  12398     if (item.val.toValue().toBool()) {
  12399         true_count.* += 1;
  12400     } else {
  12401         false_count.* += 1;
  12402     }
  12403     if (true_count.* > 1 or false_count.* > 1) {
  12404         const block_src_decl = sema.mod.declPtr(block.src_decl);
  12405         const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none);
  12406         return sema.fail(block, src, "duplicate switch value", .{});
  12407     }
  12408     return item.ref;
  12409 }
  12410 
  12411 const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, Module.SwitchProngSrc);
  12412 
  12413 fn validateSwitchItemSparse(
  12414     sema: *Sema,
  12415     block: *Block,
  12416     seen_values: *ValueSrcMap,
  12417     item_ref: Zir.Inst.Ref,
  12418     operand_ty: Type,
  12419     src_node_offset: i32,
  12420     switch_prong_src: Module.SwitchProngSrc,
  12421 ) CompileError!Air.Inst.Ref {
  12422     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
  12423     const kv = (try seen_values.fetchPut(sema.gpa, item.val, switch_prong_src)) orelse return item.ref;
  12424     try sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset);
  12425     unreachable;
  12426 }
  12427 
  12428 fn validateSwitchNoRange(
  12429     sema: *Sema,
  12430     block: *Block,
  12431     ranges_len: u32,
  12432     operand_ty: Type,
  12433     src_node_offset: i32,
  12434 ) CompileError!void {
  12435     if (ranges_len == 0)
  12436         return;
  12437 
  12438     const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
  12439     const range_src: LazySrcLoc = .{ .node_offset_switch_range = src_node_offset };
  12440 
  12441     const msg = msg: {
  12442         const msg = try sema.errMsg(
  12443             block,
  12444             operand_src,
  12445             "ranges not allowed when switching on type '{}'",
  12446             .{operand_ty.fmt(sema.mod)},
  12447         );
  12448         errdefer msg.destroy(sema.gpa);
  12449         try sema.errNote(
  12450             block,
  12451             range_src,
  12452             msg,
  12453             "range here",
  12454             .{},
  12455         );
  12456         break :msg msg;
  12457     };
  12458     return sema.failWithOwnedErrorMsg(msg);
  12459 }
  12460 
  12461 fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !bool {
  12462     const mod = sema.mod;
  12463     if (!mod.backendSupportsFeature(.panic_unwrap_error)) return false;
  12464 
  12465     const tags = sema.code.instructions.items(.tag);
  12466     for (body) |inst| {
  12467         switch (tags[inst]) {
  12468             .@"unreachable" => if (!block.wantSafety()) return false,
  12469             .save_err_ret_index,
  12470             .dbg_block_begin,
  12471             .dbg_block_end,
  12472             .dbg_stmt,
  12473             .str,
  12474             .as_node,
  12475             .panic,
  12476             .field_val,
  12477             => {},
  12478             else => return false,
  12479         }
  12480     }
  12481 
  12482     for (body) |inst| {
  12483         const air_inst = switch (tags[inst]) {
  12484             .dbg_block_begin,
  12485             .dbg_block_end,
  12486             => continue,
  12487             .dbg_stmt => {
  12488                 try sema.zirDbgStmt(block, inst);
  12489                 continue;
  12490             },
  12491             .save_err_ret_index => {
  12492                 try sema.zirSaveErrRetIndex(block, inst);
  12493                 continue;
  12494             },
  12495             .str => try sema.zirStr(block, inst),
  12496             .as_node => try sema.zirAsNode(block, inst),
  12497             .field_val => try sema.zirFieldVal(block, inst),
  12498             .@"unreachable" => {
  12499                 if (!mod.comp.formatted_panics) {
  12500                     try sema.safetyPanic(block, .unwrap_error);
  12501                     return true;
  12502                 }
  12503 
  12504                 const panic_fn = try sema.getBuiltin("panicUnwrapError");
  12505                 const err_return_trace = try sema.getErrorReturnTrace(block);
  12506                 const args: [2]Air.Inst.Ref = .{ err_return_trace, operand };
  12507                 try sema.callBuiltin(block, panic_fn, .auto, &args);
  12508                 return true;
  12509             },
  12510             .panic => {
  12511                 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  12512                 const msg_inst = try sema.resolveInst(inst_data.operand);
  12513 
  12514                 const panic_fn = try sema.getBuiltin("panic");
  12515                 const err_return_trace = try sema.getErrorReturnTrace(block);
  12516                 const args: [3]Air.Inst.Ref = .{ msg_inst, err_return_trace, .null_value };
  12517                 try sema.callBuiltin(block, panic_fn, .auto, &args);
  12518                 return true;
  12519             },
  12520             else => unreachable,
  12521         };
  12522         if (sema.typeOf(air_inst).isNoReturn(mod))
  12523             return true;
  12524         sema.inst_map.putAssumeCapacity(inst, air_inst);
  12525     }
  12526     unreachable;
  12527 }
  12528 
  12529 fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void {
  12530     const mod = sema.mod;
  12531     const index = Zir.refToIndex(cond) orelse return;
  12532     if (sema.code.instructions.items(.tag)[index] != .is_non_err) return;
  12533 
  12534     const err_inst_data = sema.code.instructions.items(.data)[index].un_node;
  12535     const err_operand = try sema.resolveInst(err_inst_data.operand);
  12536     const operand_ty = sema.typeOf(err_operand);
  12537     if (operand_ty.zigTypeTag(mod) == .ErrorSet) {
  12538         try sema.maybeErrorUnwrapComptime(block, body, err_operand);
  12539         return;
  12540     }
  12541     if (try sema.resolveDefinedValue(block, cond_src, err_operand)) |val| {
  12542         if (!operand_ty.isError(mod)) return;
  12543         if (val.getErrorName(mod) == .none) return;
  12544         try sema.maybeErrorUnwrapComptime(block, body, err_operand);
  12545     }
  12546 }
  12547 
  12548 fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !void {
  12549     const tags = sema.code.instructions.items(.tag);
  12550     const inst = for (body) |inst| {
  12551         switch (tags[inst]) {
  12552             .dbg_block_begin,
  12553             .dbg_block_end,
  12554             .dbg_stmt,
  12555             .save_err_ret_index,
  12556             => {},
  12557             .@"unreachable" => break inst,
  12558             else => return,
  12559         }
  12560     } else return;
  12561     const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
  12562     const src = inst_data.src();
  12563 
  12564     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
  12565         if (val.getErrorName(sema.mod).unwrap()) |name| {
  12566             return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&sema.mod.intern_pool)});
  12567         }
  12568     }
  12569 }
  12570 
  12571 fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  12572     const mod = sema.mod;
  12573     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  12574     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  12575     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  12576     const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  12577     const unresolved_ty = try sema.resolveType(block, ty_src, extra.lhs);
  12578     const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, "field name must be comptime-known");
  12579     const ty = try sema.resolveTypeFields(unresolved_ty);
  12580     const ip = &mod.intern_pool;
  12581 
  12582     const has_field = hf: {
  12583         switch (ip.indexToKey(ty.toIntern())) {
  12584             .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  12585                 .Slice => {
  12586                     if (ip.stringEqlSlice(field_name, "ptr")) break :hf true;
  12587                     if (ip.stringEqlSlice(field_name, "len")) break :hf true;
  12588                     break :hf false;
  12589                 },
  12590                 else => {},
  12591             },
  12592             .anon_struct_type => |anon_struct| {
  12593                 if (anon_struct.names.len != 0) {
  12594                     break :hf mem.indexOfScalar(InternPool.NullTerminatedString, anon_struct.names, field_name) != null;
  12595                 } else {
  12596                     const field_index = field_name.toUnsigned(ip) orelse break :hf false;
  12597                     break :hf field_index < ty.structFieldCount(mod);
  12598                 }
  12599             },
  12600             .struct_type => |struct_type| {
  12601                 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse break :hf false;
  12602                 assert(struct_obj.haveFieldTypes());
  12603                 break :hf struct_obj.fields.contains(field_name);
  12604             },
  12605             .union_type => |union_type| {
  12606                 const union_obj = mod.unionPtr(union_type.index);
  12607                 assert(union_obj.haveFieldTypes());
  12608                 break :hf union_obj.fields.contains(field_name);
  12609             },
  12610             .enum_type => |enum_type| {
  12611                 break :hf enum_type.nameIndex(ip, field_name) != null;
  12612             },
  12613             .array_type => break :hf ip.stringEqlSlice(field_name, "len"),
  12614             else => {},
  12615         }
  12616         return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{
  12617             ty.fmt(mod),
  12618         });
  12619     };
  12620     if (has_field) {
  12621         return Air.Inst.Ref.bool_true;
  12622     } else {
  12623         return Air.Inst.Ref.bool_false;
  12624     }
  12625 }
  12626 
  12627 fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  12628     const mod = sema.mod;
  12629     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  12630     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  12631     const src = inst_data.src();
  12632     const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  12633     const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  12634     const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
  12635     const decl_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, "decl name must be comptime-known");
  12636 
  12637     try sema.checkNamespaceType(block, lhs_src, container_type);
  12638 
  12639     const namespace = container_type.getNamespaceIndex(mod).unwrap() orelse
  12640         return Air.Inst.Ref.bool_false;
  12641     if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
  12642         const decl = mod.declPtr(decl_index);
  12643         if (decl.is_pub or decl.getFileScope(mod) == block.getFileScope(mod)) {
  12644             return Air.Inst.Ref.bool_true;
  12645         }
  12646     }
  12647     return Air.Inst.Ref.bool_false;
  12648 }
  12649 
  12650 fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  12651     const tracy = trace(@src());
  12652     defer tracy.end();
  12653 
  12654     const mod = sema.mod;
  12655     const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
  12656     const operand_src = inst_data.src();
  12657     const operand = inst_data.get(sema.code);
  12658 
  12659     const result = mod.importFile(block.getFileScope(mod), operand) catch |err| switch (err) {
  12660         error.ImportOutsidePkgPath => {
  12661             return sema.fail(block, operand_src, "import of file outside package path: '{s}'", .{operand});
  12662         },
  12663         error.PackageNotFound => {
  12664             const name = try block.getFileScope(mod).pkg.getName(sema.gpa, mod.*);
  12665             defer sema.gpa.free(name);
  12666             return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, name });
  12667         },
  12668         else => {
  12669             // TODO: these errors are file system errors; make sure an update() will
  12670             // retry this and not cache the file system error, which may be transient.
  12671             return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });
  12672         },
  12673     };
  12674     try mod.semaFile(result.file);
  12675     const file_root_decl_index = result.file.root_decl.unwrap().?;
  12676     const file_root_decl = mod.declPtr(file_root_decl_index);
  12677     try mod.declareDeclDependency(sema.owner_decl_index, file_root_decl_index);
  12678     return sema.addConstant(file_root_decl.val);
  12679 }
  12680 
  12681 fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  12682     const tracy = trace(@src());
  12683     defer tracy.end();
  12684 
  12685     const mod = sema.mod;
  12686     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  12687     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  12688     const name = try sema.resolveConstString(block, operand_src, inst_data.operand, "file path name must be comptime-known");
  12689 
  12690     const embed_file = mod.embedFile(block.getFileScope(mod), name) catch |err| switch (err) {
  12691         error.ImportOutsidePkgPath => {
  12692             return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name});
  12693         },
  12694         else => {
  12695             // TODO: these errors are file system errors; make sure an update() will
  12696             // retry this and not cache the file system error, which may be transient.
  12697             return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ name, @errorName(err) });
  12698         },
  12699     };
  12700 
  12701     var anon_decl = try block.startAnonDecl();
  12702     defer anon_decl.deinit();
  12703 
  12704     // TODO instead of using `.bytes`, create a new value tag for pointing at
  12705     // a `*Module.EmbedFile`. The purpose of this would be:
  12706     // - If only the length is read and the bytes are not inspected by comptime code,
  12707     //   there can be an optimization where the codegen backend does a copy_file_range
  12708     //   into the final binary, and never loads the data into memory.
  12709     // - When a Decl is destroyed, it can free the `*Module.EmbedFile`.
  12710     const ty = try mod.arrayType(.{
  12711         .len = embed_file.bytes.len,
  12712         .sentinel = .zero_u8,
  12713         .child = .u8_type,
  12714     });
  12715     embed_file.owner_decl = try anon_decl.finish(
  12716         ty,
  12717         (try mod.intern(.{ .aggregate = .{
  12718             .ty = ty.toIntern(),
  12719             .storage = .{ .bytes = embed_file.bytes },
  12720         } })).toValue(),
  12721         .none, // default alignment
  12722     );
  12723 
  12724     return sema.analyzeDeclRef(embed_file.owner_decl);
  12725 }
  12726 
  12727 fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  12728     const mod = sema.mod;
  12729     const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
  12730     const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
  12731     _ = try mod.getErrorValue(name);
  12732     const error_set_type = try mod.singleErrorSetType(name);
  12733     return sema.addConstant((try mod.intern(.{ .err = .{
  12734         .ty = error_set_type.toIntern(),
  12735         .name = name,
  12736     } })).toValue());
  12737 }
  12738 
  12739 fn zirShl(
  12740     sema: *Sema,
  12741     block: *Block,
  12742     inst: Zir.Inst.Index,
  12743     air_tag: Air.Inst.Tag,
  12744 ) CompileError!Air.Inst.Ref {
  12745     const tracy = trace(@src());
  12746     defer tracy.end();
  12747 
  12748     const mod = sema.mod;
  12749     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  12750     const src = inst_data.src();
  12751     sema.src = src;
  12752     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  12753     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  12754     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  12755     const lhs = try sema.resolveInst(extra.lhs);
  12756     const rhs = try sema.resolveInst(extra.rhs);
  12757     const lhs_ty = sema.typeOf(lhs);
  12758     const rhs_ty = sema.typeOf(rhs);
  12759     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  12760 
  12761     const scalar_ty = lhs_ty.scalarType(mod);
  12762     const scalar_rhs_ty = rhs_ty.scalarType(mod);
  12763 
  12764     // TODO coerce rhs if air_tag is not shl_sat
  12765     const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, scalar_rhs_ty);
  12766 
  12767     const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(lhs);
  12768     const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(rhs);
  12769 
  12770     if (maybe_rhs_val) |rhs_val| {
  12771         if (rhs_val.isUndef(mod)) {
  12772             return sema.addConstUndef(sema.typeOf(lhs));
  12773         }
  12774         // If rhs is 0, return lhs without doing any calculations.
  12775         if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  12776             return lhs;
  12777         }
  12778         if (scalar_ty.zigTypeTag(mod) != .ComptimeInt and air_tag != .shl_sat) {
  12779             const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits);
  12780             if (rhs_ty.zigTypeTag(mod) == .Vector) {
  12781                 var i: usize = 0;
  12782                 while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
  12783                     const rhs_elem = try rhs_val.elemValue(mod, i);
  12784                     if (rhs_elem.compareHetero(.gte, bit_value, mod)) {
  12785                         return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
  12786                             rhs_elem.fmtValue(scalar_ty, mod),
  12787                             i,
  12788                             scalar_ty.fmt(mod),
  12789                         });
  12790                     }
  12791                 }
  12792             } else if (rhs_val.compareHetero(.gte, bit_value, mod)) {
  12793                 return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{
  12794                     rhs_val.fmtValue(scalar_ty, mod),
  12795                     scalar_ty.fmt(mod),
  12796                 });
  12797             }
  12798         }
  12799         if (rhs_ty.zigTypeTag(mod) == .Vector) {
  12800             var i: usize = 0;
  12801             while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
  12802                 const rhs_elem = try rhs_val.elemValue(mod, i);
  12803                 if (rhs_elem.compareHetero(.lt, try mod.intValue(scalar_rhs_ty, 0), mod)) {
  12804                     return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{
  12805                         rhs_elem.fmtValue(scalar_ty, mod),
  12806                         i,
  12807                     });
  12808                 }
  12809             }
  12810         } else if (rhs_val.compareHetero(.lt, try mod.intValue(rhs_ty, 0), mod)) {
  12811             return sema.fail(block, rhs_src, "shift by negative amount '{}'", .{
  12812                 rhs_val.fmtValue(scalar_ty, mod),
  12813             });
  12814         }
  12815     }
  12816 
  12817     const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
  12818         if (lhs_val.isUndef(mod)) return sema.addConstUndef(lhs_ty);
  12819         const rhs_val = maybe_rhs_val orelse {
  12820             if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
  12821                 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
  12822             }
  12823             break :rs rhs_src;
  12824         };
  12825 
  12826         const val = switch (air_tag) {
  12827             .shl_exact => val: {
  12828                 const shifted = try lhs_val.shlWithOverflow(rhs_val, lhs_ty, sema.arena, mod);
  12829                 if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
  12830                     break :val shifted.wrapped_result;
  12831                 }
  12832                 if (shifted.overflow_bit.compareAllWithZero(.eq, mod)) {
  12833                     break :val shifted.wrapped_result;
  12834                 }
  12835                 return sema.fail(block, src, "operation caused overflow", .{});
  12836             },
  12837 
  12838             .shl_sat => if (scalar_ty.zigTypeTag(mod) == .ComptimeInt)
  12839                 try lhs_val.shl(rhs_val, lhs_ty, sema.arena, mod)
  12840             else
  12841                 try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, mod),
  12842 
  12843             .shl => if (scalar_ty.zigTypeTag(mod) == .ComptimeInt)
  12844                 try lhs_val.shl(rhs_val, lhs_ty, sema.arena, mod)
  12845             else
  12846                 try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, mod),
  12847 
  12848             else => unreachable,
  12849         };
  12850 
  12851         return sema.addConstant(val);
  12852     } else lhs_src;
  12853 
  12854     const new_rhs = if (air_tag == .shl_sat) rhs: {
  12855         // Limit the RHS type for saturating shl to be an integer as small as the LHS.
  12856         if (rhs_is_comptime_int or
  12857             scalar_rhs_ty.intInfo(mod).bits > scalar_ty.intInfo(mod).bits)
  12858         {
  12859             const max_int = try sema.addConstant(
  12860                 try lhs_ty.maxInt(mod, lhs_ty),
  12861             );
  12862             const rhs_limited = try sema.analyzeMinMax(block, rhs_src, .min, &.{ rhs, max_int }, &.{ rhs_src, rhs_src });
  12863             break :rhs try sema.intCast(block, src, lhs_ty, rhs_src, rhs_limited, rhs_src, false);
  12864         } else {
  12865             break :rhs rhs;
  12866         }
  12867     } else rhs;
  12868 
  12869     try sema.requireRuntimeBlock(block, src, runtime_src);
  12870     if (block.wantSafety()) {
  12871         const bit_count = scalar_ty.intInfo(mod).bits;
  12872         if (!std.math.isPowerOfTwo(bit_count)) {
  12873             const bit_count_val = try mod.intValue(scalar_rhs_ty, bit_count);
  12874             const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: {
  12875                 const bit_count_inst = try sema.addConstant(try sema.splat(rhs_ty, bit_count_val));
  12876                 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
  12877                 break :ok try block.addInst(.{
  12878                     .tag = .reduce,
  12879                     .data = .{ .reduce = .{
  12880                         .operand = lt,
  12881                         .operation = .And,
  12882                     } },
  12883                 });
  12884             } else ok: {
  12885                 const bit_count_inst = try sema.addConstant(bit_count_val);
  12886                 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
  12887             };
  12888             try sema.addSafetyCheck(block, ok, .shift_rhs_too_big);
  12889         }
  12890 
  12891         if (air_tag == .shl_exact) {
  12892             const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty);
  12893             const op_ov = try block.addInst(.{
  12894                 .tag = .shl_with_overflow,
  12895                 .data = .{ .ty_pl = .{
  12896                     .ty = try sema.addType(op_ov_tuple_ty),
  12897                     .payload = try sema.addExtra(Air.Bin{
  12898                         .lhs = lhs,
  12899                         .rhs = rhs,
  12900                     }),
  12901                 } },
  12902             });
  12903             const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);
  12904             const any_ov_bit = if (lhs_ty.zigTypeTag(mod) == .Vector)
  12905                 try block.addInst(.{
  12906                     .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  12907                     .data = .{ .reduce = .{
  12908                         .operand = ov_bit,
  12909                         .operation = .Or,
  12910                     } },
  12911                 })
  12912             else
  12913                 ov_bit;
  12914             const zero_ov = try sema.addConstant(try mod.intValue(Type.u1, 0));
  12915             const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
  12916 
  12917             try sema.addSafetyCheck(block, no_ov, .shl_overflow);
  12918             return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
  12919         }
  12920     }
  12921     return block.addBinOp(air_tag, lhs, new_rhs);
  12922 }
  12923 
  12924 fn zirShr(
  12925     sema: *Sema,
  12926     block: *Block,
  12927     inst: Zir.Inst.Index,
  12928     air_tag: Air.Inst.Tag,
  12929 ) CompileError!Air.Inst.Ref {
  12930     const tracy = trace(@src());
  12931     defer tracy.end();
  12932 
  12933     const mod = sema.mod;
  12934     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  12935     const src = inst_data.src();
  12936     sema.src = src;
  12937     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  12938     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  12939     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  12940     const lhs = try sema.resolveInst(extra.lhs);
  12941     const rhs = try sema.resolveInst(extra.rhs);
  12942     const lhs_ty = sema.typeOf(lhs);
  12943     const rhs_ty = sema.typeOf(rhs);
  12944     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  12945     const scalar_ty = lhs_ty.scalarType(mod);
  12946 
  12947     const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(lhs);
  12948     const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(rhs);
  12949 
  12950     const runtime_src = if (maybe_rhs_val) |rhs_val| rs: {
  12951         if (rhs_val.isUndef(mod)) {
  12952             return sema.addConstUndef(lhs_ty);
  12953         }
  12954         // If rhs is 0, return lhs without doing any calculations.
  12955         if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  12956             return lhs;
  12957         }
  12958         if (scalar_ty.zigTypeTag(mod) != .ComptimeInt) {
  12959             const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits);
  12960             if (rhs_ty.zigTypeTag(mod) == .Vector) {
  12961                 var i: usize = 0;
  12962                 while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
  12963                     const rhs_elem = try rhs_val.elemValue(mod, i);
  12964                     if (rhs_elem.compareHetero(.gte, bit_value, mod)) {
  12965                         return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
  12966                             rhs_elem.fmtValue(scalar_ty, mod),
  12967                             i,
  12968                             scalar_ty.fmt(mod),
  12969                         });
  12970                     }
  12971                 }
  12972             } else if (rhs_val.compareHetero(.gte, bit_value, mod)) {
  12973                 return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{
  12974                     rhs_val.fmtValue(scalar_ty, mod),
  12975                     scalar_ty.fmt(mod),
  12976                 });
  12977             }
  12978         }
  12979         if (rhs_ty.zigTypeTag(mod) == .Vector) {
  12980             var i: usize = 0;
  12981             while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
  12982                 const rhs_elem = try rhs_val.elemValue(mod, i);
  12983                 if (rhs_elem.compareHetero(.lt, try mod.intValue(rhs_ty.childType(mod), 0), mod)) {
  12984                     return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{
  12985                         rhs_elem.fmtValue(scalar_ty, mod),
  12986                         i,
  12987                     });
  12988                 }
  12989             }
  12990         } else if (rhs_val.compareHetero(.lt, try mod.intValue(rhs_ty, 0), mod)) {
  12991             return sema.fail(block, rhs_src, "shift by negative amount '{}'", .{
  12992                 rhs_val.fmtValue(scalar_ty, mod),
  12993             });
  12994         }
  12995         if (maybe_lhs_val) |lhs_val| {
  12996             if (lhs_val.isUndef(mod)) {
  12997                 return sema.addConstUndef(lhs_ty);
  12998             }
  12999             if (air_tag == .shr_exact) {
  13000                 // Detect if any ones would be shifted out.
  13001                 const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, mod);
  13002                 if (!(try truncated.compareAllWithZeroAdvanced(.eq, sema))) {
  13003                     return sema.fail(block, src, "exact shift shifted out 1 bits", .{});
  13004                 }
  13005             }
  13006             const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, mod);
  13007             return sema.addConstant(val);
  13008         } else {
  13009             break :rs lhs_src;
  13010         }
  13011     } else rhs_src;
  13012 
  13013     if (maybe_rhs_val == null and scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
  13014         return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
  13015     }
  13016 
  13017     try sema.requireRuntimeBlock(block, src, runtime_src);
  13018     const result = try block.addBinOp(air_tag, lhs, rhs);
  13019     if (block.wantSafety()) {
  13020         const bit_count = scalar_ty.intInfo(mod).bits;
  13021         if (!std.math.isPowerOfTwo(bit_count)) {
  13022             const bit_count_val = try mod.intValue(rhs_ty.scalarType(mod), bit_count);
  13023 
  13024             const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: {
  13025                 const bit_count_inst = try sema.addConstant(try sema.splat(rhs_ty, bit_count_val));
  13026                 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
  13027                 break :ok try block.addInst(.{
  13028                     .tag = .reduce,
  13029                     .data = .{ .reduce = .{
  13030                         .operand = lt,
  13031                         .operation = .And,
  13032                     } },
  13033                 });
  13034             } else ok: {
  13035                 const bit_count_inst = try sema.addConstant(bit_count_val);
  13036                 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
  13037             };
  13038             try sema.addSafetyCheck(block, ok, .shift_rhs_too_big);
  13039         }
  13040 
  13041         if (air_tag == .shr_exact) {
  13042             const back = try block.addBinOp(.shl, result, rhs);
  13043 
  13044             const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: {
  13045                 const eql = try block.addCmpVector(lhs, back, .eq);
  13046                 break :ok try block.addInst(.{
  13047                     .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  13048                     .data = .{ .reduce = .{
  13049                         .operand = eql,
  13050                         .operation = .And,
  13051                     } },
  13052                 });
  13053             } else try block.addBinOp(.cmp_eq, lhs, back);
  13054             try sema.addSafetyCheck(block, ok, .shr_overflow);
  13055         }
  13056     }
  13057     return result;
  13058 }
  13059 
  13060 fn zirBitwise(
  13061     sema: *Sema,
  13062     block: *Block,
  13063     inst: Zir.Inst.Index,
  13064     air_tag: Air.Inst.Tag,
  13065 ) CompileError!Air.Inst.Ref {
  13066     const tracy = trace(@src());
  13067     defer tracy.end();
  13068 
  13069     const mod = sema.mod;
  13070     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  13071     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  13072     sema.src = src;
  13073     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  13074     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  13075     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13076     const lhs = try sema.resolveInst(extra.lhs);
  13077     const rhs = try sema.resolveInst(extra.rhs);
  13078     const lhs_ty = sema.typeOf(lhs);
  13079     const rhs_ty = sema.typeOf(rhs);
  13080     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  13081 
  13082     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  13083     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } });
  13084     const scalar_type = resolved_type.scalarType(mod);
  13085     const scalar_tag = scalar_type.zigTypeTag(mod);
  13086 
  13087     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  13088     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  13089 
  13090     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  13091 
  13092     if (!is_int) {
  13093         return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag(mod)), @tagName(rhs_ty.zigTypeTag(mod)) });
  13094     }
  13095 
  13096     const runtime_src = runtime: {
  13097         // TODO: ask the linker what kind of relocations are available, and
  13098         // in some cases emit a Value that means "this decl's address AND'd with this operand".
  13099         if (try sema.resolveMaybeUndefValIntable(casted_lhs)) |lhs_val| {
  13100             if (try sema.resolveMaybeUndefValIntable(casted_rhs)) |rhs_val| {
  13101                 const result_val = switch (air_tag) {
  13102                     .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, mod),
  13103                     .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, mod),
  13104                     .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, mod),
  13105                     else => unreachable,
  13106                 };
  13107                 return sema.addConstant(result_val);
  13108             } else {
  13109                 break :runtime rhs_src;
  13110             }
  13111         } else {
  13112             break :runtime lhs_src;
  13113         }
  13114     };
  13115 
  13116     try sema.requireRuntimeBlock(block, src, runtime_src);
  13117     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  13118 }
  13119 
  13120 fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13121     const tracy = trace(@src());
  13122     defer tracy.end();
  13123 
  13124     const mod = sema.mod;
  13125     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  13126     const src = inst_data.src();
  13127     const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
  13128 
  13129     const operand = try sema.resolveInst(inst_data.operand);
  13130     const operand_type = sema.typeOf(operand);
  13131     const scalar_type = operand_type.scalarType(mod);
  13132 
  13133     if (scalar_type.zigTypeTag(mod) != .Int) {
  13134         return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{
  13135             operand_type.fmt(mod),
  13136         });
  13137     }
  13138 
  13139     if (try sema.resolveMaybeUndefVal(operand)) |val| {
  13140         if (val.isUndef(mod)) {
  13141             return sema.addConstUndef(operand_type);
  13142         } else if (operand_type.zigTypeTag(mod) == .Vector) {
  13143             const vec_len = try sema.usizeCast(block, operand_src, operand_type.vectorLen(mod));
  13144             const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  13145             for (elems, 0..) |*elem, i| {
  13146                 const elem_val = try val.elemValue(mod, i);
  13147                 elem.* = try (try elem_val.bitwiseNot(scalar_type, sema.arena, mod)).intern(scalar_type, mod);
  13148             }
  13149             return sema.addConstant((try mod.intern(.{ .aggregate = .{
  13150                 .ty = operand_type.toIntern(),
  13151                 .storage = .{ .elems = elems },
  13152             } })).toValue());
  13153         } else {
  13154             const result_val = try val.bitwiseNot(operand_type, sema.arena, mod);
  13155             return sema.addConstant(result_val);
  13156         }
  13157     }
  13158 
  13159     try sema.requireRuntimeBlock(block, src, null);
  13160     return block.addTyOp(.not, operand_type, operand);
  13161 }
  13162 
  13163 fn analyzeTupleCat(
  13164     sema: *Sema,
  13165     block: *Block,
  13166     src_node: i32,
  13167     lhs: Air.Inst.Ref,
  13168     rhs: Air.Inst.Ref,
  13169 ) CompileError!Air.Inst.Ref {
  13170     const mod = sema.mod;
  13171     const lhs_ty = sema.typeOf(lhs);
  13172     const rhs_ty = sema.typeOf(rhs);
  13173     const src = LazySrcLoc.nodeOffset(src_node);
  13174     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
  13175     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
  13176 
  13177     const lhs_len = lhs_ty.structFieldCount(mod);
  13178     const rhs_len = rhs_ty.structFieldCount(mod);
  13179     const dest_fields = lhs_len + rhs_len;
  13180 
  13181     if (dest_fields == 0) {
  13182         return sema.addConstant(Value.empty_struct);
  13183     }
  13184     if (lhs_len == 0) {
  13185         return rhs;
  13186     }
  13187     if (rhs_len == 0) {
  13188         return lhs;
  13189     }
  13190     const final_len = try sema.usizeCast(block, rhs_src, dest_fields);
  13191 
  13192     const types = try sema.arena.alloc(InternPool.Index, final_len);
  13193     const values = try sema.arena.alloc(InternPool.Index, final_len);
  13194 
  13195     const opt_runtime_src = rs: {
  13196         var runtime_src: ?LazySrcLoc = null;
  13197         var i: u32 = 0;
  13198         while (i < lhs_len) : (i += 1) {
  13199             types[i] = lhs_ty.structFieldType(i, mod).toIntern();
  13200             const default_val = lhs_ty.structFieldDefaultValue(i, mod);
  13201             values[i] = default_val.toIntern();
  13202             const operand_src = lhs_src; // TODO better source location
  13203             if (default_val.toIntern() == .unreachable_value) {
  13204                 runtime_src = operand_src;
  13205                 values[i] = .none;
  13206             }
  13207         }
  13208         i = 0;
  13209         while (i < rhs_len) : (i += 1) {
  13210             types[i + lhs_len] = rhs_ty.structFieldType(i, mod).toIntern();
  13211             const default_val = rhs_ty.structFieldDefaultValue(i, mod);
  13212             values[i + lhs_len] = default_val.toIntern();
  13213             const operand_src = rhs_src; // TODO better source location
  13214             if (default_val.toIntern() == .unreachable_value) {
  13215                 runtime_src = operand_src;
  13216                 values[i + lhs_len] = .none;
  13217             }
  13218         }
  13219         break :rs runtime_src;
  13220     };
  13221 
  13222     const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
  13223         .types = types,
  13224         .values = values,
  13225         .names = &.{},
  13226     } });
  13227 
  13228     const runtime_src = opt_runtime_src orelse {
  13229         const tuple_val = try mod.intern(.{ .aggregate = .{
  13230             .ty = tuple_ty,
  13231             .storage = .{ .elems = values },
  13232         } });
  13233         return sema.addConstant(tuple_val.toValue());
  13234     };
  13235 
  13236     try sema.requireRuntimeBlock(block, src, runtime_src);
  13237 
  13238     const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
  13239     var i: u32 = 0;
  13240     while (i < lhs_len) : (i += 1) {
  13241         const operand_src = lhs_src; // TODO better source location
  13242         element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, i, lhs_ty);
  13243     }
  13244     i = 0;
  13245     while (i < rhs_len) : (i += 1) {
  13246         const operand_src = rhs_src; // TODO better source location
  13247         element_refs[i + lhs_len] =
  13248             try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty);
  13249     }
  13250 
  13251     return block.addAggregateInit(tuple_ty.toType(), element_refs);
  13252 }
  13253 
  13254 fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13255     const tracy = trace(@src());
  13256     defer tracy.end();
  13257 
  13258     const mod = sema.mod;
  13259     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  13260     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13261     const lhs = try sema.resolveInst(extra.lhs);
  13262     const rhs = try sema.resolveInst(extra.rhs);
  13263     const lhs_ty = sema.typeOf(lhs);
  13264     const rhs_ty = sema.typeOf(rhs);
  13265     const src = inst_data.src();
  13266 
  13267     const lhs_is_tuple = lhs_ty.isTuple(mod);
  13268     const rhs_is_tuple = rhs_ty.isTuple(mod);
  13269     if (lhs_is_tuple and rhs_is_tuple) {
  13270         return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs);
  13271     }
  13272 
  13273     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  13274     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  13275 
  13276     const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: {
  13277         if (lhs_is_tuple) break :lhs_info @as(Type.ArrayInfo, undefined);
  13278         return sema.fail(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)});
  13279     };
  13280     const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse {
  13281         assert(!rhs_is_tuple);
  13282         return sema.fail(block, rhs_src, "expected indexable; found '{}'", .{rhs_ty.fmt(mod)});
  13283     };
  13284 
  13285     const resolved_elem_ty = t: {
  13286         var trash_block = block.makeSubBlock();
  13287         trash_block.is_comptime = false;
  13288         defer trash_block.instructions.deinit(sema.gpa);
  13289 
  13290         const instructions = [_]Air.Inst.Ref{
  13291             try trash_block.addBitCast(lhs_info.elem_type, .void_value),
  13292             try trash_block.addBitCast(rhs_info.elem_type, .void_value),
  13293         };
  13294         break :t try sema.resolvePeerTypes(block, src, &instructions, .{
  13295             .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  13296         });
  13297     };
  13298 
  13299     // When there is a sentinel mismatch, no sentinel on the result.
  13300     // Otherwise, use the sentinel value provided by either operand,
  13301     // coercing it to the peer-resolved element type.
  13302     const res_sent_val: ?Value = s: {
  13303         if (lhs_info.sentinel) |lhs_sent_val| {
  13304             const lhs_sent = try sema.addConstant(lhs_sent_val);
  13305             if (rhs_info.sentinel) |rhs_sent_val| {
  13306                 const rhs_sent = try sema.addConstant(rhs_sent_val);
  13307                 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
  13308                 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
  13309                 const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted, "array sentinel value must be comptime-known");
  13310                 const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted, "array sentinel value must be comptime-known");
  13311                 if (try sema.valuesEqual(lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) {
  13312                     break :s lhs_sent_casted_val;
  13313                 } else {
  13314                     break :s null;
  13315                 }
  13316             } else {
  13317                 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
  13318                 const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted, "array sentinel value must be comptime-known");
  13319                 break :s lhs_sent_casted_val;
  13320             }
  13321         } else {
  13322             if (rhs_info.sentinel) |rhs_sent_val| {
  13323                 const rhs_sent = try sema.addConstant(rhs_sent_val);
  13324                 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
  13325                 const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted, "array sentinel value must be comptime-known");
  13326                 break :s rhs_sent_casted_val;
  13327             } else {
  13328                 break :s null;
  13329             }
  13330         }
  13331     };
  13332 
  13333     const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
  13334     const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len);
  13335     const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) {
  13336         error.Overflow => return sema.fail(
  13337             block,
  13338             src,
  13339             "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle",
  13340             .{ lhs_len, rhs_len },
  13341         ),
  13342     };
  13343 
  13344     const result_ty = try mod.arrayType(.{
  13345         .len = result_len,
  13346         .sentinel = if (res_sent_val) |v| v.toIntern() else .none,
  13347         .child = resolved_elem_ty.toIntern(),
  13348     });
  13349     const ptr_addrspace = p: {
  13350         if (lhs_ty.zigTypeTag(mod) == .Pointer) break :p lhs_ty.ptrAddressSpace(mod);
  13351         if (rhs_ty.zigTypeTag(mod) == .Pointer) break :p rhs_ty.ptrAddressSpace(mod);
  13352         break :p null;
  13353     };
  13354 
  13355     const runtime_src = if (switch (lhs_ty.zigTypeTag(mod)) {
  13356         .Array, .Struct => try sema.resolveMaybeUndefVal(lhs),
  13357         .Pointer => try sema.resolveDefinedValue(block, lhs_src, lhs),
  13358         else => unreachable,
  13359     }) |lhs_val| rs: {
  13360         if (switch (rhs_ty.zigTypeTag(mod)) {
  13361             .Array, .Struct => try sema.resolveMaybeUndefVal(rhs),
  13362             .Pointer => try sema.resolveDefinedValue(block, rhs_src, rhs),
  13363             else => unreachable,
  13364         }) |rhs_val| {
  13365             const lhs_sub_val = if (lhs_ty.isSinglePointer(mod))
  13366                 (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
  13367             else
  13368                 lhs_val;
  13369 
  13370             const rhs_sub_val = if (rhs_ty.isSinglePointer(mod))
  13371                 (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).?
  13372             else
  13373                 rhs_val;
  13374 
  13375             const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
  13376             var elem_i: usize = 0;
  13377             while (elem_i < lhs_len) : (elem_i += 1) {
  13378                 const lhs_elem_i = elem_i;
  13379                 const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, mod) else Value.@"unreachable";
  13380                 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(mod, lhs_elem_i) else elem_default_val;
  13381                 const elem_val_inst = try sema.addConstant(elem_val);
  13382                 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded);
  13383                 const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, "");
  13384                 element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod);
  13385             }
  13386             while (elem_i < result_len) : (elem_i += 1) {
  13387                 const rhs_elem_i = elem_i - lhs_len;
  13388                 const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, mod) else Value.@"unreachable";
  13389                 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(mod, rhs_elem_i) else elem_default_val;
  13390                 const elem_val_inst = try sema.addConstant(elem_val);
  13391                 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded);
  13392                 const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, "");
  13393                 element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod);
  13394             }
  13395             return sema.addConstantMaybeRef(block, result_ty, (try mod.intern(.{ .aggregate = .{
  13396                 .ty = result_ty.toIntern(),
  13397                 .storage = .{ .elems = element_vals },
  13398             } })).toValue(), ptr_addrspace != null);
  13399         } else break :rs rhs_src;
  13400     } else lhs_src;
  13401 
  13402     try sema.requireRuntimeBlock(block, src, runtime_src);
  13403 
  13404     if (ptr_addrspace) |ptr_as| {
  13405         const alloc_ty = try mod.ptrType(.{
  13406             .child = result_ty.toIntern(),
  13407             .flags = .{ .address_space = ptr_as },
  13408         });
  13409         const alloc = try block.addTy(.alloc, alloc_ty);
  13410         const elem_ptr_ty = try mod.ptrType(.{
  13411             .child = resolved_elem_ty.toIntern(),
  13412             .flags = .{ .address_space = ptr_as },
  13413         });
  13414 
  13415         var elem_i: usize = 0;
  13416         while (elem_i < lhs_len) : (elem_i += 1) {
  13417             const elem_index = try sema.addIntUnsigned(Type.usize, elem_i);
  13418             const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  13419             const init = try sema.elemVal(block, lhs_src, lhs, elem_index, src, true);
  13420             try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  13421         }
  13422         while (elem_i < result_len) : (elem_i += 1) {
  13423             const elem_index = try sema.addIntUnsigned(Type.usize, elem_i);
  13424             const rhs_index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len);
  13425             const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  13426             const init = try sema.elemVal(block, rhs_src, rhs, rhs_index, src, true);
  13427             try sema.storePtr2(block, src, elem_ptr, src, init, rhs_src, .store);
  13428         }
  13429         if (res_sent_val) |sent_val| {
  13430             const elem_index = try sema.addIntUnsigned(Type.usize, result_len);
  13431             const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  13432             const init = try sema.addConstant(try mod.getCoerced(sent_val, lhs_info.elem_type));
  13433             try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  13434         }
  13435 
  13436         return alloc;
  13437     }
  13438 
  13439     const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
  13440     {
  13441         var elem_i: usize = 0;
  13442         while (elem_i < lhs_len) : (elem_i += 1) {
  13443             const index = try sema.addIntUnsigned(Type.usize, elem_i);
  13444             const init = try sema.elemVal(block, lhs_src, lhs, index, src, true);
  13445             element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, lhs_src);
  13446         }
  13447         while (elem_i < result_len) : (elem_i += 1) {
  13448             const index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len);
  13449             const init = try sema.elemVal(block, rhs_src, rhs, index, src, true);
  13450             element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, rhs_src);
  13451         }
  13452     }
  13453 
  13454     return block.addAggregateInit(result_ty, element_refs);
  13455 }
  13456 
  13457 fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo {
  13458     const mod = sema.mod;
  13459     const operand_ty = sema.typeOf(operand);
  13460     switch (operand_ty.zigTypeTag(mod)) {
  13461         .Array => return operand_ty.arrayInfo(mod),
  13462         .Pointer => {
  13463             const ptr_info = operand_ty.ptrInfo(mod);
  13464             switch (ptr_info.flags.size) {
  13465                 // TODO: in the Many case here this should only work if the type
  13466                 // has a sentinel, and this code should compute the length based
  13467                 // on the sentinel value.
  13468                 .Slice, .Many => {
  13469                     const val = try sema.resolveConstValue(block, src, operand, "slice value being concatenated must be comptime-known");
  13470                     return Type.ArrayInfo{
  13471                         .elem_type = ptr_info.child.toType(),
  13472                         .sentinel = switch (ptr_info.sentinel) {
  13473                             .none => null,
  13474                             else => ptr_info.sentinel.toValue(),
  13475                         },
  13476                         .len = val.sliceLen(mod),
  13477                     };
  13478                 },
  13479                 .One => {
  13480                     if (ptr_info.child.toType().zigTypeTag(mod) == .Array) {
  13481                         return ptr_info.child.toType().arrayInfo(mod);
  13482                     }
  13483                 },
  13484                 .C => {},
  13485             }
  13486         },
  13487         .Struct => {
  13488             if (operand_ty.isTuple(mod) and peer_ty.isIndexable(mod)) {
  13489                 assert(!peer_ty.isTuple(mod));
  13490                 return .{
  13491                     .elem_type = peer_ty.elemType2(mod),
  13492                     .sentinel = null,
  13493                     .len = operand_ty.arrayLen(mod),
  13494                 };
  13495             }
  13496         },
  13497         else => {},
  13498     }
  13499     return null;
  13500 }
  13501 
  13502 fn analyzeTupleMul(
  13503     sema: *Sema,
  13504     block: *Block,
  13505     src_node: i32,
  13506     operand: Air.Inst.Ref,
  13507     factor: usize,
  13508 ) CompileError!Air.Inst.Ref {
  13509     const mod = sema.mod;
  13510     const operand_ty = sema.typeOf(operand);
  13511     const src = LazySrcLoc.nodeOffset(src_node);
  13512     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
  13513     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
  13514 
  13515     const tuple_len = operand_ty.structFieldCount(mod);
  13516     const final_len = std.math.mul(usize, tuple_len, factor) catch
  13517         return sema.fail(block, rhs_src, "operation results in overflow", .{});
  13518 
  13519     if (final_len == 0) {
  13520         return sema.addConstant(Value.empty_struct);
  13521     }
  13522     const types = try sema.arena.alloc(InternPool.Index, final_len);
  13523     const values = try sema.arena.alloc(InternPool.Index, final_len);
  13524 
  13525     const opt_runtime_src = rs: {
  13526         var runtime_src: ?LazySrcLoc = null;
  13527         for (0..tuple_len) |i| {
  13528             types[i] = operand_ty.structFieldType(i, mod).toIntern();
  13529             values[i] = operand_ty.structFieldDefaultValue(i, mod).toIntern();
  13530             const operand_src = lhs_src; // TODO better source location
  13531             if (values[i] == .unreachable_value) {
  13532                 runtime_src = operand_src;
  13533                 values[i] = .none; // TODO don't treat unreachable_value as special
  13534             }
  13535         }
  13536         for (0..factor) |i| {
  13537             mem.copyForwards(InternPool.Index, types[tuple_len * i ..], types[0..tuple_len]);
  13538             mem.copyForwards(InternPool.Index, values[tuple_len * i ..], values[0..tuple_len]);
  13539         }
  13540         break :rs runtime_src;
  13541     };
  13542 
  13543     const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
  13544         .types = types,
  13545         .values = values,
  13546         .names = &.{},
  13547     } });
  13548 
  13549     const runtime_src = opt_runtime_src orelse {
  13550         const tuple_val = try mod.intern(.{ .aggregate = .{
  13551             .ty = tuple_ty,
  13552             .storage = .{ .elems = values },
  13553         } });
  13554         return sema.addConstant(tuple_val.toValue());
  13555     };
  13556 
  13557     try sema.requireRuntimeBlock(block, src, runtime_src);
  13558 
  13559     const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
  13560     var i: u32 = 0;
  13561     while (i < tuple_len) : (i += 1) {
  13562         const operand_src = lhs_src; // TODO better source location
  13563         element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @as(u32, @intCast(i)), operand_ty);
  13564     }
  13565     i = 1;
  13566     while (i < factor) : (i += 1) {
  13567         @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]);
  13568     }
  13569 
  13570     return block.addAggregateInit(tuple_ty.toType(), element_refs);
  13571 }
  13572 
  13573 fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13574     const tracy = trace(@src());
  13575     defer tracy.end();
  13576 
  13577     const mod = sema.mod;
  13578     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  13579     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13580     const lhs = try sema.resolveInst(extra.lhs);
  13581     const lhs_ty = sema.typeOf(lhs);
  13582     const src: LazySrcLoc = inst_data.src();
  13583     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  13584     const operator_src: LazySrcLoc = .{ .node_offset_main_token = inst_data.src_node };
  13585     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  13586 
  13587     if (lhs_ty.isTuple(mod)) {
  13588         // In `**` rhs must be comptime-known, but lhs can be runtime-known
  13589         const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, "array multiplication factor must be comptime-known");
  13590         const factor_casted = try sema.usizeCast(block, rhs_src, factor);
  13591         return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor_casted);
  13592     }
  13593 
  13594     // Analyze the lhs first, to catch the case that someone tried to do exponentiation
  13595     const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse {
  13596         const msg = msg: {
  13597             const msg = try sema.errMsg(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)});
  13598             errdefer msg.destroy(sema.gpa);
  13599             switch (lhs_ty.zigTypeTag(mod)) {
  13600                 .Int, .Float, .ComptimeFloat, .ComptimeInt, .Vector => {
  13601                     try sema.errNote(block, operator_src, msg, "this operator multiplies arrays; use std.math.pow for exponentiation", .{});
  13602                 },
  13603                 else => {},
  13604             }
  13605             break :msg msg;
  13606         };
  13607         return sema.failWithOwnedErrorMsg(msg);
  13608     };
  13609 
  13610     // In `**` rhs must be comptime-known, but lhs can be runtime-known
  13611     const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, "array multiplication factor must be comptime-known");
  13612 
  13613     const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch
  13614         return sema.fail(block, rhs_src, "operation results in overflow", .{});
  13615     const result_len = try sema.usizeCast(block, src, result_len_u64);
  13616 
  13617     const result_ty = try mod.arrayType(.{
  13618         .len = result_len,
  13619         .sentinel = if (lhs_info.sentinel) |s| s.toIntern() else .none,
  13620         .child = lhs_info.elem_type.toIntern(),
  13621     });
  13622 
  13623     const ptr_addrspace = if (lhs_ty.zigTypeTag(mod) == .Pointer) lhs_ty.ptrAddressSpace(mod) else null;
  13624     const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
  13625 
  13626     if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
  13627         const lhs_sub_val = if (lhs_ty.isSinglePointer(mod))
  13628             (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
  13629         else
  13630             lhs_val;
  13631 
  13632         const val = v: {
  13633             // Optimization for the common pattern of a single element repeated N times, such
  13634             // as zero-filling a byte array.
  13635             if (lhs_len == 1 and lhs_info.sentinel == null) {
  13636                 const elem_val = try lhs_sub_val.elemValue(mod, 0);
  13637                 break :v try mod.intern(.{ .aggregate = .{
  13638                     .ty = result_ty.toIntern(),
  13639                     .storage = .{ .repeated_elem = elem_val.toIntern() },
  13640                 } });
  13641             }
  13642 
  13643             const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
  13644             var elem_i: usize = 0;
  13645             while (elem_i < result_len) {
  13646                 var lhs_i: usize = 0;
  13647                 while (lhs_i < lhs_len) : (lhs_i += 1) {
  13648                     const elem_val = try lhs_sub_val.elemValue(mod, lhs_i);
  13649                     element_vals[elem_i] = elem_val.toIntern();
  13650                     elem_i += 1;
  13651                 }
  13652             }
  13653             break :v try mod.intern(.{ .aggregate = .{
  13654                 .ty = result_ty.toIntern(),
  13655                 .storage = .{ .elems = element_vals },
  13656             } });
  13657         };
  13658         return sema.addConstantMaybeRef(block, result_ty, val.toValue(), ptr_addrspace != null);
  13659     }
  13660 
  13661     try sema.requireRuntimeBlock(block, src, lhs_src);
  13662 
  13663     if (ptr_addrspace) |ptr_as| {
  13664         const alloc_ty = try mod.ptrType(.{
  13665             .child = result_ty.toIntern(),
  13666             .flags = .{ .address_space = ptr_as },
  13667         });
  13668         const alloc = try block.addTy(.alloc, alloc_ty);
  13669         const elem_ptr_ty = try mod.ptrType(.{
  13670             .child = lhs_info.elem_type.toIntern(),
  13671             .flags = .{ .address_space = ptr_as },
  13672         });
  13673 
  13674         var elem_i: usize = 0;
  13675         while (elem_i < result_len) {
  13676             var lhs_i: usize = 0;
  13677             while (lhs_i < lhs_len) : (lhs_i += 1) {
  13678                 const elem_index = try sema.addIntUnsigned(Type.usize, elem_i);
  13679                 elem_i += 1;
  13680                 const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i);
  13681                 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  13682                 const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src, true);
  13683                 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  13684             }
  13685         }
  13686         if (lhs_info.sentinel) |sent_val| {
  13687             const elem_index = try sema.addIntUnsigned(Type.usize, result_len);
  13688             const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  13689             const init = try sema.addConstant(sent_val);
  13690             try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  13691         }
  13692 
  13693         return alloc;
  13694     }
  13695 
  13696     const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
  13697     var elem_i: usize = 0;
  13698     while (elem_i < result_len) {
  13699         var lhs_i: usize = 0;
  13700         while (lhs_i < lhs_len) : (lhs_i += 1) {
  13701             const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i);
  13702             const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src, true);
  13703             element_refs[elem_i] = init;
  13704             elem_i += 1;
  13705         }
  13706     }
  13707 
  13708     return block.addAggregateInit(result_ty, element_refs);
  13709 }
  13710 
  13711 fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13712     const mod = sema.mod;
  13713     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  13714     const src = inst_data.src();
  13715     const lhs_src = src;
  13716     const rhs_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
  13717 
  13718     const rhs = try sema.resolveInst(inst_data.operand);
  13719     const rhs_ty = sema.typeOf(rhs);
  13720     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  13721 
  13722     if (rhs_scalar_ty.isUnsignedInt(mod) or switch (rhs_scalar_ty.zigTypeTag(mod)) {
  13723         .Int, .ComptimeInt, .Float, .ComptimeFloat => false,
  13724         else => true,
  13725     }) {
  13726         return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(mod)});
  13727     }
  13728 
  13729     if (rhs_scalar_ty.isAnyFloat()) {
  13730         // We handle float negation here to ensure negative zero is represented in the bits.
  13731         if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| {
  13732             if (rhs_val.isUndef(mod)) return sema.addConstUndef(rhs_ty);
  13733             return sema.addConstant(try rhs_val.floatNeg(rhs_ty, sema.arena, mod));
  13734         }
  13735         try sema.requireRuntimeBlock(block, src, null);
  13736         return block.addUnOp(if (block.float_mode == .Optimized) .neg_optimized else .neg, rhs);
  13737     }
  13738 
  13739     const lhs = try sema.addConstant(try sema.splat(rhs_ty, try mod.intValue(rhs_scalar_ty, 0)));
  13740     return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true);
  13741 }
  13742 
  13743 fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13744     const mod = sema.mod;
  13745     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  13746     const src = inst_data.src();
  13747     const lhs_src = src;
  13748     const rhs_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
  13749 
  13750     const rhs = try sema.resolveInst(inst_data.operand);
  13751     const rhs_ty = sema.typeOf(rhs);
  13752     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  13753 
  13754     switch (rhs_scalar_ty.zigTypeTag(mod)) {
  13755         .Int, .ComptimeInt, .Float, .ComptimeFloat => {},
  13756         else => return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(mod)}),
  13757     }
  13758 
  13759     const lhs = try sema.addConstant(try sema.splat(rhs_ty, try mod.intValue(rhs_scalar_ty, 0)));
  13760     return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true);
  13761 }
  13762 
  13763 fn zirArithmetic(
  13764     sema: *Sema,
  13765     block: *Block,
  13766     inst: Zir.Inst.Index,
  13767     zir_tag: Zir.Inst.Tag,
  13768     safety: bool,
  13769 ) CompileError!Air.Inst.Ref {
  13770     const tracy = trace(@src());
  13771     defer tracy.end();
  13772 
  13773     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  13774     sema.src = .{ .node_offset_bin_op = inst_data.src_node };
  13775     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  13776     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  13777     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13778     const lhs = try sema.resolveInst(extra.lhs);
  13779     const rhs = try sema.resolveInst(extra.rhs);
  13780 
  13781     return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src, safety);
  13782 }
  13783 
  13784 fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13785     const mod = sema.mod;
  13786     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  13787     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  13788     sema.src = src;
  13789     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  13790     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  13791     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13792     const lhs = try sema.resolveInst(extra.lhs);
  13793     const rhs = try sema.resolveInst(extra.rhs);
  13794     const lhs_ty = sema.typeOf(lhs);
  13795     const rhs_ty = sema.typeOf(rhs);
  13796     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  13797     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  13798     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  13799     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  13800 
  13801     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  13802     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  13803         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  13804     });
  13805 
  13806     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  13807     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  13808 
  13809     const lhs_scalar_ty = lhs_ty.scalarType(mod);
  13810     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  13811     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  13812 
  13813     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  13814 
  13815     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div);
  13816 
  13817     const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs);
  13818     const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs);
  13819 
  13820     if ((lhs_ty.zigTypeTag(mod) == .ComptimeFloat and rhs_ty.zigTypeTag(mod) == .ComptimeInt) or
  13821         (lhs_ty.zigTypeTag(mod) == .ComptimeInt and rhs_ty.zigTypeTag(mod) == .ComptimeFloat))
  13822     {
  13823         // If it makes a difference whether we coerce to ints or floats before doing the division, error.
  13824         // If lhs % rhs is 0, it doesn't matter.
  13825         const lhs_val = maybe_lhs_val orelse unreachable;
  13826         const rhs_val = maybe_rhs_val orelse unreachable;
  13827         const rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod) catch unreachable;
  13828         if (!rem.compareAllWithZero(.eq, mod)) {
  13829             return sema.fail(
  13830                 block,
  13831                 src,
  13832                 "ambiguous coercion of division operands '{}' and '{}'; non-zero remainder '{}'",
  13833                 .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod), rem.fmtValue(resolved_type, mod) },
  13834             );
  13835         }
  13836     }
  13837 
  13838     // TODO: emit compile error when .div is used on integers and there would be an
  13839     // ambiguous result between div_floor and div_trunc.
  13840 
  13841     // For integers:
  13842     // If the lhs is zero, then zero is returned regardless of rhs.
  13843     // If the rhs is zero, compile error for division by zero.
  13844     // If the rhs is undefined, compile error because there is a possible
  13845     // value (zero) for which the division would be illegal behavior.
  13846     // If the lhs is undefined:
  13847     //   * if lhs type is signed:
  13848     //     * if rhs is comptime-known and not -1, result is undefined
  13849     //     * if rhs is -1 or runtime-known, compile error because there is a
  13850     //        possible value (-min_int / -1)  for which division would be
  13851     //        illegal behavior.
  13852     //   * if lhs type is unsigned, undef is returned regardless of rhs.
  13853     //
  13854     // For floats:
  13855     // If the rhs is zero:
  13856     //  * comptime_float: compile error for division by zero.
  13857     //  * other float type:
  13858     //    * if the lhs is zero: QNaN
  13859     //    * otherwise: +Inf or -Inf depending on lhs sign
  13860     // If the rhs is undefined:
  13861     //  * comptime_float: compile error because there is a possible
  13862     //    value (zero) for which the division would be illegal behavior.
  13863     //  * other float type: result is undefined
  13864     // If the lhs is undefined, result is undefined.
  13865     switch (scalar_tag) {
  13866         .Int, .ComptimeInt, .ComptimeFloat => {
  13867             if (maybe_lhs_val) |lhs_val| {
  13868                 if (!lhs_val.isUndef(mod)) {
  13869                     if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  13870                         const scalar_zero = switch (scalar_tag) {
  13871                             .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  13872                             .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  13873                             else => unreachable,
  13874                         };
  13875                         const zero_val = try sema.splat(resolved_type, scalar_zero);
  13876                         return sema.addConstant(zero_val);
  13877                     }
  13878                 }
  13879             }
  13880             if (maybe_rhs_val) |rhs_val| {
  13881                 if (rhs_val.isUndef(mod)) {
  13882                     return sema.failWithUseOfUndef(block, rhs_src);
  13883                 }
  13884                 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  13885                     return sema.failWithDivideByZero(block, rhs_src);
  13886                 }
  13887                 // TODO: if the RHS is one, return the LHS directly
  13888             }
  13889         },
  13890         else => {},
  13891     }
  13892 
  13893     const runtime_src = rs: {
  13894         if (maybe_lhs_val) |lhs_val| {
  13895             if (lhs_val.isUndef(mod)) {
  13896                 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) {
  13897                     if (maybe_rhs_val) |rhs_val| {
  13898                         if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) {
  13899                             return sema.addConstUndef(resolved_type);
  13900                         }
  13901                     }
  13902                     return sema.failWithUseOfUndef(block, rhs_src);
  13903                 }
  13904                 return sema.addConstUndef(resolved_type);
  13905             }
  13906 
  13907             if (maybe_rhs_val) |rhs_val| {
  13908                 if (is_int) {
  13909                     var overflow_idx: ?usize = null;
  13910                     const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
  13911                     if (overflow_idx) |vec_idx| {
  13912                         return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
  13913                     }
  13914                     return sema.addConstant(res);
  13915                 } else {
  13916                     return sema.addConstant(
  13917                         try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, mod),
  13918                     );
  13919                 }
  13920             } else {
  13921                 break :rs rhs_src;
  13922             }
  13923         } else {
  13924             break :rs lhs_src;
  13925         }
  13926     };
  13927 
  13928     try sema.requireRuntimeBlock(block, src, runtime_src);
  13929 
  13930     if (block.wantSafety()) {
  13931         try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  13932         try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  13933     }
  13934 
  13935     const air_tag = if (is_int) blk: {
  13936         if (lhs_ty.isSignedInt(mod) or rhs_ty.isSignedInt(mod)) {
  13937             return sema.fail(
  13938                 block,
  13939                 src,
  13940                 "division with '{}' and '{}': signed integers must use @divTrunc, @divFloor, or @divExact",
  13941                 .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod) },
  13942             );
  13943         }
  13944         break :blk Air.Inst.Tag.div_trunc;
  13945     } else switch (block.float_mode) {
  13946         .Optimized => Air.Inst.Tag.div_float_optimized,
  13947         .Strict => Air.Inst.Tag.div_float,
  13948     };
  13949     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  13950 }
  13951 
  13952 fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13953     const mod = sema.mod;
  13954     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  13955     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  13956     sema.src = src;
  13957     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  13958     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  13959     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13960     const lhs = try sema.resolveInst(extra.lhs);
  13961     const rhs = try sema.resolveInst(extra.rhs);
  13962     const lhs_ty = sema.typeOf(lhs);
  13963     const rhs_ty = sema.typeOf(rhs);
  13964     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  13965     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  13966     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  13967     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  13968 
  13969     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  13970     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  13971         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  13972     });
  13973 
  13974     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  13975     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  13976 
  13977     const lhs_scalar_ty = lhs_ty.scalarType(mod);
  13978     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  13979 
  13980     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  13981 
  13982     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact);
  13983 
  13984     const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs);
  13985     const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs);
  13986 
  13987     const runtime_src = rs: {
  13988         // For integers:
  13989         // If the lhs is zero, then zero is returned regardless of rhs.
  13990         // If the rhs is zero, compile error for division by zero.
  13991         // If the rhs is undefined, compile error because there is a possible
  13992         // value (zero) for which the division would be illegal behavior.
  13993         // If the lhs is undefined, compile error because there is a possible
  13994         // value for which the division would result in a remainder.
  13995         // TODO: emit runtime safety for if there is a remainder
  13996         // TODO: emit runtime safety for division by zero
  13997         //
  13998         // For floats:
  13999         // If the rhs is zero, compile error for division by zero.
  14000         // If the rhs is undefined, compile error because there is a possible
  14001         // value (zero) for which the division would be illegal behavior.
  14002         // If the lhs is undefined, compile error because there is a possible
  14003         // value for which the division would result in a remainder.
  14004         if (maybe_lhs_val) |lhs_val| {
  14005             if (lhs_val.isUndef(mod)) {
  14006                 return sema.failWithUseOfUndef(block, rhs_src);
  14007             } else {
  14008                 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  14009                     const scalar_zero = switch (scalar_tag) {
  14010                         .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  14011                         .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  14012                         else => unreachable,
  14013                     };
  14014                     const zero_val = try sema.splat(resolved_type, scalar_zero);
  14015                     return sema.addConstant(zero_val);
  14016                 }
  14017             }
  14018         }
  14019         if (maybe_rhs_val) |rhs_val| {
  14020             if (rhs_val.isUndef(mod)) {
  14021                 return sema.failWithUseOfUndef(block, rhs_src);
  14022             }
  14023             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14024                 return sema.failWithDivideByZero(block, rhs_src);
  14025             }
  14026             // TODO: if the RHS is one, return the LHS directly
  14027         }
  14028         if (maybe_lhs_val) |lhs_val| {
  14029             if (maybe_rhs_val) |rhs_val| {
  14030                 if (is_int) {
  14031                     const modulus_val = try lhs_val.intMod(rhs_val, resolved_type, sema.arena, mod);
  14032                     if (!(modulus_val.compareAllWithZero(.eq, mod))) {
  14033                         return sema.fail(block, src, "exact division produced remainder", .{});
  14034                     }
  14035                     var overflow_idx: ?usize = null;
  14036                     const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
  14037                     if (overflow_idx) |vec_idx| {
  14038                         return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
  14039                     }
  14040                     return sema.addConstant(res);
  14041                 } else {
  14042                     const modulus_val = try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, mod);
  14043                     if (!(modulus_val.compareAllWithZero(.eq, mod))) {
  14044                         return sema.fail(block, src, "exact division produced remainder", .{});
  14045                     }
  14046                     return sema.addConstant(
  14047                         try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, mod),
  14048                     );
  14049                 }
  14050             } else break :rs rhs_src;
  14051         } else break :rs lhs_src;
  14052     };
  14053 
  14054     try sema.requireRuntimeBlock(block, src, runtime_src);
  14055 
  14056     // Depending on whether safety is enabled, we will have a slightly different strategy
  14057     // here. The `div_exact` AIR instruction causes undefined behavior if a remainder
  14058     // is produced, so in the safety check case, it cannot be used. Instead we do a
  14059     // div_trunc and check for remainder.
  14060 
  14061     if (block.wantSafety()) {
  14062         try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  14063         try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14064 
  14065         const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs);
  14066         const ok = if (!is_int) ok: {
  14067             const floored = try block.addUnOp(.floor, result);
  14068 
  14069             if (resolved_type.zigTypeTag(mod) == .Vector) {
  14070                 const eql = try block.addCmpVector(result, floored, .eq);
  14071                 break :ok try block.addInst(.{
  14072                     .tag = switch (block.float_mode) {
  14073                         .Strict => .reduce,
  14074                         .Optimized => .reduce_optimized,
  14075                     },
  14076                     .data = .{ .reduce = .{
  14077                         .operand = eql,
  14078                         .operation = .And,
  14079                     } },
  14080                 });
  14081             } else {
  14082                 const is_in_range = try block.addBinOp(switch (block.float_mode) {
  14083                     .Strict => .cmp_eq,
  14084                     .Optimized => .cmp_eq_optimized,
  14085                 }, result, floored);
  14086                 break :ok is_in_range;
  14087             }
  14088         } else ok: {
  14089             const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs);
  14090 
  14091             const scalar_zero = switch (scalar_tag) {
  14092                 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  14093                 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  14094                 else => unreachable,
  14095             };
  14096             if (resolved_type.zigTypeTag(mod) == .Vector) {
  14097                 const zero_val = try sema.splat(resolved_type, scalar_zero);
  14098                 const zero = try sema.addConstant(zero_val);
  14099                 const eql = try block.addCmpVector(remainder, zero, .eq);
  14100                 break :ok try block.addInst(.{
  14101                     .tag = .reduce,
  14102                     .data = .{ .reduce = .{
  14103                         .operand = eql,
  14104                         .operation = .And,
  14105                     } },
  14106                 });
  14107             } else {
  14108                 const zero = try sema.addConstant(scalar_zero);
  14109                 const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero);
  14110                 break :ok is_in_range;
  14111             }
  14112         };
  14113         try sema.addSafetyCheck(block, ok, .exact_division_remainder);
  14114         return result;
  14115     }
  14116 
  14117     return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs);
  14118 }
  14119 
  14120 fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14121     const mod = sema.mod;
  14122     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  14123     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  14124     sema.src = src;
  14125     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14126     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14127     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14128     const lhs = try sema.resolveInst(extra.lhs);
  14129     const rhs = try sema.resolveInst(extra.rhs);
  14130     const lhs_ty = sema.typeOf(lhs);
  14131     const rhs_ty = sema.typeOf(rhs);
  14132     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  14133     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  14134     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14135     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  14136 
  14137     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  14138     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  14139         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14140     });
  14141 
  14142     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14143     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14144 
  14145     const lhs_scalar_ty = lhs_ty.scalarType(mod);
  14146     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  14147     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  14148 
  14149     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  14150 
  14151     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor);
  14152 
  14153     const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs);
  14154     const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs);
  14155 
  14156     const runtime_src = rs: {
  14157         // For integers:
  14158         // If the lhs is zero, then zero is returned regardless of rhs.
  14159         // If the rhs is zero, compile error for division by zero.
  14160         // If the rhs is undefined, compile error because there is a possible
  14161         // value (zero) for which the division would be illegal behavior.
  14162         // If the lhs is undefined:
  14163         //   * if lhs type is signed:
  14164         //     * if rhs is comptime-known and not -1, result is undefined
  14165         //     * if rhs is -1 or runtime-known, compile error because there is a
  14166         //        possible value (-min_int / -1)  for which division would be
  14167         //        illegal behavior.
  14168         //   * if lhs type is unsigned, undef is returned regardless of rhs.
  14169         // TODO: emit runtime safety for division by zero
  14170         //
  14171         // For floats:
  14172         // If the rhs is zero, compile error for division by zero.
  14173         // If the rhs is undefined, compile error because there is a possible
  14174         // value (zero) for which the division would be illegal behavior.
  14175         // If the lhs is undefined, result is undefined.
  14176         if (maybe_lhs_val) |lhs_val| {
  14177             if (!lhs_val.isUndef(mod)) {
  14178                 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  14179                     const scalar_zero = switch (scalar_tag) {
  14180                         .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  14181                         .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  14182                         else => unreachable,
  14183                     };
  14184                     const zero_val = try sema.splat(resolved_type, scalar_zero);
  14185                     return sema.addConstant(zero_val);
  14186                 }
  14187             }
  14188         }
  14189         if (maybe_rhs_val) |rhs_val| {
  14190             if (rhs_val.isUndef(mod)) {
  14191                 return sema.failWithUseOfUndef(block, rhs_src);
  14192             }
  14193             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14194                 return sema.failWithDivideByZero(block, rhs_src);
  14195             }
  14196             // TODO: if the RHS is one, return the LHS directly
  14197         }
  14198         if (maybe_lhs_val) |lhs_val| {
  14199             if (lhs_val.isUndef(mod)) {
  14200                 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) {
  14201                     if (maybe_rhs_val) |rhs_val| {
  14202                         if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) {
  14203                             return sema.addConstUndef(resolved_type);
  14204                         }
  14205                     }
  14206                     return sema.failWithUseOfUndef(block, rhs_src);
  14207                 }
  14208                 return sema.addConstUndef(resolved_type);
  14209             }
  14210 
  14211             if (maybe_rhs_val) |rhs_val| {
  14212                 if (is_int) {
  14213                     return sema.addConstant(
  14214                         try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, mod),
  14215                     );
  14216                 } else {
  14217                     return sema.addConstant(
  14218                         try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, mod),
  14219                     );
  14220                 }
  14221             } else break :rs rhs_src;
  14222         } else break :rs lhs_src;
  14223     };
  14224 
  14225     try sema.requireRuntimeBlock(block, src, runtime_src);
  14226 
  14227     if (block.wantSafety()) {
  14228         try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  14229         try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14230     }
  14231 
  14232     return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
  14233 }
  14234 
  14235 fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14236     const mod = sema.mod;
  14237     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  14238     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  14239     sema.src = src;
  14240     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14241     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14242     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14243     const lhs = try sema.resolveInst(extra.lhs);
  14244     const rhs = try sema.resolveInst(extra.rhs);
  14245     const lhs_ty = sema.typeOf(lhs);
  14246     const rhs_ty = sema.typeOf(rhs);
  14247     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  14248     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  14249     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14250     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  14251 
  14252     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  14253     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  14254         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14255     });
  14256 
  14257     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14258     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14259 
  14260     const lhs_scalar_ty = lhs_ty.scalarType(mod);
  14261     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  14262     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  14263 
  14264     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  14265 
  14266     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc);
  14267 
  14268     const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs);
  14269     const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs);
  14270 
  14271     const runtime_src = rs: {
  14272         // For integers:
  14273         // If the lhs is zero, then zero is returned regardless of rhs.
  14274         // If the rhs is zero, compile error for division by zero.
  14275         // If the rhs is undefined, compile error because there is a possible
  14276         // value (zero) for which the division would be illegal behavior.
  14277         // If the lhs is undefined:
  14278         //   * if lhs type is signed:
  14279         //     * if rhs is comptime-known and not -1, result is undefined
  14280         //     * if rhs is -1 or runtime-known, compile error because there is a
  14281         //        possible value (-min_int / -1)  for which division would be
  14282         //        illegal behavior.
  14283         //   * if lhs type is unsigned, undef is returned regardless of rhs.
  14284         // TODO: emit runtime safety for division by zero
  14285         //
  14286         // For floats:
  14287         // If the rhs is zero, compile error for division by zero.
  14288         // If the rhs is undefined, compile error because there is a possible
  14289         // value (zero) for which the division would be illegal behavior.
  14290         // If the lhs is undefined, result is undefined.
  14291         if (maybe_lhs_val) |lhs_val| {
  14292             if (!lhs_val.isUndef(mod)) {
  14293                 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  14294                     const scalar_zero = switch (scalar_tag) {
  14295                         .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  14296                         .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  14297                         else => unreachable,
  14298                     };
  14299                     const zero_val = try sema.splat(resolved_type, scalar_zero);
  14300                     return sema.addConstant(zero_val);
  14301                 }
  14302             }
  14303         }
  14304         if (maybe_rhs_val) |rhs_val| {
  14305             if (rhs_val.isUndef(mod)) {
  14306                 return sema.failWithUseOfUndef(block, rhs_src);
  14307             }
  14308             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14309                 return sema.failWithDivideByZero(block, rhs_src);
  14310             }
  14311         }
  14312         if (maybe_lhs_val) |lhs_val| {
  14313             if (lhs_val.isUndef(mod)) {
  14314                 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) {
  14315                     if (maybe_rhs_val) |rhs_val| {
  14316                         if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) {
  14317                             return sema.addConstUndef(resolved_type);
  14318                         }
  14319                     }
  14320                     return sema.failWithUseOfUndef(block, rhs_src);
  14321                 }
  14322                 return sema.addConstUndef(resolved_type);
  14323             }
  14324 
  14325             if (maybe_rhs_val) |rhs_val| {
  14326                 if (is_int) {
  14327                     var overflow_idx: ?usize = null;
  14328                     const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
  14329                     if (overflow_idx) |vec_idx| {
  14330                         return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
  14331                     }
  14332                     return sema.addConstant(res);
  14333                 } else {
  14334                     return sema.addConstant(
  14335                         try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, mod),
  14336                     );
  14337                 }
  14338             } else break :rs rhs_src;
  14339         } else break :rs lhs_src;
  14340     };
  14341 
  14342     try sema.requireRuntimeBlock(block, src, runtime_src);
  14343 
  14344     if (block.wantSafety()) {
  14345         try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  14346         try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14347     }
  14348 
  14349     return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs);
  14350 }
  14351 
  14352 fn addDivIntOverflowSafety(
  14353     sema: *Sema,
  14354     block: *Block,
  14355     resolved_type: Type,
  14356     lhs_scalar_ty: Type,
  14357     maybe_lhs_val: ?Value,
  14358     maybe_rhs_val: ?Value,
  14359     casted_lhs: Air.Inst.Ref,
  14360     casted_rhs: Air.Inst.Ref,
  14361     is_int: bool,
  14362 ) CompileError!void {
  14363     const mod = sema.mod;
  14364     if (!is_int) return;
  14365 
  14366     // If the LHS is unsigned, it cannot cause overflow.
  14367     if (!lhs_scalar_ty.isSignedInt(mod)) return;
  14368 
  14369     // If the LHS is widened to a larger integer type, no overflow is possible.
  14370     if (lhs_scalar_ty.intInfo(mod).bits < resolved_type.intInfo(mod).bits) {
  14371         return;
  14372     }
  14373 
  14374     const min_int = try resolved_type.minInt(mod, resolved_type);
  14375     const neg_one_scalar = try mod.intValue(lhs_scalar_ty, -1);
  14376     const neg_one = try sema.splat(resolved_type, neg_one_scalar);
  14377 
  14378     // If the LHS is comptime-known to be not equal to the min int,
  14379     // no overflow is possible.
  14380     if (maybe_lhs_val) |lhs_val| {
  14381         if (try lhs_val.compareAll(.neq, min_int, resolved_type, mod)) return;
  14382     }
  14383 
  14384     // If the RHS is comptime-known to not be equal to -1, no overflow is possible.
  14385     if (maybe_rhs_val) |rhs_val| {
  14386         if (try rhs_val.compareAll(.neq, neg_one, resolved_type, mod)) return;
  14387     }
  14388 
  14389     var ok: Air.Inst.Ref = .none;
  14390     if (resolved_type.zigTypeTag(mod) == .Vector) {
  14391         if (maybe_lhs_val == null) {
  14392             const min_int_ref = try sema.addConstant(min_int);
  14393             ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq);
  14394         }
  14395         if (maybe_rhs_val == null) {
  14396             const neg_one_ref = try sema.addConstant(neg_one);
  14397             const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq);
  14398             if (ok == .none) {
  14399                 ok = rhs_ok;
  14400             } else {
  14401                 ok = try block.addBinOp(.bool_or, ok, rhs_ok);
  14402             }
  14403         }
  14404         assert(ok != .none);
  14405         ok = try block.addInst(.{
  14406             .tag = .reduce,
  14407             .data = .{ .reduce = .{
  14408                 .operand = ok,
  14409                 .operation = .And,
  14410             } },
  14411         });
  14412     } else {
  14413         if (maybe_lhs_val == null) {
  14414             const min_int_ref = try sema.addConstant(min_int);
  14415             ok = try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref);
  14416         }
  14417         if (maybe_rhs_val == null) {
  14418             const neg_one_ref = try sema.addConstant(neg_one);
  14419             const rhs_ok = try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref);
  14420             if (ok == .none) {
  14421                 ok = rhs_ok;
  14422             } else {
  14423                 ok = try block.addBinOp(.bool_or, ok, rhs_ok);
  14424             }
  14425         }
  14426         assert(ok != .none);
  14427     }
  14428     try sema.addSafetyCheck(block, ok, .integer_overflow);
  14429 }
  14430 
  14431 fn addDivByZeroSafety(
  14432     sema: *Sema,
  14433     block: *Block,
  14434     resolved_type: Type,
  14435     maybe_rhs_val: ?Value,
  14436     casted_rhs: Air.Inst.Ref,
  14437     is_int: bool,
  14438 ) CompileError!void {
  14439     // Strict IEEE floats have well-defined division by zero.
  14440     if (!is_int and block.float_mode == .Strict) return;
  14441 
  14442     // If rhs was comptime-known to be zero a compile error would have been
  14443     // emitted above.
  14444     if (maybe_rhs_val != null) return;
  14445 
  14446     const mod = sema.mod;
  14447     const scalar_zero = if (is_int)
  14448         try mod.intValue(resolved_type.scalarType(mod), 0)
  14449     else
  14450         try mod.floatValue(resolved_type.scalarType(mod), 0.0);
  14451     const ok = if (resolved_type.zigTypeTag(mod) == .Vector) ok: {
  14452         const zero_val = try sema.splat(resolved_type, scalar_zero);
  14453         const zero = try sema.addConstant(zero_val);
  14454         const ok = try block.addCmpVector(casted_rhs, zero, .neq);
  14455         break :ok try block.addInst(.{
  14456             .tag = if (is_int) .reduce else .reduce_optimized,
  14457             .data = .{ .reduce = .{
  14458                 .operand = ok,
  14459                 .operation = .And,
  14460             } },
  14461         });
  14462     } else ok: {
  14463         const zero = try sema.addConstant(scalar_zero);
  14464         break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero);
  14465     };
  14466     try sema.addSafetyCheck(block, ok, .divide_by_zero);
  14467 }
  14468 
  14469 fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag {
  14470     if (is_int) return normal;
  14471     return switch (block.float_mode) {
  14472         .Strict => normal,
  14473         .Optimized => optimized,
  14474     };
  14475 }
  14476 
  14477 fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14478     const mod = sema.mod;
  14479     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  14480     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  14481     sema.src = src;
  14482     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14483     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14484     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14485     const lhs = try sema.resolveInst(extra.lhs);
  14486     const rhs = try sema.resolveInst(extra.rhs);
  14487     const lhs_ty = sema.typeOf(lhs);
  14488     const rhs_ty = sema.typeOf(rhs);
  14489     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  14490     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  14491     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14492     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  14493 
  14494     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  14495     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  14496         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14497     });
  14498 
  14499     const is_vector = resolved_type.zigTypeTag(mod) == .Vector;
  14500 
  14501     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14502     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14503 
  14504     const lhs_scalar_ty = lhs_ty.scalarType(mod);
  14505     const rhs_scalar_ty = rhs_ty.scalarType(mod);
  14506     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  14507 
  14508     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  14509 
  14510     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem);
  14511 
  14512     const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs);
  14513     const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs);
  14514 
  14515     const runtime_src = rs: {
  14516         // For integers:
  14517         // Either operand being undef is a compile error because there exists
  14518         // a possible value (TODO what is it?) that would invoke illegal behavior.
  14519         // TODO: can lhs undef be handled better?
  14520         //
  14521         // For floats:
  14522         // If the rhs is zero, compile error for division by zero.
  14523         // If the rhs is undefined, compile error because there is a possible
  14524         // value (zero) for which the division would be illegal behavior.
  14525         // If the lhs is undefined, result is undefined.
  14526         //
  14527         // For either one: if the result would be different between @mod and @rem,
  14528         // then emit a compile error saying you have to pick one.
  14529         if (is_int) {
  14530             if (maybe_lhs_val) |lhs_val| {
  14531                 if (lhs_val.isUndef(mod)) {
  14532                     return sema.failWithUseOfUndef(block, lhs_src);
  14533                 }
  14534                 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  14535                     const scalar_zero = switch (scalar_tag) {
  14536                         .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0),
  14537                         .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0),
  14538                         else => unreachable,
  14539                     };
  14540                     const zero_val = if (is_vector) (try mod.intern(.{ .aggregate = .{
  14541                         .ty = resolved_type.toIntern(),
  14542                         .storage = .{ .repeated_elem = scalar_zero.toIntern() },
  14543                     } })).toValue() else scalar_zero;
  14544                     return sema.addConstant(zero_val);
  14545                 }
  14546             } else if (lhs_scalar_ty.isSignedInt(mod)) {
  14547                 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
  14548             }
  14549             if (maybe_rhs_val) |rhs_val| {
  14550                 if (rhs_val.isUndef(mod)) {
  14551                     return sema.failWithUseOfUndef(block, rhs_src);
  14552                 }
  14553                 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14554                     return sema.failWithDivideByZero(block, rhs_src);
  14555                 }
  14556                 if (!(try rhs_val.compareAllWithZeroAdvanced(.gte, sema))) {
  14557                     return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
  14558                 }
  14559                 if (maybe_lhs_val) |lhs_val| {
  14560                     const rem_result = try sema.intRem(resolved_type, lhs_val, rhs_val);
  14561                     // If this answer could possibly be different by doing `intMod`,
  14562                     // we must emit a compile error. Otherwise, it's OK.
  14563                     if (!(try lhs_val.compareAllWithZeroAdvanced(.gte, sema)) and
  14564                         !(try rem_result.compareAllWithZeroAdvanced(.eq, sema)))
  14565                     {
  14566                         return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
  14567                     }
  14568                     return sema.addConstant(rem_result);
  14569                 }
  14570                 break :rs lhs_src;
  14571             } else if (rhs_scalar_ty.isSignedInt(mod)) {
  14572                 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
  14573             } else {
  14574                 break :rs rhs_src;
  14575             }
  14576         }
  14577         // float operands
  14578         if (maybe_rhs_val) |rhs_val| {
  14579             if (rhs_val.isUndef(mod)) {
  14580                 return sema.failWithUseOfUndef(block, rhs_src);
  14581             }
  14582             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14583                 return sema.failWithDivideByZero(block, rhs_src);
  14584             }
  14585             if (!(try rhs_val.compareAllWithZeroAdvanced(.gte, sema))) {
  14586                 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
  14587             }
  14588             if (maybe_lhs_val) |lhs_val| {
  14589                 if (lhs_val.isUndef(mod) or !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema))) {
  14590                     return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
  14591                 }
  14592                 return sema.addConstant(
  14593                     try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod),
  14594                 );
  14595             } else {
  14596                 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
  14597             }
  14598         } else {
  14599             return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
  14600         }
  14601     };
  14602 
  14603     try sema.requireRuntimeBlock(block, src, runtime_src);
  14604 
  14605     if (block.wantSafety()) {
  14606         try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14607     }
  14608 
  14609     const air_tag = airTag(block, is_int, .rem, .rem_optimized);
  14610     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  14611 }
  14612 
  14613 fn intRem(
  14614     sema: *Sema,
  14615     ty: Type,
  14616     lhs: Value,
  14617     rhs: Value,
  14618 ) CompileError!Value {
  14619     const mod = sema.mod;
  14620     if (ty.zigTypeTag(mod) == .Vector) {
  14621         const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
  14622         const scalar_ty = ty.scalarType(mod);
  14623         for (result_data, 0..) |*scalar, i| {
  14624             const lhs_elem = try lhs.elemValue(mod, i);
  14625             const rhs_elem = try rhs.elemValue(mod, i);
  14626             scalar.* = try (try sema.intRemScalar(lhs_elem, rhs_elem, scalar_ty)).intern(scalar_ty, mod);
  14627         }
  14628         return (try mod.intern(.{ .aggregate = .{
  14629             .ty = ty.toIntern(),
  14630             .storage = .{ .elems = result_data },
  14631         } })).toValue();
  14632     }
  14633     return sema.intRemScalar(lhs, rhs, ty);
  14634 }
  14635 
  14636 fn intRemScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) CompileError!Value {
  14637     const mod = sema.mod;
  14638     // TODO is this a performance issue? maybe we should try the operation without
  14639     // resorting to BigInt first.
  14640     var lhs_space: Value.BigIntSpace = undefined;
  14641     var rhs_space: Value.BigIntSpace = undefined;
  14642     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  14643     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  14644     const limbs_q = try sema.arena.alloc(
  14645         math.big.Limb,
  14646         lhs_bigint.limbs.len,
  14647     );
  14648     const limbs_r = try sema.arena.alloc(
  14649         math.big.Limb,
  14650         // TODO: consider reworking Sema to re-use Values rather than
  14651         // always producing new Value objects.
  14652         rhs_bigint.limbs.len,
  14653     );
  14654     const limbs_buffer = try sema.arena.alloc(
  14655         math.big.Limb,
  14656         math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
  14657     );
  14658     var result_q = math.big.int.Mutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
  14659     var result_r = math.big.int.Mutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
  14660     result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
  14661     return mod.intValue_big(scalar_ty, result_r.toConst());
  14662 }
  14663 
  14664 fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14665     const mod = sema.mod;
  14666     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  14667     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  14668     sema.src = src;
  14669     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14670     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14671     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14672     const lhs = try sema.resolveInst(extra.lhs);
  14673     const rhs = try sema.resolveInst(extra.rhs);
  14674     const lhs_ty = sema.typeOf(lhs);
  14675     const rhs_ty = sema.typeOf(rhs);
  14676     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  14677     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  14678     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14679     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  14680 
  14681     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  14682     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  14683         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14684     });
  14685 
  14686     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14687     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14688 
  14689     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  14690 
  14691     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  14692 
  14693     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod);
  14694 
  14695     const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs);
  14696     const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs);
  14697 
  14698     const runtime_src = rs: {
  14699         // For integers:
  14700         // Either operand being undef is a compile error because there exists
  14701         // a possible value (TODO what is it?) that would invoke illegal behavior.
  14702         // TODO: can lhs zero be handled better?
  14703         // TODO: can lhs undef be handled better?
  14704         //
  14705         // For floats:
  14706         // If the rhs is zero, compile error for division by zero.
  14707         // If the rhs is undefined, compile error because there is a possible
  14708         // value (zero) for which the division would be illegal behavior.
  14709         // If the lhs is undefined, result is undefined.
  14710         if (is_int) {
  14711             if (maybe_lhs_val) |lhs_val| {
  14712                 if (lhs_val.isUndef(mod)) {
  14713                     return sema.failWithUseOfUndef(block, lhs_src);
  14714                 }
  14715             }
  14716             if (maybe_rhs_val) |rhs_val| {
  14717                 if (rhs_val.isUndef(mod)) {
  14718                     return sema.failWithUseOfUndef(block, rhs_src);
  14719                 }
  14720                 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14721                     return sema.failWithDivideByZero(block, rhs_src);
  14722                 }
  14723                 if (maybe_lhs_val) |lhs_val| {
  14724                     return sema.addConstant(
  14725                         try lhs_val.intMod(rhs_val, resolved_type, sema.arena, mod),
  14726                     );
  14727                 }
  14728                 break :rs lhs_src;
  14729             } else {
  14730                 break :rs rhs_src;
  14731             }
  14732         }
  14733         // float operands
  14734         if (maybe_rhs_val) |rhs_val| {
  14735             if (rhs_val.isUndef(mod)) {
  14736                 return sema.failWithUseOfUndef(block, rhs_src);
  14737             }
  14738             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14739                 return sema.failWithDivideByZero(block, rhs_src);
  14740             }
  14741         }
  14742         if (maybe_lhs_val) |lhs_val| {
  14743             if (lhs_val.isUndef(mod)) {
  14744                 return sema.addConstUndef(resolved_type);
  14745             }
  14746             if (maybe_rhs_val) |rhs_val| {
  14747                 return sema.addConstant(
  14748                     try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, mod),
  14749                 );
  14750             } else break :rs rhs_src;
  14751         } else break :rs lhs_src;
  14752     };
  14753 
  14754     try sema.requireRuntimeBlock(block, src, runtime_src);
  14755 
  14756     if (block.wantSafety()) {
  14757         try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14758     }
  14759 
  14760     const air_tag = airTag(block, is_int, .mod, .mod_optimized);
  14761     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  14762 }
  14763 
  14764 fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14765     const mod = sema.mod;
  14766     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  14767     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  14768     sema.src = src;
  14769     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  14770     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  14771     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14772     const lhs = try sema.resolveInst(extra.lhs);
  14773     const rhs = try sema.resolveInst(extra.rhs);
  14774     const lhs_ty = sema.typeOf(lhs);
  14775     const rhs_ty = sema.typeOf(rhs);
  14776     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  14777     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  14778     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14779     try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
  14780 
  14781     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  14782     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  14783         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14784     });
  14785 
  14786     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14787     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14788 
  14789     const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod);
  14790 
  14791     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  14792 
  14793     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem);
  14794 
  14795     const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs);
  14796     const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs);
  14797 
  14798     const runtime_src = rs: {
  14799         // For integers:
  14800         // Either operand being undef is a compile error because there exists
  14801         // a possible value (TODO what is it?) that would invoke illegal behavior.
  14802         // TODO: can lhs zero be handled better?
  14803         // TODO: can lhs undef be handled better?
  14804         //
  14805         // For floats:
  14806         // If the rhs is zero, compile error for division by zero.
  14807         // If the rhs is undefined, compile error because there is a possible
  14808         // value (zero) for which the division would be illegal behavior.
  14809         // If the lhs is undefined, result is undefined.
  14810         if (is_int) {
  14811             if (maybe_lhs_val) |lhs_val| {
  14812                 if (lhs_val.isUndef(mod)) {
  14813                     return sema.failWithUseOfUndef(block, lhs_src);
  14814                 }
  14815             }
  14816             if (maybe_rhs_val) |rhs_val| {
  14817                 if (rhs_val.isUndef(mod)) {
  14818                     return sema.failWithUseOfUndef(block, rhs_src);
  14819                 }
  14820                 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14821                     return sema.failWithDivideByZero(block, rhs_src);
  14822                 }
  14823                 if (maybe_lhs_val) |lhs_val| {
  14824                     return sema.addConstant(
  14825                         try sema.intRem(resolved_type, lhs_val, rhs_val),
  14826                     );
  14827                 }
  14828                 break :rs lhs_src;
  14829             } else {
  14830                 break :rs rhs_src;
  14831             }
  14832         }
  14833         // float operands
  14834         if (maybe_rhs_val) |rhs_val| {
  14835             if (rhs_val.isUndef(mod)) {
  14836                 return sema.failWithUseOfUndef(block, rhs_src);
  14837             }
  14838             if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) {
  14839                 return sema.failWithDivideByZero(block, rhs_src);
  14840             }
  14841         }
  14842         if (maybe_lhs_val) |lhs_val| {
  14843             if (lhs_val.isUndef(mod)) {
  14844                 return sema.addConstUndef(resolved_type);
  14845             }
  14846             if (maybe_rhs_val) |rhs_val| {
  14847                 return sema.addConstant(
  14848                     try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod),
  14849                 );
  14850             } else break :rs rhs_src;
  14851         } else break :rs lhs_src;
  14852     };
  14853 
  14854     try sema.requireRuntimeBlock(block, src, runtime_src);
  14855 
  14856     if (block.wantSafety()) {
  14857         try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14858     }
  14859 
  14860     const air_tag = airTag(block, is_int, .rem, .rem_optimized);
  14861     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  14862 }
  14863 
  14864 fn zirOverflowArithmetic(
  14865     sema: *Sema,
  14866     block: *Block,
  14867     extended: Zir.Inst.Extended.InstData,
  14868     zir_tag: Zir.Inst.Extended,
  14869 ) CompileError!Air.Inst.Ref {
  14870     const tracy = trace(@src());
  14871     defer tracy.end();
  14872 
  14873     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  14874     const src = LazySrcLoc.nodeOffset(extra.node);
  14875 
  14876     const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  14877     const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  14878 
  14879     const uncasted_lhs = try sema.resolveInst(extra.lhs);
  14880     const uncasted_rhs = try sema.resolveInst(extra.rhs);
  14881 
  14882     const lhs_ty = sema.typeOf(uncasted_lhs);
  14883     const rhs_ty = sema.typeOf(uncasted_rhs);
  14884     const mod = sema.mod;
  14885 
  14886     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14887 
  14888     const instructions = &[_]Air.Inst.Ref{ uncasted_lhs, uncasted_rhs };
  14889     const dest_ty = if (zir_tag == .shl_with_overflow)
  14890         lhs_ty
  14891     else
  14892         try sema.resolvePeerTypes(block, src, instructions, .{
  14893             .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14894         });
  14895 
  14896     const rhs_dest_ty = if (zir_tag == .shl_with_overflow)
  14897         try sema.log2IntType(block, lhs_ty, src)
  14898     else
  14899         dest_ty;
  14900 
  14901     const lhs = try sema.coerce(block, dest_ty, uncasted_lhs, lhs_src);
  14902     const rhs = try sema.coerce(block, rhs_dest_ty, uncasted_rhs, rhs_src);
  14903 
  14904     if (dest_ty.scalarType(mod).zigTypeTag(mod) != .Int) {
  14905         return sema.fail(block, src, "expected vector of integers or integer tag type, found '{}'", .{dest_ty.fmt(mod)});
  14906     }
  14907 
  14908     const maybe_lhs_val = try sema.resolveMaybeUndefVal(lhs);
  14909     const maybe_rhs_val = try sema.resolveMaybeUndefVal(rhs);
  14910 
  14911     const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty);
  14912     const overflow_ty = mod.intern_pool.indexToKey(tuple_ty.toIntern()).anon_struct_type.types[1].toType();
  14913 
  14914     var result: struct {
  14915         inst: Air.Inst.Ref = .none,
  14916         wrapped: Value = Value.@"unreachable",
  14917         overflow_bit: Value,
  14918     } = result: {
  14919         const zero_bit = try mod.intValue(Type.u1, 0);
  14920         switch (zir_tag) {
  14921             .add_with_overflow => {
  14922                 // If either of the arguments is zero, `false` is returned and the other is stored
  14923                 // to the result, even if it is undefined..
  14924                 // Otherwise, if either of the argument is undefined, undefined is returned.
  14925                 if (maybe_lhs_val) |lhs_val| {
  14926                     if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  14927                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs };
  14928                     }
  14929                 }
  14930                 if (maybe_rhs_val) |rhs_val| {
  14931                     if (!rhs_val.isUndef(mod) and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  14932                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  14933                     }
  14934                 }
  14935                 if (maybe_lhs_val) |lhs_val| {
  14936                     if (maybe_rhs_val) |rhs_val| {
  14937                         if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  14938                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  14939                         }
  14940 
  14941                         const result = try sema.intAddWithOverflow(lhs_val, rhs_val, dest_ty);
  14942                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  14943                     }
  14944                 }
  14945             },
  14946             .sub_with_overflow => {
  14947                 // If the rhs is zero, then the result is lhs and no overflow occured.
  14948                 // Otherwise, if either result is undefined, both results are undefined.
  14949                 if (maybe_rhs_val) |rhs_val| {
  14950                     if (rhs_val.isUndef(mod)) {
  14951                         break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  14952                     } else if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  14953                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  14954                     } else if (maybe_lhs_val) |lhs_val| {
  14955                         if (lhs_val.isUndef(mod)) {
  14956                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  14957                         }
  14958 
  14959                         const result = try sema.intSubWithOverflow(lhs_val, rhs_val, dest_ty);
  14960                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  14961                     }
  14962                 }
  14963             },
  14964             .mul_with_overflow => {
  14965                 // If either of the arguments is zero, the result is zero and no overflow occured.
  14966                 // If either of the arguments is one, the result is the other and no overflow occured.
  14967                 // Otherwise, if either of the arguments is undefined, both results are undefined.
  14968                 const scalar_one = try mod.intValue(dest_ty.scalarType(mod), 1);
  14969                 if (maybe_lhs_val) |lhs_val| {
  14970                     if (!lhs_val.isUndef(mod)) {
  14971                         if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  14972                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  14973                         } else if (try sema.compareAll(lhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) {
  14974                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs };
  14975                         }
  14976                     }
  14977                 }
  14978 
  14979                 if (maybe_rhs_val) |rhs_val| {
  14980                     if (!rhs_val.isUndef(mod)) {
  14981                         if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  14982                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs };
  14983                         } else if (try sema.compareAll(rhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) {
  14984                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  14985                         }
  14986                     }
  14987                 }
  14988 
  14989                 if (maybe_lhs_val) |lhs_val| {
  14990                     if (maybe_rhs_val) |rhs_val| {
  14991                         if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  14992                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  14993                         }
  14994 
  14995                         const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, mod);
  14996                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  14997                     }
  14998                 }
  14999             },
  15000             .shl_with_overflow => {
  15001                 // If lhs is zero, the result is zero and no overflow occurred.
  15002                 // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred.
  15003                 // Oterhwise if either of the arguments is undefined, both results are undefined.
  15004                 if (maybe_lhs_val) |lhs_val| {
  15005                     if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15006                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  15007                     }
  15008                 }
  15009                 if (maybe_rhs_val) |rhs_val| {
  15010                     if (!rhs_val.isUndef(mod) and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15011                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs };
  15012                     }
  15013                 }
  15014                 if (maybe_lhs_val) |lhs_val| {
  15015                     if (maybe_rhs_val) |rhs_val| {
  15016                         if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  15017                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  15018                         }
  15019 
  15020                         const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, mod);
  15021                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15022                     }
  15023                 }
  15024             },
  15025             else => unreachable,
  15026         }
  15027 
  15028         const air_tag: Air.Inst.Tag = switch (zir_tag) {
  15029             .add_with_overflow => .add_with_overflow,
  15030             .mul_with_overflow => .mul_with_overflow,
  15031             .sub_with_overflow => .sub_with_overflow,
  15032             .shl_with_overflow => .shl_with_overflow,
  15033             else => unreachable,
  15034         };
  15035 
  15036         const runtime_src = if (maybe_lhs_val == null) lhs_src else rhs_src;
  15037         try sema.requireRuntimeBlock(block, src, runtime_src);
  15038 
  15039         return block.addInst(.{
  15040             .tag = air_tag,
  15041             .data = .{ .ty_pl = .{
  15042                 .ty = try block.sema.addType(tuple_ty),
  15043                 .payload = try block.sema.addExtra(Air.Bin{
  15044                     .lhs = lhs,
  15045                     .rhs = rhs,
  15046                 }),
  15047             } },
  15048         });
  15049     };
  15050 
  15051     if (result.inst != .none) {
  15052         if (try sema.resolveMaybeUndefVal(result.inst)) |some| {
  15053             result.wrapped = some;
  15054             result.inst = .none;
  15055         }
  15056     }
  15057 
  15058     if (result.inst == .none) {
  15059         return sema.addConstant((try mod.intern(.{ .aggregate = .{
  15060             .ty = tuple_ty.toIntern(),
  15061             .storage = .{ .elems = &.{
  15062                 result.wrapped.toIntern(),
  15063                 result.overflow_bit.toIntern(),
  15064             } },
  15065         } })).toValue());
  15066     }
  15067 
  15068     const element_refs = try sema.arena.alloc(Air.Inst.Ref, 2);
  15069     element_refs[0] = result.inst;
  15070     element_refs[1] = try sema.addConstant(result.overflow_bit);
  15071     return block.addAggregateInit(tuple_ty, element_refs);
  15072 }
  15073 
  15074 fn splat(sema: *Sema, ty: Type, val: Value) !Value {
  15075     const mod = sema.mod;
  15076     if (ty.zigTypeTag(mod) != .Vector) return val;
  15077     const repeated = try mod.intern(.{ .aggregate = .{
  15078         .ty = ty.toIntern(),
  15079         .storage = .{ .repeated_elem = val.toIntern() },
  15080     } });
  15081     return repeated.toValue();
  15082 }
  15083 
  15084 fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type {
  15085     const mod = sema.mod;
  15086     const ov_ty = if (ty.zigTypeTag(mod) == .Vector) try mod.vectorType(.{
  15087         .len = ty.vectorLen(mod),
  15088         .child = .u1_type,
  15089     }) else Type.u1;
  15090 
  15091     const types = [2]InternPool.Index{ ty.toIntern(), ov_ty.toIntern() };
  15092     const values = [2]InternPool.Index{ .none, .none };
  15093     const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
  15094         .types = &types,
  15095         .values = &values,
  15096         .names = &.{},
  15097     } });
  15098     return tuple_ty.toType();
  15099 }
  15100 
  15101 fn analyzeArithmetic(
  15102     sema: *Sema,
  15103     block: *Block,
  15104     /// TODO performance investigation: make this comptime?
  15105     zir_tag: Zir.Inst.Tag,
  15106     lhs: Air.Inst.Ref,
  15107     rhs: Air.Inst.Ref,
  15108     src: LazySrcLoc,
  15109     lhs_src: LazySrcLoc,
  15110     rhs_src: LazySrcLoc,
  15111     want_safety: bool,
  15112 ) CompileError!Air.Inst.Ref {
  15113     const mod = sema.mod;
  15114     const lhs_ty = sema.typeOf(lhs);
  15115     const rhs_ty = sema.typeOf(rhs);
  15116     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  15117     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  15118     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15119 
  15120     if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize(mod)) {
  15121         .One, .Slice => {},
  15122         .Many, .C => {
  15123             const air_tag: Air.Inst.Tag = switch (zir_tag) {
  15124                 .add => .ptr_add,
  15125                 .sub => .ptr_sub,
  15126                 else => return sema.fail(block, src, "invalid pointer arithmetic operator", .{}),
  15127             };
  15128             return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
  15129         },
  15130     };
  15131 
  15132     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  15133     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
  15134         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  15135     });
  15136 
  15137     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15138     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15139 
  15140     const scalar_type = resolved_type.scalarType(mod);
  15141     const scalar_tag = scalar_type.zigTypeTag(mod);
  15142 
  15143     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  15144 
  15145     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag);
  15146 
  15147     const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs);
  15148     const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs);
  15149     const rs: struct {
  15150         src: LazySrcLoc,
  15151         air_tag: Air.Inst.Tag,
  15152         air_tag_safe: Air.Inst.Tag,
  15153     } = rs: {
  15154         switch (zir_tag) {
  15155             .add, .add_unsafe => {
  15156                 // For integers:intAddSat
  15157                 // If either of the operands are zero, then the other operand is
  15158                 // returned, even if it is undefined.
  15159                 // If either of the operands are undefined, it's a compile error
  15160                 // because there is a possible value for which the addition would
  15161                 // overflow (max_int), causing illegal behavior.
  15162                 // For floats: either operand being undef makes the result undef.
  15163                 if (maybe_lhs_val) |lhs_val| {
  15164                     if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15165                         return casted_rhs;
  15166                     }
  15167                 }
  15168                 if (maybe_rhs_val) |rhs_val| {
  15169                     if (rhs_val.isUndef(mod)) {
  15170                         if (is_int) {
  15171                             return sema.failWithUseOfUndef(block, rhs_src);
  15172                         } else {
  15173                             return sema.addConstUndef(resolved_type);
  15174                         }
  15175                     }
  15176                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15177                         return casted_lhs;
  15178                     }
  15179                 }
  15180                 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .add_optimized else .add;
  15181                 if (maybe_lhs_val) |lhs_val| {
  15182                     if (lhs_val.isUndef(mod)) {
  15183                         if (is_int) {
  15184                             return sema.failWithUseOfUndef(block, lhs_src);
  15185                         } else {
  15186                             return sema.addConstUndef(resolved_type);
  15187                         }
  15188                     }
  15189                     if (maybe_rhs_val) |rhs_val| {
  15190                         if (is_int) {
  15191                             var overflow_idx: ?usize = null;
  15192                             const sum = try sema.intAdd(lhs_val, rhs_val, resolved_type, &overflow_idx);
  15193                             if (overflow_idx) |vec_idx| {
  15194                                 return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vec_idx);
  15195                             }
  15196                             return sema.addConstant(sum);
  15197                         } else {
  15198                             return sema.addConstant(
  15199                                 try Value.floatAdd(lhs_val, rhs_val, resolved_type, sema.arena, mod),
  15200                             );
  15201                         }
  15202                     } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .add_safe };
  15203                 } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .add_safe };
  15204             },
  15205             .addwrap => {
  15206                 // Integers only; floats are checked above.
  15207                 // If either of the operands are zero, the other operand is returned.
  15208                 // If either of the operands are undefined, the result is undefined.
  15209                 if (maybe_lhs_val) |lhs_val| {
  15210                     if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15211                         return casted_rhs;
  15212                     }
  15213                 }
  15214                 if (maybe_rhs_val) |rhs_val| {
  15215                     if (rhs_val.isUndef(mod)) {
  15216                         return sema.addConstUndef(resolved_type);
  15217                     }
  15218                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15219                         return casted_lhs;
  15220                     }
  15221                     if (maybe_lhs_val) |lhs_val| {
  15222                         return sema.addConstant(
  15223                             try sema.numberAddWrapScalar(lhs_val, rhs_val, resolved_type),
  15224                         );
  15225                     } else break :rs .{ .src = lhs_src, .air_tag = .add_wrap, .air_tag_safe = .add_wrap };
  15226                 } else break :rs .{ .src = rhs_src, .air_tag = .add_wrap, .air_tag_safe = .add_wrap };
  15227             },
  15228             .add_sat => {
  15229                 // Integers only; floats are checked above.
  15230                 // If either of the operands are zero, then the other operand is returned.
  15231                 // If either of the operands are undefined, the result is undefined.
  15232                 if (maybe_lhs_val) |lhs_val| {
  15233                     if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) {
  15234                         return casted_rhs;
  15235                     }
  15236                 }
  15237                 if (maybe_rhs_val) |rhs_val| {
  15238                     if (rhs_val.isUndef(mod)) {
  15239                         return sema.addConstUndef(resolved_type);
  15240                     }
  15241                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15242                         return casted_lhs;
  15243                     }
  15244                     if (maybe_lhs_val) |lhs_val| {
  15245                         const val = if (scalar_tag == .ComptimeInt)
  15246                             try sema.intAdd(lhs_val, rhs_val, resolved_type, undefined)
  15247                         else
  15248                             try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, mod);
  15249 
  15250                         return sema.addConstant(val);
  15251                     } else break :rs .{
  15252                         .src = lhs_src,
  15253                         .air_tag = .add_sat,
  15254                         .air_tag_safe = .add_sat,
  15255                     };
  15256                 } else break :rs .{
  15257                     .src = rhs_src,
  15258                     .air_tag = .add_sat,
  15259                     .air_tag_safe = .add_sat,
  15260                 };
  15261             },
  15262             .sub => {
  15263                 // For integers:
  15264                 // If the rhs is zero, then the other operand is
  15265                 // returned, even if it is undefined.
  15266                 // If either of the operands are undefined, it's a compile error
  15267                 // because there is a possible value for which the subtraction would
  15268                 // overflow, causing illegal behavior.
  15269                 // For floats: either operand being undef makes the result undef.
  15270                 if (maybe_rhs_val) |rhs_val| {
  15271                     if (rhs_val.isUndef(mod)) {
  15272                         if (is_int) {
  15273                             return sema.failWithUseOfUndef(block, rhs_src);
  15274                         } else {
  15275                             return sema.addConstUndef(resolved_type);
  15276                         }
  15277                     }
  15278                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15279                         return casted_lhs;
  15280                     }
  15281                 }
  15282                 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .sub_optimized else .sub;
  15283                 if (maybe_lhs_val) |lhs_val| {
  15284                     if (lhs_val.isUndef(mod)) {
  15285                         if (is_int) {
  15286                             return sema.failWithUseOfUndef(block, lhs_src);
  15287                         } else {
  15288                             return sema.addConstUndef(resolved_type);
  15289                         }
  15290                     }
  15291                     if (maybe_rhs_val) |rhs_val| {
  15292                         if (is_int) {
  15293                             var overflow_idx: ?usize = null;
  15294                             const diff = try sema.intSub(lhs_val, rhs_val, resolved_type, &overflow_idx);
  15295                             if (overflow_idx) |vec_idx| {
  15296                                 return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vec_idx);
  15297                             }
  15298                             return sema.addConstant(diff);
  15299                         } else {
  15300                             return sema.addConstant(
  15301                                 try Value.floatSub(lhs_val, rhs_val, resolved_type, sema.arena, mod),
  15302                             );
  15303                         }
  15304                     } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .sub_safe };
  15305                 } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .sub_safe };
  15306             },
  15307             .subwrap => {
  15308                 // Integers only; floats are checked above.
  15309                 // If the RHS is zero, then the other operand is returned, even if it is undefined.
  15310                 // If either of the operands are undefined, the result is undefined.
  15311                 if (maybe_rhs_val) |rhs_val| {
  15312                     if (rhs_val.isUndef(mod)) {
  15313                         return sema.addConstUndef(resolved_type);
  15314                     }
  15315                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15316                         return casted_lhs;
  15317                     }
  15318                 }
  15319                 if (maybe_lhs_val) |lhs_val| {
  15320                     if (lhs_val.isUndef(mod)) {
  15321                         return sema.addConstUndef(resolved_type);
  15322                     }
  15323                     if (maybe_rhs_val) |rhs_val| {
  15324                         return sema.addConstant(
  15325                             try sema.numberSubWrapScalar(lhs_val, rhs_val, resolved_type),
  15326                         );
  15327                     } else break :rs .{ .src = rhs_src, .air_tag = .sub_wrap, .air_tag_safe = .sub_wrap };
  15328                 } else break :rs .{ .src = lhs_src, .air_tag = .sub_wrap, .air_tag_safe = .sub_wrap };
  15329             },
  15330             .sub_sat => {
  15331                 // Integers only; floats are checked above.
  15332                 // If the RHS is zero, result is LHS.
  15333                 // If either of the operands are undefined, result is undefined.
  15334                 if (maybe_rhs_val) |rhs_val| {
  15335                     if (rhs_val.isUndef(mod)) {
  15336                         return sema.addConstUndef(resolved_type);
  15337                     }
  15338                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15339                         return casted_lhs;
  15340                     }
  15341                 }
  15342                 if (maybe_lhs_val) |lhs_val| {
  15343                     if (lhs_val.isUndef(mod)) {
  15344                         return sema.addConstUndef(resolved_type);
  15345                     }
  15346                     if (maybe_rhs_val) |rhs_val| {
  15347                         const val = if (scalar_tag == .ComptimeInt)
  15348                             try sema.intSub(lhs_val, rhs_val, resolved_type, undefined)
  15349                         else
  15350                             try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, mod);
  15351 
  15352                         return sema.addConstant(val);
  15353                     } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat, .air_tag_safe = .sub_sat };
  15354                 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat, .air_tag_safe = .sub_sat };
  15355             },
  15356             .mul => {
  15357                 // For integers:
  15358                 // If either of the operands are zero, the result is zero.
  15359                 // If either of the operands are one, the result is the other
  15360                 // operand, even if it is undefined.
  15361                 // If either of the operands are undefined, it's a compile error
  15362                 // because there is a possible value for which the addition would
  15363                 // overflow (max_int), causing illegal behavior.
  15364                 // For floats: either operand being undef makes the result undef.
  15365                 // If either of the operands are inf, and the other operand is zero,
  15366                 // the result is nan.
  15367                 // If either of the operands are nan, the result is nan.
  15368                 const scalar_zero = switch (scalar_tag) {
  15369                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0),
  15370                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 0),
  15371                     else => unreachable,
  15372                 };
  15373                 const scalar_one = switch (scalar_tag) {
  15374                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0),
  15375                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 1),
  15376                     else => unreachable,
  15377                 };
  15378                 if (maybe_lhs_val) |lhs_val| {
  15379                     if (!lhs_val.isUndef(mod)) {
  15380                         if (lhs_val.isNan(mod)) {
  15381                             return sema.addConstant(lhs_val);
  15382                         }
  15383                         if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) lz: {
  15384                             if (maybe_rhs_val) |rhs_val| {
  15385                                 if (rhs_val.isNan(mod)) {
  15386                                     return sema.addConstant(rhs_val);
  15387                                 }
  15388                                 if (rhs_val.isInf(mod)) {
  15389                                     return sema.addConstant(
  15390                                         try mod.floatValue(resolved_type, std.math.nan_f128),
  15391                                     );
  15392                                 }
  15393                             } else if (resolved_type.isAnyFloat()) {
  15394                                 break :lz;
  15395                             }
  15396                             const zero_val = try sema.splat(resolved_type, scalar_zero);
  15397                             return sema.addConstant(zero_val);
  15398                         }
  15399                         if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  15400                             return casted_rhs;
  15401                         }
  15402                     }
  15403                 }
  15404                 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .mul_optimized else .mul;
  15405                 if (maybe_rhs_val) |rhs_val| {
  15406                     if (rhs_val.isUndef(mod)) {
  15407                         if (is_int) {
  15408                             return sema.failWithUseOfUndef(block, rhs_src);
  15409                         } else {
  15410                             return sema.addConstUndef(resolved_type);
  15411                         }
  15412                     }
  15413                     if (rhs_val.isNan(mod)) {
  15414                         return sema.addConstant(rhs_val);
  15415                     }
  15416                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) rz: {
  15417                         if (maybe_lhs_val) |lhs_val| {
  15418                             if (lhs_val.isInf(mod)) {
  15419                                 return sema.addConstant(
  15420                                     try mod.floatValue(resolved_type, std.math.nan_f128),
  15421                                 );
  15422                             }
  15423                         } else if (resolved_type.isAnyFloat()) {
  15424                             break :rz;
  15425                         }
  15426                         const zero_val = try sema.splat(resolved_type, scalar_zero);
  15427                         return sema.addConstant(zero_val);
  15428                     }
  15429                     if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  15430                         return casted_lhs;
  15431                     }
  15432                     if (maybe_lhs_val) |lhs_val| {
  15433                         if (lhs_val.isUndef(mod)) {
  15434                             if (is_int) {
  15435                                 return sema.failWithUseOfUndef(block, lhs_src);
  15436                             } else {
  15437                                 return sema.addConstUndef(resolved_type);
  15438                             }
  15439                         }
  15440                         if (is_int) {
  15441                             var overflow_idx: ?usize = null;
  15442                             const product = try lhs_val.intMul(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
  15443                             if (overflow_idx) |vec_idx| {
  15444                                 return sema.failWithIntegerOverflow(block, src, resolved_type, product, vec_idx);
  15445                             }
  15446                             return sema.addConstant(product);
  15447                         } else {
  15448                             return sema.addConstant(
  15449                                 try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, mod),
  15450                             );
  15451                         }
  15452                     } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .mul_safe };
  15453                 } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .mul_safe };
  15454             },
  15455             .mulwrap => {
  15456                 // Integers only; floats are handled above.
  15457                 // If either of the operands are zero, result is zero.
  15458                 // If either of the operands are one, result is the other operand.
  15459                 // If either of the operands are undefined, result is undefined.
  15460                 const scalar_zero = switch (scalar_tag) {
  15461                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0),
  15462                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 0),
  15463                     else => unreachable,
  15464                 };
  15465                 const scalar_one = switch (scalar_tag) {
  15466                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0),
  15467                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 1),
  15468                     else => unreachable,
  15469                 };
  15470                 if (maybe_lhs_val) |lhs_val| {
  15471                     if (!lhs_val.isUndef(mod)) {
  15472                         if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15473                             const zero_val = try sema.splat(resolved_type, scalar_zero);
  15474                             return sema.addConstant(zero_val);
  15475                         }
  15476                         if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  15477                             return casted_rhs;
  15478                         }
  15479                     }
  15480                 }
  15481                 if (maybe_rhs_val) |rhs_val| {
  15482                     if (rhs_val.isUndef(mod)) {
  15483                         return sema.addConstUndef(resolved_type);
  15484                     }
  15485                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15486                         const zero_val = try sema.splat(resolved_type, scalar_zero);
  15487                         return sema.addConstant(zero_val);
  15488                     }
  15489                     if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  15490                         return casted_lhs;
  15491                     }
  15492                     if (maybe_lhs_val) |lhs_val| {
  15493                         if (lhs_val.isUndef(mod)) {
  15494                             return sema.addConstUndef(resolved_type);
  15495                         }
  15496                         return sema.addConstant(
  15497                             try lhs_val.numberMulWrap(rhs_val, resolved_type, sema.arena, mod),
  15498                         );
  15499                     } else break :rs .{ .src = lhs_src, .air_tag = .mul_wrap, .air_tag_safe = .mul_wrap };
  15500                 } else break :rs .{ .src = rhs_src, .air_tag = .mul_wrap, .air_tag_safe = .mul_wrap };
  15501             },
  15502             .mul_sat => {
  15503                 // Integers only; floats are checked above.
  15504                 // If either of the operands are zero, result is zero.
  15505                 // If either of the operands are one, result is the other operand.
  15506                 // If either of the operands are undefined, result is undefined.
  15507                 const scalar_zero = switch (scalar_tag) {
  15508                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0),
  15509                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 0),
  15510                     else => unreachable,
  15511                 };
  15512                 const scalar_one = switch (scalar_tag) {
  15513                     .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0),
  15514                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 1),
  15515                     else => unreachable,
  15516                 };
  15517                 if (maybe_lhs_val) |lhs_val| {
  15518                     if (!lhs_val.isUndef(mod)) {
  15519                         if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15520                             const zero_val = try sema.splat(resolved_type, scalar_zero);
  15521                             return sema.addConstant(zero_val);
  15522                         }
  15523                         if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  15524                             return casted_rhs;
  15525                         }
  15526                     }
  15527                 }
  15528                 if (maybe_rhs_val) |rhs_val| {
  15529                     if (rhs_val.isUndef(mod)) {
  15530                         return sema.addConstUndef(resolved_type);
  15531                     }
  15532                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
  15533                         const zero_val = try sema.splat(resolved_type, scalar_zero);
  15534                         return sema.addConstant(zero_val);
  15535                     }
  15536                     if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
  15537                         return casted_lhs;
  15538                     }
  15539                     if (maybe_lhs_val) |lhs_val| {
  15540                         if (lhs_val.isUndef(mod)) {
  15541                             return sema.addConstUndef(resolved_type);
  15542                         }
  15543 
  15544                         const val = if (scalar_tag == .ComptimeInt)
  15545                             try lhs_val.intMul(rhs_val, resolved_type, undefined, sema.arena, mod)
  15546                         else
  15547                             try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, mod);
  15548 
  15549                         return sema.addConstant(val);
  15550                     } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat, .air_tag_safe = .mul_sat };
  15551                 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat, .air_tag_safe = .mul_sat };
  15552             },
  15553             else => unreachable,
  15554         }
  15555     };
  15556 
  15557     try sema.requireRuntimeBlock(block, src, rs.src);
  15558     if (block.wantSafety() and want_safety and scalar_tag == .Int) {
  15559         if (mod.backendSupportsFeature(.safety_checked_instructions)) {
  15560             _ = try sema.preparePanicId(block, .integer_overflow);
  15561             return block.addBinOp(rs.air_tag_safe, casted_lhs, casted_rhs);
  15562         } else {
  15563             const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) {
  15564                 .add => .add_with_overflow,
  15565                 .sub => .sub_with_overflow,
  15566                 .mul => .mul_with_overflow,
  15567                 else => null,
  15568             };
  15569             if (maybe_op_ov) |op_ov_tag| {
  15570                 const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(resolved_type);
  15571                 const op_ov = try block.addInst(.{
  15572                     .tag = op_ov_tag,
  15573                     .data = .{ .ty_pl = .{
  15574                         .ty = try sema.addType(op_ov_tuple_ty),
  15575                         .payload = try sema.addExtra(Air.Bin{
  15576                             .lhs = casted_lhs,
  15577                             .rhs = casted_rhs,
  15578                         }),
  15579                     } },
  15580                 });
  15581                 const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);
  15582                 const any_ov_bit = if (resolved_type.zigTypeTag(mod) == .Vector)
  15583                     try block.addInst(.{
  15584                         .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  15585                         .data = .{ .reduce = .{
  15586                             .operand = ov_bit,
  15587                             .operation = .Or,
  15588                         } },
  15589                     })
  15590                 else
  15591                     ov_bit;
  15592                 const zero_ov = try sema.addConstant(try mod.intValue(Type.u1, 0));
  15593                 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
  15594 
  15595                 try sema.addSafetyCheck(block, no_ov, .integer_overflow);
  15596                 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
  15597             }
  15598         }
  15599     }
  15600     return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
  15601 }
  15602 
  15603 fn analyzePtrArithmetic(
  15604     sema: *Sema,
  15605     block: *Block,
  15606     op_src: LazySrcLoc,
  15607     ptr: Air.Inst.Ref,
  15608     uncasted_offset: Air.Inst.Ref,
  15609     air_tag: Air.Inst.Tag,
  15610     ptr_src: LazySrcLoc,
  15611     offset_src: LazySrcLoc,
  15612 ) CompileError!Air.Inst.Ref {
  15613     // TODO if the operand is comptime-known to be negative, or is a negative int,
  15614     // coerce to isize instead of usize.
  15615     const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src);
  15616     const mod = sema.mod;
  15617     const opt_ptr_val = try sema.resolveMaybeUndefVal(ptr);
  15618     const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset);
  15619     const ptr_ty = sema.typeOf(ptr);
  15620     const ptr_info = ptr_ty.ptrInfo(mod);
  15621     assert(ptr_info.flags.size == .Many or ptr_info.flags.size == .C);
  15622 
  15623     const new_ptr_ty = t: {
  15624         // Calculate the new pointer alignment.
  15625         // This code is duplicated in `elemPtrType`.
  15626         if (ptr_info.flags.alignment == .none) {
  15627             // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
  15628             break :t ptr_ty;
  15629         }
  15630         // If the addend is not a comptime-known value we can still count on
  15631         // it being a multiple of the type size.
  15632         const elem_size = ptr_info.child.toType().abiSize(mod);
  15633         const addend = if (opt_off_val) |off_val| a: {
  15634             const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(mod));
  15635             break :a elem_size * off_int;
  15636         } else elem_size;
  15637 
  15638         // The resulting pointer is aligned to the lcd between the offset (an
  15639         // arbitrary number) and the alignment factor (always a power of two,
  15640         // non zero).
  15641         const new_align = @as(Alignment, @enumFromInt(@min(
  15642             @ctz(addend),
  15643             @intFromEnum(ptr_info.flags.alignment),
  15644         )));
  15645         assert(new_align != .none);
  15646 
  15647         break :t try mod.ptrType(.{
  15648             .child = ptr_info.child,
  15649             .sentinel = ptr_info.sentinel,
  15650             .flags = .{
  15651                 .size = ptr_info.flags.size,
  15652                 .alignment = new_align,
  15653                 .is_const = ptr_info.flags.is_const,
  15654                 .is_volatile = ptr_info.flags.is_volatile,
  15655                 .is_allowzero = ptr_info.flags.is_allowzero,
  15656                 .address_space = ptr_info.flags.address_space,
  15657             },
  15658         });
  15659     };
  15660 
  15661     const runtime_src = rs: {
  15662         if (opt_ptr_val) |ptr_val| {
  15663             if (opt_off_val) |offset_val| {
  15664                 if (ptr_val.isUndef(mod)) return sema.addConstUndef(new_ptr_ty);
  15665 
  15666                 const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(mod));
  15667                 if (offset_int == 0) return ptr;
  15668                 if (try ptr_val.getUnsignedIntAdvanced(mod, sema)) |addr| {
  15669                     const elem_size = ptr_info.child.toType().abiSize(mod);
  15670                     const new_addr = switch (air_tag) {
  15671                         .ptr_add => addr + elem_size * offset_int,
  15672                         .ptr_sub => addr - elem_size * offset_int,
  15673                         else => unreachable,
  15674                     };
  15675                     const new_ptr_val = try mod.ptrIntValue(new_ptr_ty, new_addr);
  15676                     return sema.addConstant(new_ptr_val);
  15677                 }
  15678                 if (air_tag == .ptr_sub) {
  15679                     return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{});
  15680                 }
  15681                 const new_ptr_val = try ptr_val.elemPtr(new_ptr_ty, offset_int, mod);
  15682                 return sema.addConstant(new_ptr_val);
  15683             } else break :rs offset_src;
  15684         } else break :rs ptr_src;
  15685     };
  15686 
  15687     try sema.requireRuntimeBlock(block, op_src, runtime_src);
  15688     return block.addInst(.{
  15689         .tag = air_tag,
  15690         .data = .{ .ty_pl = .{
  15691             .ty = try sema.addType(new_ptr_ty),
  15692             .payload = try sema.addExtra(Air.Bin{
  15693                 .lhs = ptr,
  15694                 .rhs = offset,
  15695             }),
  15696         } },
  15697     });
  15698 }
  15699 
  15700 fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15701     const tracy = trace(@src());
  15702     defer tracy.end();
  15703 
  15704     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  15705     const src = inst_data.src();
  15706     const ptr_src = src; // TODO better source location
  15707     const ptr = try sema.resolveInst(inst_data.operand);
  15708     return sema.analyzeLoad(block, src, ptr, ptr_src);
  15709 }
  15710 
  15711 fn zirAsm(
  15712     sema: *Sema,
  15713     block: *Block,
  15714     extended: Zir.Inst.Extended.InstData,
  15715     tmpl_is_expr: bool,
  15716 ) CompileError!Air.Inst.Ref {
  15717     const tracy = trace(@src());
  15718     defer tracy.end();
  15719 
  15720     const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);
  15721     const src = LazySrcLoc.nodeOffset(extra.data.src_node);
  15722     const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node };
  15723     const outputs_len = @as(u5, @truncate(extended.small));
  15724     const inputs_len = @as(u5, @truncate(extended.small >> 5));
  15725     const clobbers_len = @as(u5, @truncate(extended.small >> 10));
  15726     const is_volatile = @as(u1, @truncate(extended.small >> 15)) != 0;
  15727     const is_global_assembly = sema.func_index == .none;
  15728 
  15729     const asm_source: []const u8 = if (tmpl_is_expr) blk: {
  15730         const tmpl = @as(Zir.Inst.Ref, @enumFromInt(extra.data.asm_source));
  15731         const s: []const u8 = try sema.resolveConstString(block, src, tmpl, "assembly code must be comptime-known");
  15732         break :blk s;
  15733     } else sema.code.nullTerminatedString(extra.data.asm_source);
  15734 
  15735     if (is_global_assembly) {
  15736         if (outputs_len != 0) {
  15737             return sema.fail(block, src, "module-level assembly does not support outputs", .{});
  15738         }
  15739         if (inputs_len != 0) {
  15740             return sema.fail(block, src, "module-level assembly does not support inputs", .{});
  15741         }
  15742         if (clobbers_len != 0) {
  15743             return sema.fail(block, src, "module-level assembly does not support clobbers", .{});
  15744         }
  15745         if (is_volatile) {
  15746             return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{});
  15747         }
  15748         try sema.mod.addGlobalAssembly(sema.owner_decl_index, asm_source);
  15749         return Air.Inst.Ref.void_value;
  15750     }
  15751 
  15752     if (block.is_comptime) {
  15753         try sema.requireRuntimeBlock(block, src, null);
  15754     }
  15755 
  15756     var extra_i = extra.end;
  15757     var output_type_bits = extra.data.output_type_bits;
  15758     var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len;
  15759 
  15760     const ConstraintName = struct { c: []const u8, n: []const u8 };
  15761     const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len);
  15762     const outputs = try sema.arena.alloc(ConstraintName, outputs_len);
  15763     var expr_ty = Air.Inst.Ref.void_type;
  15764 
  15765     for (out_args, 0..) |*arg, out_i| {
  15766         const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i);
  15767         extra_i = output.end;
  15768 
  15769         const is_type = @as(u1, @truncate(output_type_bits)) != 0;
  15770         output_type_bits >>= 1;
  15771 
  15772         if (is_type) {
  15773             // Indicate the output is the asm instruction return value.
  15774             arg.* = .none;
  15775             const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand);
  15776             try sema.queueFullTypeResolution(out_ty);
  15777             expr_ty = try sema.addType(out_ty);
  15778         } else {
  15779             arg.* = try sema.resolveInst(output.data.operand);
  15780         }
  15781 
  15782         const constraint = sema.code.nullTerminatedString(output.data.constraint);
  15783         const name = sema.code.nullTerminatedString(output.data.name);
  15784         needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
  15785 
  15786         outputs[out_i] = .{ .c = constraint, .n = name };
  15787     }
  15788 
  15789     const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len);
  15790     const inputs = try sema.arena.alloc(ConstraintName, inputs_len);
  15791     const mod = sema.mod;
  15792 
  15793     for (args, 0..) |*arg, arg_i| {
  15794         const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i);
  15795         extra_i = input.end;
  15796 
  15797         const uncasted_arg = try sema.resolveInst(input.data.operand);
  15798         const uncasted_arg_ty = sema.typeOf(uncasted_arg);
  15799         switch (uncasted_arg_ty.zigTypeTag(mod)) {
  15800             .ComptimeInt => arg.* = try sema.coerce(block, Type.usize, uncasted_arg, src),
  15801             .ComptimeFloat => arg.* = try sema.coerce(block, Type.f64, uncasted_arg, src),
  15802             else => {
  15803                 arg.* = uncasted_arg;
  15804                 try sema.queueFullTypeResolution(uncasted_arg_ty);
  15805             },
  15806         }
  15807 
  15808         const constraint = sema.code.nullTerminatedString(input.data.constraint);
  15809         const name = sema.code.nullTerminatedString(input.data.name);
  15810         needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
  15811         inputs[arg_i] = .{ .c = constraint, .n = name };
  15812     }
  15813 
  15814     const clobbers = try sema.arena.alloc([]const u8, clobbers_len);
  15815     for (clobbers) |*name| {
  15816         name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
  15817         extra_i += 1;
  15818 
  15819         needed_capacity += name.*.len / 4 + 1;
  15820     }
  15821 
  15822     needed_capacity += (asm_source.len + 3) / 4;
  15823 
  15824     const gpa = sema.gpa;
  15825     try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity);
  15826     const asm_air = try block.addInst(.{
  15827         .tag = .assembly,
  15828         .data = .{ .ty_pl = .{
  15829             .ty = expr_ty,
  15830             .payload = sema.addExtraAssumeCapacity(Air.Asm{
  15831                 .source_len = @as(u32, @intCast(asm_source.len)),
  15832                 .outputs_len = outputs_len,
  15833                 .inputs_len = @as(u32, @intCast(args.len)),
  15834                 .flags = (@as(u32, @intFromBool(is_volatile)) << 31) | @as(u32, @intCast(clobbers.len)),
  15835             }),
  15836         } },
  15837     });
  15838     sema.appendRefsAssumeCapacity(out_args);
  15839     sema.appendRefsAssumeCapacity(args);
  15840     for (outputs) |o| {
  15841         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  15842         @memcpy(buffer[0..o.c.len], o.c);
  15843         buffer[o.c.len] = 0;
  15844         @memcpy(buffer[o.c.len + 1 ..][0..o.n.len], o.n);
  15845         buffer[o.c.len + 1 + o.n.len] = 0;
  15846         sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4;
  15847     }
  15848     for (inputs) |input| {
  15849         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  15850         @memcpy(buffer[0..input.c.len], input.c);
  15851         buffer[input.c.len] = 0;
  15852         @memcpy(buffer[input.c.len + 1 ..][0..input.n.len], input.n);
  15853         buffer[input.c.len + 1 + input.n.len] = 0;
  15854         sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4;
  15855     }
  15856     for (clobbers) |clobber| {
  15857         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  15858         @memcpy(buffer[0..clobber.len], clobber);
  15859         buffer[clobber.len] = 0;
  15860         sema.air_extra.items.len += clobber.len / 4 + 1;
  15861     }
  15862     {
  15863         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  15864         @memcpy(buffer[0..asm_source.len], asm_source);
  15865         sema.air_extra.items.len += (asm_source.len + 3) / 4;
  15866     }
  15867     return asm_air;
  15868 }
  15869 
  15870 /// Only called for equality operators. See also `zirCmp`.
  15871 fn zirCmpEq(
  15872     sema: *Sema,
  15873     block: *Block,
  15874     inst: Zir.Inst.Index,
  15875     op: std.math.CompareOperator,
  15876     air_tag: Air.Inst.Tag,
  15877 ) CompileError!Air.Inst.Ref {
  15878     const tracy = trace(@src());
  15879     defer tracy.end();
  15880 
  15881     const mod = sema.mod;
  15882     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  15883     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15884     const src: LazySrcLoc = inst_data.src();
  15885     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  15886     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  15887     const lhs = try sema.resolveInst(extra.lhs);
  15888     const rhs = try sema.resolveInst(extra.rhs);
  15889 
  15890     const lhs_ty = sema.typeOf(lhs);
  15891     const rhs_ty = sema.typeOf(rhs);
  15892     const lhs_ty_tag = lhs_ty.zigTypeTag(mod);
  15893     const rhs_ty_tag = rhs_ty.zigTypeTag(mod);
  15894     if (lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
  15895         // null == null, null != null
  15896         if (op == .eq) {
  15897             return Air.Inst.Ref.bool_true;
  15898         } else {
  15899             return Air.Inst.Ref.bool_false;
  15900         }
  15901     }
  15902 
  15903     // comparing null with optionals
  15904     if (lhs_ty_tag == .Null and (rhs_ty_tag == .Optional or rhs_ty.isCPtr(mod))) {
  15905         return sema.analyzeIsNull(block, src, rhs, op == .neq);
  15906     }
  15907     if (rhs_ty_tag == .Null and (lhs_ty_tag == .Optional or lhs_ty.isCPtr(mod))) {
  15908         return sema.analyzeIsNull(block, src, lhs, op == .neq);
  15909     }
  15910 
  15911     if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
  15912         const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty;
  15913         return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(mod)});
  15914     }
  15915 
  15916     if (lhs_ty_tag == .Union and (rhs_ty_tag == .EnumLiteral or rhs_ty_tag == .Enum)) {
  15917         return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op);
  15918     }
  15919     if (rhs_ty_tag == .Union and (lhs_ty_tag == .EnumLiteral or lhs_ty_tag == .Enum)) {
  15920         return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op);
  15921     }
  15922 
  15923     if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
  15924         const runtime_src: LazySrcLoc = src: {
  15925             if (try sema.resolveMaybeUndefVal(lhs)) |lval| {
  15926                 if (try sema.resolveMaybeUndefVal(rhs)) |rval| {
  15927                     if (lval.isUndef(mod) or rval.isUndef(mod)) {
  15928                         return sema.addConstUndef(Type.bool);
  15929                     }
  15930                     const lkey = mod.intern_pool.indexToKey(lval.toIntern());
  15931                     const rkey = mod.intern_pool.indexToKey(rval.toIntern());
  15932                     if ((lkey.err.name == rkey.err.name) == (op == .eq)) {
  15933                         return Air.Inst.Ref.bool_true;
  15934                     } else {
  15935                         return Air.Inst.Ref.bool_false;
  15936                     }
  15937                 } else {
  15938                     break :src rhs_src;
  15939                 }
  15940             } else {
  15941                 break :src lhs_src;
  15942             }
  15943         };
  15944         try sema.requireRuntimeBlock(block, src, runtime_src);
  15945         return block.addBinOp(air_tag, lhs, rhs);
  15946     }
  15947     if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) {
  15948         const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs);
  15949         const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs);
  15950         if (lhs_as_type.eql(rhs_as_type, mod) == (op == .eq)) {
  15951             return Air.Inst.Ref.bool_true;
  15952         } else {
  15953             return Air.Inst.Ref.bool_false;
  15954         }
  15955     }
  15956     return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true);
  15957 }
  15958 
  15959 fn analyzeCmpUnionTag(
  15960     sema: *Sema,
  15961     block: *Block,
  15962     src: LazySrcLoc,
  15963     un: Air.Inst.Ref,
  15964     un_src: LazySrcLoc,
  15965     tag: Air.Inst.Ref,
  15966     tag_src: LazySrcLoc,
  15967     op: std.math.CompareOperator,
  15968 ) CompileError!Air.Inst.Ref {
  15969     const mod = sema.mod;
  15970     const union_ty = try sema.resolveTypeFields(sema.typeOf(un));
  15971     const union_tag_ty = union_ty.unionTagType(mod) orelse {
  15972         const msg = msg: {
  15973             const msg = try sema.errMsg(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{});
  15974             errdefer msg.destroy(sema.gpa);
  15975             try mod.errNoteNonLazy(union_ty.declSrcLoc(mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(mod)});
  15976             break :msg msg;
  15977         };
  15978         return sema.failWithOwnedErrorMsg(msg);
  15979     };
  15980     // Coerce both the union and the tag to the union's tag type, and then execute the
  15981     // enum comparison codepath.
  15982     const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src);
  15983     const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src);
  15984 
  15985     if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| {
  15986         if (enum_val.isUndef(mod)) return sema.addConstUndef(Type.bool);
  15987         const field_ty = union_ty.unionFieldType(enum_val, mod);
  15988         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  15989             return Air.Inst.Ref.bool_false;
  15990         }
  15991     }
  15992 
  15993     return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src);
  15994 }
  15995 
  15996 /// Only called for non-equality operators. See also `zirCmpEq`.
  15997 fn zirCmp(
  15998     sema: *Sema,
  15999     block: *Block,
  16000     inst: Zir.Inst.Index,
  16001     op: std.math.CompareOperator,
  16002 ) CompileError!Air.Inst.Ref {
  16003     const tracy = trace(@src());
  16004     defer tracy.end();
  16005 
  16006     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  16007     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  16008     const src: LazySrcLoc = inst_data.src();
  16009     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  16010     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  16011     const lhs = try sema.resolveInst(extra.lhs);
  16012     const rhs = try sema.resolveInst(extra.rhs);
  16013     return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false);
  16014 }
  16015 
  16016 fn analyzeCmp(
  16017     sema: *Sema,
  16018     block: *Block,
  16019     src: LazySrcLoc,
  16020     lhs: Air.Inst.Ref,
  16021     rhs: Air.Inst.Ref,
  16022     op: std.math.CompareOperator,
  16023     lhs_src: LazySrcLoc,
  16024     rhs_src: LazySrcLoc,
  16025     is_equality_cmp: bool,
  16026 ) CompileError!Air.Inst.Ref {
  16027     const mod = sema.mod;
  16028     const lhs_ty = sema.typeOf(lhs);
  16029     const rhs_ty = sema.typeOf(rhs);
  16030     if (lhs_ty.zigTypeTag(mod) != .Optional and rhs_ty.zigTypeTag(mod) != .Optional) {
  16031         try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  16032     }
  16033 
  16034     if (lhs_ty.zigTypeTag(mod) == .Vector and rhs_ty.zigTypeTag(mod) == .Vector) {
  16035         return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src);
  16036     }
  16037     if (lhs_ty.isNumeric(mod) and rhs_ty.isNumeric(mod)) {
  16038         // This operation allows any combination of integer and float types, regardless of the
  16039         // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
  16040         // numeric types.
  16041         return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src);
  16042     }
  16043     if (is_equality_cmp and lhs_ty.zigTypeTag(mod) == .ErrorUnion and rhs_ty.zigTypeTag(mod) == .ErrorSet) {
  16044         const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs);
  16045         return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src);
  16046     }
  16047     if (is_equality_cmp and lhs_ty.zigTypeTag(mod) == .ErrorSet and rhs_ty.zigTypeTag(mod) == .ErrorUnion) {
  16048         const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs);
  16049         return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src);
  16050     }
  16051     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  16052     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } });
  16053     if (!resolved_type.isSelfComparable(mod, is_equality_cmp)) {
  16054         return sema.fail(block, src, "operator {s} not allowed for type '{}'", .{
  16055             compareOperatorName(op), resolved_type.fmt(mod),
  16056         });
  16057     }
  16058     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  16059     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  16060     return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src);
  16061 }
  16062 
  16063 fn compareOperatorName(comp: std.math.CompareOperator) []const u8 {
  16064     return switch (comp) {
  16065         .lt => "<",
  16066         .lte => "<=",
  16067         .eq => "==",
  16068         .gte => ">=",
  16069         .gt => ">",
  16070         .neq => "!=",
  16071     };
  16072 }
  16073 
  16074 fn cmpSelf(
  16075     sema: *Sema,
  16076     block: *Block,
  16077     src: LazySrcLoc,
  16078     casted_lhs: Air.Inst.Ref,
  16079     casted_rhs: Air.Inst.Ref,
  16080     op: std.math.CompareOperator,
  16081     lhs_src: LazySrcLoc,
  16082     rhs_src: LazySrcLoc,
  16083 ) CompileError!Air.Inst.Ref {
  16084     const mod = sema.mod;
  16085     const resolved_type = sema.typeOf(casted_lhs);
  16086     const runtime_src: LazySrcLoc = src: {
  16087         if (try sema.resolveMaybeUndefVal(casted_lhs)) |lhs_val| {
  16088             if (lhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool);
  16089             if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| {
  16090                 if (rhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool);
  16091 
  16092                 if (resolved_type.zigTypeTag(mod) == .Vector) {
  16093                     const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type);
  16094                     return sema.addConstant(cmp_val);
  16095                 }
  16096 
  16097                 if (try sema.compareAll(lhs_val, op, rhs_val, resolved_type)) {
  16098                     return Air.Inst.Ref.bool_true;
  16099                 } else {
  16100                     return Air.Inst.Ref.bool_false;
  16101                 }
  16102             } else {
  16103                 if (resolved_type.zigTypeTag(mod) == .Bool) {
  16104                     // We can lower bool eq/neq more efficiently.
  16105                     return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src);
  16106                 }
  16107                 break :src rhs_src;
  16108             }
  16109         } else {
  16110             // For bools, we still check the other operand, because we can lower
  16111             // bool eq/neq more efficiently.
  16112             if (resolved_type.zigTypeTag(mod) == .Bool) {
  16113                 if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| {
  16114                     if (rhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool);
  16115                     return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);
  16116                 }
  16117             }
  16118             break :src lhs_src;
  16119         }
  16120     };
  16121     try sema.requireRuntimeBlock(block, src, runtime_src);
  16122     if (resolved_type.zigTypeTag(mod) == .Vector) {
  16123         return block.addCmpVector(casted_lhs, casted_rhs, op);
  16124     }
  16125     const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized);
  16126     return block.addBinOp(tag, casted_lhs, casted_rhs);
  16127 }
  16128 
  16129 /// cmp_eq (x, false) => not(x)
  16130 /// cmp_eq (x, true ) => x
  16131 /// cmp_neq(x, false) => x
  16132 /// cmp_neq(x, true ) => not(x)
  16133 fn runtimeBoolCmp(
  16134     sema: *Sema,
  16135     block: *Block,
  16136     src: LazySrcLoc,
  16137     op: std.math.CompareOperator,
  16138     lhs: Air.Inst.Ref,
  16139     rhs: bool,
  16140     runtime_src: LazySrcLoc,
  16141 ) CompileError!Air.Inst.Ref {
  16142     if ((op == .neq) == rhs) {
  16143         try sema.requireRuntimeBlock(block, src, runtime_src);
  16144         return block.addTyOp(.not, Type.bool, lhs);
  16145     } else {
  16146         return lhs;
  16147     }
  16148 }
  16149 
  16150 fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16151     const mod = sema.mod;
  16152     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  16153     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  16154     const ty = try sema.resolveType(block, operand_src, inst_data.operand);
  16155     switch (ty.zigTypeTag(mod)) {
  16156         .Fn,
  16157         .NoReturn,
  16158         .Undefined,
  16159         .Null,
  16160         .Opaque,
  16161         => return sema.fail(block, operand_src, "no size available for type '{}'", .{ty.fmt(mod)}),
  16162 
  16163         .Type,
  16164         .EnumLiteral,
  16165         .ComptimeFloat,
  16166         .ComptimeInt,
  16167         .Void,
  16168         => return sema.addIntUnsigned(Type.comptime_int, 0),
  16169 
  16170         .Bool,
  16171         .Int,
  16172         .Float,
  16173         .Pointer,
  16174         .Array,
  16175         .Struct,
  16176         .Optional,
  16177         .ErrorUnion,
  16178         .ErrorSet,
  16179         .Enum,
  16180         .Union,
  16181         .Vector,
  16182         .Frame,
  16183         .AnyFrame,
  16184         => {},
  16185     }
  16186     const val = try ty.lazyAbiSize(mod);
  16187     if (val.isLazySize(mod)) {
  16188         try sema.queueFullTypeResolution(ty);
  16189     }
  16190     return sema.addConstant(val);
  16191 }
  16192 
  16193 fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16194     const mod = sema.mod;
  16195     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  16196     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  16197     const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
  16198     switch (operand_ty.zigTypeTag(mod)) {
  16199         .Fn,
  16200         .NoReturn,
  16201         .Undefined,
  16202         .Null,
  16203         .Opaque,
  16204         => return sema.fail(block, operand_src, "no size available for type '{}'", .{operand_ty.fmt(mod)}),
  16205 
  16206         .Type,
  16207         .EnumLiteral,
  16208         .ComptimeFloat,
  16209         .ComptimeInt,
  16210         .Void,
  16211         => return sema.addIntUnsigned(Type.comptime_int, 0),
  16212 
  16213         .Bool,
  16214         .Int,
  16215         .Float,
  16216         .Pointer,
  16217         .Array,
  16218         .Struct,
  16219         .Optional,
  16220         .ErrorUnion,
  16221         .ErrorSet,
  16222         .Enum,
  16223         .Union,
  16224         .Vector,
  16225         .Frame,
  16226         .AnyFrame,
  16227         => {},
  16228     }
  16229     const bit_size = try operand_ty.bitSizeAdvanced(mod, sema);
  16230     return sema.addIntUnsigned(Type.comptime_int, bit_size);
  16231 }
  16232 
  16233 fn zirThis(
  16234     sema: *Sema,
  16235     block: *Block,
  16236     extended: Zir.Inst.Extended.InstData,
  16237 ) CompileError!Air.Inst.Ref {
  16238     const mod = sema.mod;
  16239     const this_decl_index = mod.namespaceDeclIndex(block.namespace);
  16240     const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand)));
  16241     return sema.analyzeDeclVal(block, src, this_decl_index);
  16242 }
  16243 
  16244 fn zirClosureCapture(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  16245     const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
  16246     // Closures are not necessarily constant values. For example, the
  16247     // code might do something like this:
  16248     // fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; }
  16249     // ...in which case the closure_capture instruction has access to a runtime
  16250     // value only. In such case we preserve the type and use a dummy runtime value.
  16251     const operand = try sema.resolveInst(inst_data.operand);
  16252     const ty = sema.typeOf(operand);
  16253     const capture: CaptureScope.Capture = blk: {
  16254         if (try sema.resolveMaybeUndefValAllowVariables(operand)) |val| {
  16255             const ip_index = try val.intern(ty, sema.mod);
  16256             break :blk .{ .comptime_val = ip_index };
  16257         }
  16258         break :blk .{ .runtime_val = ty.toIntern() };
  16259     };
  16260     try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, capture);
  16261 }
  16262 
  16263 fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16264     const mod = sema.mod;
  16265     const inst_data = sema.code.instructions.items(.data)[inst].inst_node;
  16266     var scope: *CaptureScope = mod.declPtr(block.src_decl).src_scope.?;
  16267     // Note: The target closure must be in this scope list.
  16268     // If it's not here, the zir is invalid, or the list is broken.
  16269     const capture = while (true) {
  16270         // Note: We don't need to add a dependency here, because
  16271         // decls always depend on their lexical parents.
  16272 
  16273         // Fail this decl if a scope it depended on failed.
  16274         if (scope.failed()) {
  16275             if (sema.owner_func) |owner_func| {
  16276                 owner_func.state = .dependency_failure;
  16277             } else {
  16278                 sema.owner_decl.analysis = .dependency_failure;
  16279             }
  16280             return error.AnalysisFail;
  16281         }
  16282         if (scope.captures.get(inst_data.inst)) |capture| {
  16283             break capture;
  16284         }
  16285         scope = scope.parent.?;
  16286     };
  16287 
  16288     if (capture == .runtime_val and !block.is_typeof and sema.func_index == .none) {
  16289         const msg = msg: {
  16290             const name = name: {
  16291                 const file = sema.owner_decl.getFileScope(mod);
  16292                 const tree = file.getTree(sema.gpa) catch |err| {
  16293                     // In this case we emit a warning + a less precise source location.
  16294                     log.warn("unable to load {s}: {s}", .{
  16295                         file.sub_file_path, @errorName(err),
  16296                     });
  16297                     break :name null;
  16298                 };
  16299                 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node);
  16300                 const token = tree.nodes.items(.main_token)[node];
  16301                 break :name tree.tokenSlice(token);
  16302             };
  16303 
  16304             const msg = if (name) |some|
  16305                 try sema.errMsg(block, inst_data.src(), "'{s}' not accessible outside function scope", .{some})
  16306             else
  16307                 try sema.errMsg(block, inst_data.src(), "variable not accessible outside function scope", .{});
  16308             errdefer msg.destroy(sema.gpa);
  16309 
  16310             // TODO add "declared here" note
  16311             break :msg msg;
  16312         };
  16313         return sema.failWithOwnedErrorMsg(msg);
  16314     }
  16315 
  16316     if (capture == .runtime_val and !block.is_typeof and !block.is_comptime and sema.func_index != .none) {
  16317         const msg = msg: {
  16318             const name = name: {
  16319                 const file = sema.owner_decl.getFileScope(mod);
  16320                 const tree = file.getTree(sema.gpa) catch |err| {
  16321                     // In this case we emit a warning + a less precise source location.
  16322                     log.warn("unable to load {s}: {s}", .{
  16323                         file.sub_file_path, @errorName(err),
  16324                     });
  16325                     break :name null;
  16326                 };
  16327                 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node);
  16328                 const token = tree.nodes.items(.main_token)[node];
  16329                 break :name tree.tokenSlice(token);
  16330             };
  16331 
  16332             const msg = if (name) |some|
  16333                 try sema.errMsg(block, inst_data.src(), "'{s}' not accessible from inner function", .{some})
  16334             else
  16335                 try sema.errMsg(block, inst_data.src(), "variable not accessible from inner function", .{});
  16336             errdefer msg.destroy(sema.gpa);
  16337 
  16338             try sema.errNote(block, LazySrcLoc.nodeOffset(0), msg, "crossed function definition here", .{});
  16339 
  16340             // TODO add "declared here" note
  16341             break :msg msg;
  16342         };
  16343         return sema.failWithOwnedErrorMsg(msg);
  16344     }
  16345 
  16346     switch (capture) {
  16347         .runtime_val => |ty_ip_index| {
  16348             assert(block.is_typeof);
  16349             // We need a dummy runtime instruction with the correct type.
  16350             return block.addTy(.alloc, ty_ip_index.toType());
  16351         },
  16352         .comptime_val => |val_ip_index| {
  16353             return sema.addConstant(val_ip_index.toValue());
  16354         },
  16355     }
  16356 }
  16357 
  16358 fn zirRetAddr(
  16359     sema: *Sema,
  16360     block: *Block,
  16361     extended: Zir.Inst.Extended.InstData,
  16362 ) CompileError!Air.Inst.Ref {
  16363     _ = extended;
  16364     if (block.is_comptime) {
  16365         // TODO: we could give a meaningful lazy value here. #14938
  16366         return sema.addIntUnsigned(Type.usize, 0);
  16367     } else {
  16368         return block.addNoOp(.ret_addr);
  16369     }
  16370 }
  16371 
  16372 fn zirFrameAddress(
  16373     sema: *Sema,
  16374     block: *Block,
  16375     extended: Zir.Inst.Extended.InstData,
  16376 ) CompileError!Air.Inst.Ref {
  16377     const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand)));
  16378     try sema.requireRuntimeBlock(block, src, null);
  16379     return try block.addNoOp(.frame_addr);
  16380 }
  16381 
  16382 fn zirBuiltinSrc(
  16383     sema: *Sema,
  16384     block: *Block,
  16385     extended: Zir.Inst.Extended.InstData,
  16386 ) CompileError!Air.Inst.Ref {
  16387     const tracy = trace(@src());
  16388     defer tracy.end();
  16389 
  16390     const mod = sema.mod;
  16391     const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data;
  16392     const src = LazySrcLoc.nodeOffset(extra.node);
  16393     const func = sema.func orelse return sema.fail(block, src, "@src outside function", .{});
  16394     const fn_owner_decl = mod.declPtr(func.owner_decl);
  16395 
  16396     const func_name_val = blk: {
  16397         var anon_decl = try block.startAnonDecl();
  16398         defer anon_decl.deinit();
  16399         // TODO: write something like getCoercedInts to avoid needing to dupe
  16400         const name = try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(fn_owner_decl.name));
  16401         const new_decl_ty = try mod.arrayType(.{
  16402             .len = name.len,
  16403             .sentinel = .zero_u8,
  16404             .child = .u8_type,
  16405         });
  16406         const new_decl = try anon_decl.finish(
  16407             new_decl_ty,
  16408             (try mod.intern(.{ .aggregate = .{
  16409                 .ty = new_decl_ty.toIntern(),
  16410                 .storage = .{ .bytes = name },
  16411             } })).toValue(),
  16412             .none, // default alignment
  16413         );
  16414         break :blk try mod.intern(.{ .ptr = .{
  16415             .ty = .slice_const_u8_sentinel_0_type,
  16416             .addr = .{ .decl = new_decl },
  16417             .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  16418         } });
  16419     };
  16420 
  16421     const file_name_val = blk: {
  16422         var anon_decl = try block.startAnonDecl();
  16423         defer anon_decl.deinit();
  16424         // The compiler must not call realpath anywhere.
  16425         const name = try fn_owner_decl.getFileScope(mod).fullPathZ(sema.arena);
  16426         const new_decl_ty = try mod.arrayType(.{
  16427             .len = name.len,
  16428             .sentinel = .zero_u8,
  16429             .child = .u8_type,
  16430         });
  16431         const new_decl = try anon_decl.finish(
  16432             new_decl_ty,
  16433             (try mod.intern(.{ .aggregate = .{
  16434                 .ty = new_decl_ty.toIntern(),
  16435                 .storage = .{ .bytes = name },
  16436             } })).toValue(),
  16437             .none, // default alignment
  16438         );
  16439         break :blk try mod.intern(.{ .ptr = .{
  16440             .ty = .slice_const_u8_sentinel_0_type,
  16441             .addr = .{ .decl = new_decl },
  16442             .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  16443         } });
  16444     };
  16445 
  16446     const src_loc_ty = try sema.getBuiltinType("SourceLocation");
  16447     const fields = .{
  16448         // file: [:0]const u8,
  16449         file_name_val,
  16450         // fn_name: [:0]const u8,
  16451         func_name_val,
  16452         // line: u32,
  16453         try mod.intern(.{ .runtime_value = .{
  16454             .ty = .u32_type,
  16455             .val = (try mod.intValue(Type.u32, extra.line + 1)).toIntern(),
  16456         } }),
  16457         // column: u32,
  16458         (try mod.intValue(Type.u32, extra.column + 1)).toIntern(),
  16459     };
  16460     return sema.addConstant((try mod.intern(.{ .aggregate = .{
  16461         .ty = src_loc_ty.toIntern(),
  16462         .storage = .{ .elems = &fields },
  16463     } })).toValue());
  16464 }
  16465 
  16466 fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16467     const mod = sema.mod;
  16468     const gpa = sema.gpa;
  16469     const ip = &mod.intern_pool;
  16470     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  16471     const src = inst_data.src();
  16472     const ty = try sema.resolveType(block, src, inst_data.operand);
  16473     const type_info_ty = try sema.getBuiltinType("Type");
  16474     const type_info_tag_ty = type_info_ty.unionTagType(mod).?;
  16475 
  16476     switch (ty.zigTypeTag(mod)) {
  16477         .Type,
  16478         .Void,
  16479         .Bool,
  16480         .NoReturn,
  16481         .ComptimeFloat,
  16482         .ComptimeInt,
  16483         .Undefined,
  16484         .Null,
  16485         .EnumLiteral,
  16486         => |type_info_tag| return sema.addConstant((try mod.intern(.{ .un = .{
  16487             .ty = type_info_ty.toIntern(),
  16488             .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(type_info_tag))).toIntern(),
  16489             .val = .void_value,
  16490         } })).toValue()),
  16491         .Fn => {
  16492             // TODO: look into memoizing this result.
  16493             var params_anon_decl = try block.startAnonDecl();
  16494             defer params_anon_decl.deinit();
  16495 
  16496             const fn_info_decl_index = (try sema.namespaceLookup(
  16497                 block,
  16498                 src,
  16499                 type_info_ty.getNamespaceIndex(mod).unwrap().?,
  16500                 try ip.getOrPutString(gpa, "Fn"),
  16501             )).?;
  16502             try mod.declareDeclDependency(sema.owner_decl_index, fn_info_decl_index);
  16503             try sema.ensureDeclAnalyzed(fn_info_decl_index);
  16504             const fn_info_decl = mod.declPtr(fn_info_decl_index);
  16505             const fn_info_ty = fn_info_decl.val.toType();
  16506 
  16507             const param_info_decl_index = (try sema.namespaceLookup(
  16508                 block,
  16509                 src,
  16510                 fn_info_ty.getNamespaceIndex(mod).unwrap().?,
  16511                 try ip.getOrPutString(gpa, "Param"),
  16512             )).?;
  16513             try mod.declareDeclDependency(sema.owner_decl_index, param_info_decl_index);
  16514             try sema.ensureDeclAnalyzed(param_info_decl_index);
  16515             const param_info_decl = mod.declPtr(param_info_decl_index);
  16516             const param_info_ty = param_info_decl.val.toType();
  16517 
  16518             const param_vals = try sema.arena.alloc(InternPool.Index, mod.typeToFunc(ty).?.param_types.len);
  16519             for (param_vals, 0..) |*param_val, i| {
  16520                 const info = mod.typeToFunc(ty).?;
  16521                 const param_ty = info.param_types[i];
  16522                 const is_generic = param_ty == .generic_poison_type;
  16523                 const param_ty_val = try ip.get(gpa, .{ .opt = .{
  16524                     .ty = try ip.get(gpa, .{ .opt_type = .type_type }),
  16525                     .val = if (is_generic) .none else param_ty,
  16526                 } });
  16527 
  16528                 const is_noalias = blk: {
  16529                     const index = std.math.cast(u5, i) orelse break :blk false;
  16530                     break :blk @as(u1, @truncate(info.noalias_bits >> index)) != 0;
  16531                 };
  16532 
  16533                 const param_fields = .{
  16534                     // is_generic: bool,
  16535                     Value.makeBool(is_generic).toIntern(),
  16536                     // is_noalias: bool,
  16537                     Value.makeBool(is_noalias).toIntern(),
  16538                     // type: ?type,
  16539                     param_ty_val,
  16540                 };
  16541                 param_val.* = try mod.intern(.{ .aggregate = .{
  16542                     .ty = param_info_ty.toIntern(),
  16543                     .storage = .{ .elems = &param_fields },
  16544                 } });
  16545             }
  16546 
  16547             const args_val = v: {
  16548                 const new_decl_ty = try mod.arrayType(.{
  16549                     .len = param_vals.len,
  16550                     .child = param_info_ty.toIntern(),
  16551                 });
  16552                 const new_decl = try params_anon_decl.finish(
  16553                     new_decl_ty,
  16554                     (try mod.intern(.{ .aggregate = .{
  16555                         .ty = new_decl_ty.toIntern(),
  16556                         .storage = .{ .elems = param_vals },
  16557                     } })).toValue(),
  16558                     .none, // default alignment
  16559                 );
  16560                 break :v try mod.intern(.{ .ptr = .{
  16561                     .ty = (try mod.ptrType(.{
  16562                         .child = param_info_ty.toIntern(),
  16563                         .flags = .{
  16564                             .size = .Slice,
  16565                             .is_const = true,
  16566                         },
  16567                     })).toIntern(),
  16568                     .addr = .{ .decl = new_decl },
  16569                     .len = (try mod.intValue(Type.usize, param_vals.len)).toIntern(),
  16570                 } });
  16571             };
  16572 
  16573             const info = mod.typeToFunc(ty).?;
  16574             const ret_ty_opt = try mod.intern(.{ .opt = .{
  16575                 .ty = try ip.get(gpa, .{ .opt_type = .type_type }),
  16576                 .val = if (info.return_type == .generic_poison_type) .none else info.return_type,
  16577             } });
  16578 
  16579             const callconv_ty = try sema.getBuiltinType("CallingConvention");
  16580 
  16581             const field_values = .{
  16582                 // calling_convention: CallingConvention,
  16583                 (try mod.enumValueFieldIndex(callconv_ty, @intFromEnum(info.cc))).toIntern(),
  16584                 // alignment: comptime_int,
  16585                 (try mod.intValue(Type.comptime_int, ty.abiAlignment(mod))).toIntern(),
  16586                 // is_generic: bool,
  16587                 Value.makeBool(info.is_generic).toIntern(),
  16588                 // is_var_args: bool,
  16589                 Value.makeBool(info.is_var_args).toIntern(),
  16590                 // return_type: ?type,
  16591                 ret_ty_opt,
  16592                 // args: []const Fn.Param,
  16593                 args_val,
  16594             };
  16595             return sema.addConstant((try mod.intern(.{ .un = .{
  16596                 .ty = type_info_ty.toIntern(),
  16597                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Fn))).toIntern(),
  16598                 .val = try mod.intern(.{ .aggregate = .{
  16599                     .ty = fn_info_ty.toIntern(),
  16600                     .storage = .{ .elems = &field_values },
  16601                 } }),
  16602             } })).toValue());
  16603         },
  16604         .Int => {
  16605             const int_info_decl_index = (try sema.namespaceLookup(
  16606                 block,
  16607                 src,
  16608                 type_info_ty.getNamespaceIndex(mod).unwrap().?,
  16609                 try ip.getOrPutString(gpa, "Int"),
  16610             )).?;
  16611             try mod.declareDeclDependency(sema.owner_decl_index, int_info_decl_index);
  16612             try sema.ensureDeclAnalyzed(int_info_decl_index);
  16613             const int_info_decl = mod.declPtr(int_info_decl_index);
  16614             const int_info_ty = int_info_decl.val.toType();
  16615 
  16616             const signedness_ty = try sema.getBuiltinType("Signedness");
  16617             const info = ty.intInfo(mod);
  16618             const field_values = .{
  16619                 // signedness: Signedness,
  16620                 try (try mod.enumValueFieldIndex(signedness_ty, @intFromEnum(info.signedness))).intern(signedness_ty, mod),
  16621                 // bits: u16,
  16622                 (try mod.intValue(Type.u16, info.bits)).toIntern(),
  16623             };
  16624             return sema.addConstant((try mod.intern(.{ .un = .{
  16625                 .ty = type_info_ty.toIntern(),
  16626                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Int))).toIntern(),
  16627                 .val = try mod.intern(.{ .aggregate = .{
  16628                     .ty = int_info_ty.toIntern(),
  16629                     .storage = .{ .elems = &field_values },
  16630                 } }),
  16631             } })).toValue());
  16632         },
  16633         .Float => {
  16634             const float_info_decl_index = (try sema.namespaceLookup(
  16635                 block,
  16636                 src,
  16637                 type_info_ty.getNamespaceIndex(mod).unwrap().?,
  16638                 try ip.getOrPutString(gpa, "Float"),
  16639             )).?;
  16640             try mod.declareDeclDependency(sema.owner_decl_index, float_info_decl_index);
  16641             try sema.ensureDeclAnalyzed(float_info_decl_index);
  16642             const float_info_decl = mod.declPtr(float_info_decl_index);
  16643             const float_info_ty = float_info_decl.val.toType();
  16644 
  16645             const field_vals = .{
  16646                 // bits: u16,
  16647                 (try mod.intValue(Type.u16, ty.bitSize(mod))).toIntern(),
  16648             };
  16649             return sema.addConstant((try mod.intern(.{ .un = .{
  16650                 .ty = type_info_ty.toIntern(),
  16651                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Float))).toIntern(),
  16652                 .val = try mod.intern(.{ .aggregate = .{
  16653                     .ty = float_info_ty.toIntern(),
  16654                     .storage = .{ .elems = &field_vals },
  16655                 } }),
  16656             } })).toValue());
  16657         },
  16658         .Pointer => {
  16659             const info = ty.ptrInfo(mod);
  16660             const alignment = if (info.flags.alignment.toByteUnitsOptional()) |alignment|
  16661                 try mod.intValue(Type.comptime_int, alignment)
  16662             else
  16663                 try info.child.toType().lazyAbiAlignment(mod);
  16664 
  16665             const addrspace_ty = try sema.getBuiltinType("AddressSpace");
  16666             const pointer_ty = t: {
  16667                 const decl_index = (try sema.namespaceLookup(
  16668                     block,
  16669                     src,
  16670                     (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?,
  16671                     try ip.getOrPutString(gpa, "Pointer"),
  16672                 )).?;
  16673                 try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
  16674                 try sema.ensureDeclAnalyzed(decl_index);
  16675                 const decl = mod.declPtr(decl_index);
  16676                 break :t decl.val.toType();
  16677             };
  16678             const ptr_size_ty = t: {
  16679                 const decl_index = (try sema.namespaceLookup(
  16680                     block,
  16681                     src,
  16682                     pointer_ty.getNamespaceIndex(mod).unwrap().?,
  16683                     try ip.getOrPutString(gpa, "Size"),
  16684                 )).?;
  16685                 try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
  16686                 try sema.ensureDeclAnalyzed(decl_index);
  16687                 const decl = mod.declPtr(decl_index);
  16688                 break :t decl.val.toType();
  16689             };
  16690 
  16691             const field_values = .{
  16692                 // size: Size,
  16693                 try (try mod.enumValueFieldIndex(ptr_size_ty, @intFromEnum(info.flags.size))).intern(ptr_size_ty, mod),
  16694                 // is_const: bool,
  16695                 Value.makeBool(info.flags.is_const).toIntern(),
  16696                 // is_volatile: bool,
  16697                 Value.makeBool(info.flags.is_volatile).toIntern(),
  16698                 // alignment: comptime_int,
  16699                 alignment.toIntern(),
  16700                 // address_space: AddressSpace
  16701                 try (try mod.enumValueFieldIndex(addrspace_ty, @intFromEnum(info.flags.address_space))).intern(addrspace_ty, mod),
  16702                 // child: type,
  16703                 info.child,
  16704                 // is_allowzero: bool,
  16705                 Value.makeBool(info.flags.is_allowzero).toIntern(),
  16706                 // sentinel: ?*const anyopaque,
  16707                 (try sema.optRefValue(block, info.child.toType(), switch (info.sentinel) {
  16708                     .none => null,
  16709                     else => info.sentinel.toValue(),
  16710                 })).toIntern(),
  16711             };
  16712             return sema.addConstant((try mod.intern(.{ .un = .{
  16713                 .ty = type_info_ty.toIntern(),
  16714                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Pointer))).toIntern(),
  16715                 .val = try mod.intern(.{ .aggregate = .{
  16716                     .ty = pointer_ty.toIntern(),
  16717                     .storage = .{ .elems = &field_values },
  16718                 } }),
  16719             } })).toValue());
  16720         },
  16721         .Array => {
  16722             const array_field_ty = t: {
  16723                 const array_field_ty_decl_index = (try sema.namespaceLookup(
  16724                     block,
  16725                     src,
  16726                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  16727                     try ip.getOrPutString(gpa, "Array"),
  16728                 )).?;
  16729                 try mod.declareDeclDependency(sema.owner_decl_index, array_field_ty_decl_index);
  16730                 try sema.ensureDeclAnalyzed(array_field_ty_decl_index);
  16731                 const array_field_ty_decl = mod.declPtr(array_field_ty_decl_index);
  16732                 break :t array_field_ty_decl.val.toType();
  16733             };
  16734 
  16735             const info = ty.arrayInfo(mod);
  16736             const field_values = .{
  16737                 // len: comptime_int,
  16738                 (try mod.intValue(Type.comptime_int, info.len)).toIntern(),
  16739                 // child: type,
  16740                 info.elem_type.toIntern(),
  16741                 // sentinel: ?*const anyopaque,
  16742                 (try sema.optRefValue(block, info.elem_type, info.sentinel)).toIntern(),
  16743             };
  16744             return sema.addConstant((try mod.intern(.{ .un = .{
  16745                 .ty = type_info_ty.toIntern(),
  16746                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Array))).toIntern(),
  16747                 .val = try mod.intern(.{ .aggregate = .{
  16748                     .ty = array_field_ty.toIntern(),
  16749                     .storage = .{ .elems = &field_values },
  16750                 } }),
  16751             } })).toValue());
  16752         },
  16753         .Vector => {
  16754             const vector_field_ty = t: {
  16755                 const vector_field_ty_decl_index = (try sema.namespaceLookup(
  16756                     block,
  16757                     src,
  16758                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  16759                     try ip.getOrPutString(gpa, "Vector"),
  16760                 )).?;
  16761                 try mod.declareDeclDependency(sema.owner_decl_index, vector_field_ty_decl_index);
  16762                 try sema.ensureDeclAnalyzed(vector_field_ty_decl_index);
  16763                 const vector_field_ty_decl = mod.declPtr(vector_field_ty_decl_index);
  16764                 break :t vector_field_ty_decl.val.toType();
  16765             };
  16766 
  16767             const info = ty.arrayInfo(mod);
  16768             const field_values = .{
  16769                 // len: comptime_int,
  16770                 (try mod.intValue(Type.comptime_int, info.len)).toIntern(),
  16771                 // child: type,
  16772                 info.elem_type.toIntern(),
  16773             };
  16774             return sema.addConstant((try mod.intern(.{ .un = .{
  16775                 .ty = type_info_ty.toIntern(),
  16776                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Vector))).toIntern(),
  16777                 .val = try mod.intern(.{ .aggregate = .{
  16778                     .ty = vector_field_ty.toIntern(),
  16779                     .storage = .{ .elems = &field_values },
  16780                 } }),
  16781             } })).toValue());
  16782         },
  16783         .Optional => {
  16784             const optional_field_ty = t: {
  16785                 const optional_field_ty_decl_index = (try sema.namespaceLookup(
  16786                     block,
  16787                     src,
  16788                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  16789                     try ip.getOrPutString(gpa, "Optional"),
  16790                 )).?;
  16791                 try mod.declareDeclDependency(sema.owner_decl_index, optional_field_ty_decl_index);
  16792                 try sema.ensureDeclAnalyzed(optional_field_ty_decl_index);
  16793                 const optional_field_ty_decl = mod.declPtr(optional_field_ty_decl_index);
  16794                 break :t optional_field_ty_decl.val.toType();
  16795             };
  16796 
  16797             const field_values = .{
  16798                 // child: type,
  16799                 ty.optionalChild(mod).toIntern(),
  16800             };
  16801             return sema.addConstant((try mod.intern(.{ .un = .{
  16802                 .ty = type_info_ty.toIntern(),
  16803                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Optional))).toIntern(),
  16804                 .val = try mod.intern(.{ .aggregate = .{
  16805                     .ty = optional_field_ty.toIntern(),
  16806                     .storage = .{ .elems = &field_values },
  16807                 } }),
  16808             } })).toValue());
  16809         },
  16810         .ErrorSet => {
  16811             var fields_anon_decl = try block.startAnonDecl();
  16812             defer fields_anon_decl.deinit();
  16813 
  16814             // Get the Error type
  16815             const error_field_ty = t: {
  16816                 const set_field_ty_decl_index = (try sema.namespaceLookup(
  16817                     block,
  16818                     src,
  16819                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  16820                     try ip.getOrPutString(gpa, "Error"),
  16821                 )).?;
  16822                 try mod.declareDeclDependency(sema.owner_decl_index, set_field_ty_decl_index);
  16823                 try sema.ensureDeclAnalyzed(set_field_ty_decl_index);
  16824                 const set_field_ty_decl = mod.declPtr(set_field_ty_decl_index);
  16825                 break :t set_field_ty_decl.val.toType();
  16826             };
  16827 
  16828             try sema.queueFullTypeResolution(error_field_ty);
  16829 
  16830             // If the error set is inferred it must be resolved at this point
  16831             try sema.resolveInferredErrorSetTy(block, src, ty);
  16832 
  16833             // Build our list of Error values
  16834             // Optional value is only null if anyerror
  16835             // Value can be zero-length slice otherwise
  16836             const error_field_vals = if (ty.isAnyError(mod)) null else blk: {
  16837                 const vals = try sema.arena.alloc(InternPool.Index, ty.errorSetNames(mod).len);
  16838                 for (vals, 0..) |*field_val, i| {
  16839                     // TODO: write something like getCoercedInts to avoid needing to dupe
  16840                     const name = try sema.arena.dupe(u8, ip.stringToSlice(ty.errorSetNames(mod)[i]));
  16841                     const name_val = v: {
  16842                         var anon_decl = try block.startAnonDecl();
  16843                         defer anon_decl.deinit();
  16844                         const new_decl_ty = try mod.arrayType(.{
  16845                             .len = name.len,
  16846                             .child = .u8_type,
  16847                         });
  16848                         const new_decl = try anon_decl.finish(
  16849                             new_decl_ty,
  16850                             (try mod.intern(.{ .aggregate = .{
  16851                                 .ty = new_decl_ty.toIntern(),
  16852                                 .storage = .{ .bytes = name },
  16853                             } })).toValue(),
  16854                             .none, // default alignment
  16855                         );
  16856                         break :v try mod.intern(.{ .ptr = .{
  16857                             .ty = .slice_const_u8_type,
  16858                             .addr = .{ .decl = new_decl },
  16859                             .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  16860                         } });
  16861                     };
  16862 
  16863                     const error_field_fields = .{
  16864                         // name: []const u8,
  16865                         name_val,
  16866                     };
  16867                     field_val.* = try mod.intern(.{ .aggregate = .{
  16868                         .ty = error_field_ty.toIntern(),
  16869                         .storage = .{ .elems = &error_field_fields },
  16870                     } });
  16871                 }
  16872 
  16873                 break :blk vals;
  16874             };
  16875 
  16876             // Build our ?[]const Error value
  16877             const slice_errors_ty = try mod.ptrType(.{
  16878                 .child = error_field_ty.toIntern(),
  16879                 .flags = .{
  16880                     .size = .Slice,
  16881                     .is_const = true,
  16882                 },
  16883             });
  16884             const opt_slice_errors_ty = try mod.optionalType(slice_errors_ty.toIntern());
  16885             const errors_payload_val: InternPool.Index = if (error_field_vals) |vals| v: {
  16886                 const array_errors_ty = try mod.arrayType(.{
  16887                     .len = vals.len,
  16888                     .child = error_field_ty.toIntern(),
  16889                 });
  16890                 const new_decl = try fields_anon_decl.finish(
  16891                     array_errors_ty,
  16892                     (try mod.intern(.{ .aggregate = .{
  16893                         .ty = array_errors_ty.toIntern(),
  16894                         .storage = .{ .elems = vals },
  16895                     } })).toValue(),
  16896                     .none, // default alignment
  16897                 );
  16898                 break :v try mod.intern(.{ .ptr = .{
  16899                     .ty = slice_errors_ty.toIntern(),
  16900                     .addr = .{ .decl = new_decl },
  16901                     .len = (try mod.intValue(Type.usize, vals.len)).toIntern(),
  16902                 } });
  16903             } else .none;
  16904             const errors_val = try mod.intern(.{ .opt = .{
  16905                 .ty = opt_slice_errors_ty.toIntern(),
  16906                 .val = errors_payload_val,
  16907             } });
  16908 
  16909             // Construct Type{ .ErrorSet = errors_val }
  16910             return sema.addConstant((try mod.intern(.{ .un = .{
  16911                 .ty = type_info_ty.toIntern(),
  16912                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.ErrorSet))).toIntern(),
  16913                 .val = errors_val,
  16914             } })).toValue());
  16915         },
  16916         .ErrorUnion => {
  16917             const error_union_field_ty = t: {
  16918                 const error_union_field_ty_decl_index = (try sema.namespaceLookup(
  16919                     block,
  16920                     src,
  16921                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  16922                     try ip.getOrPutString(gpa, "ErrorUnion"),
  16923                 )).?;
  16924                 try mod.declareDeclDependency(sema.owner_decl_index, error_union_field_ty_decl_index);
  16925                 try sema.ensureDeclAnalyzed(error_union_field_ty_decl_index);
  16926                 const error_union_field_ty_decl = mod.declPtr(error_union_field_ty_decl_index);
  16927                 break :t error_union_field_ty_decl.val.toType();
  16928             };
  16929 
  16930             const field_values = .{
  16931                 // error_set: type,
  16932                 ty.errorUnionSet(mod).toIntern(),
  16933                 // payload: type,
  16934                 ty.errorUnionPayload(mod).toIntern(),
  16935             };
  16936             return sema.addConstant((try mod.intern(.{ .un = .{
  16937                 .ty = type_info_ty.toIntern(),
  16938                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.ErrorUnion))).toIntern(),
  16939                 .val = try mod.intern(.{ .aggregate = .{
  16940                     .ty = error_union_field_ty.toIntern(),
  16941                     .storage = .{ .elems = &field_values },
  16942                 } }),
  16943             } })).toValue());
  16944         },
  16945         .Enum => {
  16946             // TODO: look into memoizing this result.
  16947             const is_exhaustive = Value.makeBool(ip.indexToKey(ty.toIntern()).enum_type.tag_mode != .nonexhaustive);
  16948 
  16949             var fields_anon_decl = try block.startAnonDecl();
  16950             defer fields_anon_decl.deinit();
  16951 
  16952             const enum_field_ty = t: {
  16953                 const enum_field_ty_decl_index = (try sema.namespaceLookup(
  16954                     block,
  16955                     src,
  16956                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  16957                     try ip.getOrPutString(gpa, "EnumField"),
  16958                 )).?;
  16959                 try mod.declareDeclDependency(sema.owner_decl_index, enum_field_ty_decl_index);
  16960                 try sema.ensureDeclAnalyzed(enum_field_ty_decl_index);
  16961                 const enum_field_ty_decl = mod.declPtr(enum_field_ty_decl_index);
  16962                 break :t enum_field_ty_decl.val.toType();
  16963             };
  16964 
  16965             const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.indexToKey(ty.toIntern()).enum_type.names.len);
  16966             for (enum_field_vals, 0..) |*field_val, i| {
  16967                 const enum_type = ip.indexToKey(ty.toIntern()).enum_type;
  16968                 const value_val = if (enum_type.values.len > 0)
  16969                     try mod.intern_pool.getCoerced(gpa, enum_type.values[i], .comptime_int_type)
  16970                 else
  16971                     try mod.intern(.{ .int = .{
  16972                         .ty = .comptime_int_type,
  16973                         .storage = .{ .u64 = @as(u64, @intCast(i)) },
  16974                     } });
  16975                 // TODO: write something like getCoercedInts to avoid needing to dupe
  16976                 const name = try sema.arena.dupe(u8, ip.stringToSlice(enum_type.names[i]));
  16977                 const name_val = v: {
  16978                     var anon_decl = try block.startAnonDecl();
  16979                     defer anon_decl.deinit();
  16980                     const new_decl_ty = try mod.arrayType(.{
  16981                         .len = name.len,
  16982                         .child = .u8_type,
  16983                     });
  16984                     const new_decl = try anon_decl.finish(
  16985                         new_decl_ty,
  16986                         (try mod.intern(.{ .aggregate = .{
  16987                             .ty = new_decl_ty.toIntern(),
  16988                             .storage = .{ .bytes = name },
  16989                         } })).toValue(),
  16990                         .none, // default alignment
  16991                     );
  16992                     break :v try mod.intern(.{ .ptr = .{
  16993                         .ty = .slice_const_u8_type,
  16994                         .addr = .{ .decl = new_decl },
  16995                         .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  16996                     } });
  16997                 };
  16998 
  16999                 const enum_field_fields = .{
  17000                     // name: []const u8,
  17001                     name_val,
  17002                     // value: comptime_int,
  17003                     value_val,
  17004                 };
  17005                 field_val.* = try mod.intern(.{ .aggregate = .{
  17006                     .ty = enum_field_ty.toIntern(),
  17007                     .storage = .{ .elems = &enum_field_fields },
  17008                 } });
  17009             }
  17010 
  17011             const fields_val = v: {
  17012                 const fields_array_ty = try mod.arrayType(.{
  17013                     .len = enum_field_vals.len,
  17014                     .child = enum_field_ty.toIntern(),
  17015                 });
  17016                 const new_decl = try fields_anon_decl.finish(
  17017                     fields_array_ty,
  17018                     (try mod.intern(.{ .aggregate = .{
  17019                         .ty = fields_array_ty.toIntern(),
  17020                         .storage = .{ .elems = enum_field_vals },
  17021                     } })).toValue(),
  17022                     .none, // default alignment
  17023                 );
  17024                 break :v try mod.intern(.{ .ptr = .{
  17025                     .ty = (try mod.ptrType(.{
  17026                         .child = enum_field_ty.toIntern(),
  17027                         .flags = .{
  17028                             .size = .Slice,
  17029                             .is_const = true,
  17030                         },
  17031                     })).toIntern(),
  17032                     .addr = .{ .decl = new_decl },
  17033                     .len = (try mod.intValue(Type.usize, enum_field_vals.len)).toIntern(),
  17034                 } });
  17035             };
  17036 
  17037             const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ip.indexToKey(ty.toIntern()).enum_type.namespace);
  17038 
  17039             const type_enum_ty = t: {
  17040                 const type_enum_ty_decl_index = (try sema.namespaceLookup(
  17041                     block,
  17042                     src,
  17043                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17044                     try ip.getOrPutString(gpa, "Enum"),
  17045                 )).?;
  17046                 try mod.declareDeclDependency(sema.owner_decl_index, type_enum_ty_decl_index);
  17047                 try sema.ensureDeclAnalyzed(type_enum_ty_decl_index);
  17048                 const type_enum_ty_decl = mod.declPtr(type_enum_ty_decl_index);
  17049                 break :t type_enum_ty_decl.val.toType();
  17050             };
  17051 
  17052             const field_values = .{
  17053                 // tag_type: type,
  17054                 ip.indexToKey(ty.toIntern()).enum_type.tag_ty,
  17055                 // fields: []const EnumField,
  17056                 fields_val,
  17057                 // decls: []const Declaration,
  17058                 decls_val,
  17059                 // is_exhaustive: bool,
  17060                 is_exhaustive.toIntern(),
  17061             };
  17062             return sema.addConstant((try mod.intern(.{ .un = .{
  17063                 .ty = type_info_ty.toIntern(),
  17064                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Enum))).toIntern(),
  17065                 .val = try mod.intern(.{ .aggregate = .{
  17066                     .ty = type_enum_ty.toIntern(),
  17067                     .storage = .{ .elems = &field_values },
  17068                 } }),
  17069             } })).toValue());
  17070         },
  17071         .Union => {
  17072             // TODO: look into memoizing this result.
  17073 
  17074             var fields_anon_decl = try block.startAnonDecl();
  17075             defer fields_anon_decl.deinit();
  17076 
  17077             const type_union_ty = t: {
  17078                 const type_union_ty_decl_index = (try sema.namespaceLookup(
  17079                     block,
  17080                     src,
  17081                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17082                     try ip.getOrPutString(gpa, "Union"),
  17083                 )).?;
  17084                 try mod.declareDeclDependency(sema.owner_decl_index, type_union_ty_decl_index);
  17085                 try sema.ensureDeclAnalyzed(type_union_ty_decl_index);
  17086                 const type_union_ty_decl = mod.declPtr(type_union_ty_decl_index);
  17087                 break :t type_union_ty_decl.val.toType();
  17088             };
  17089 
  17090             const union_field_ty = t: {
  17091                 const union_field_ty_decl_index = (try sema.namespaceLookup(
  17092                     block,
  17093                     src,
  17094                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17095                     try ip.getOrPutString(gpa, "UnionField"),
  17096                 )).?;
  17097                 try mod.declareDeclDependency(sema.owner_decl_index, union_field_ty_decl_index);
  17098                 try sema.ensureDeclAnalyzed(union_field_ty_decl_index);
  17099                 const union_field_ty_decl = mod.declPtr(union_field_ty_decl_index);
  17100                 break :t union_field_ty_decl.val.toType();
  17101             };
  17102 
  17103             const union_ty = try sema.resolveTypeFields(ty);
  17104             try sema.resolveTypeLayout(ty); // Getting alignment requires type layout
  17105             const layout = union_ty.containerLayout(mod);
  17106 
  17107             const union_fields = union_ty.unionFields(mod);
  17108             const union_field_vals = try gpa.alloc(InternPool.Index, union_fields.count());
  17109             defer gpa.free(union_field_vals);
  17110 
  17111             for (union_field_vals, 0..) |*field_val, i| {
  17112                 const field = union_fields.values()[i];
  17113                 // TODO: write something like getCoercedInts to avoid needing to dupe
  17114                 const name = try sema.arena.dupe(u8, ip.stringToSlice(union_fields.keys()[i]));
  17115                 const name_val = v: {
  17116                     var anon_decl = try block.startAnonDecl();
  17117                     defer anon_decl.deinit();
  17118                     const new_decl_ty = try mod.arrayType(.{
  17119                         .len = name.len,
  17120                         .child = .u8_type,
  17121                     });
  17122                     const new_decl = try anon_decl.finish(
  17123                         new_decl_ty,
  17124                         (try mod.intern(.{ .aggregate = .{
  17125                             .ty = new_decl_ty.toIntern(),
  17126                             .storage = .{ .bytes = name },
  17127                         } })).toValue(),
  17128                         .none, // default alignment
  17129                     );
  17130                     break :v try mod.intern(.{ .ptr = .{
  17131                         .ty = .slice_const_u8_type,
  17132                         .addr = .{ .decl = new_decl },
  17133                         .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  17134                     } });
  17135                 };
  17136 
  17137                 const alignment = switch (layout) {
  17138                     .Auto, .Extern => try sema.unionFieldAlignment(field),
  17139                     .Packed => 0,
  17140                 };
  17141 
  17142                 const union_field_fields = .{
  17143                     // name: []const u8,
  17144                     name_val,
  17145                     // type: type,
  17146                     field.ty.toIntern(),
  17147                     // alignment: comptime_int,
  17148                     (try mod.intValue(Type.comptime_int, alignment)).toIntern(),
  17149                 };
  17150                 field_val.* = try mod.intern(.{ .aggregate = .{
  17151                     .ty = union_field_ty.toIntern(),
  17152                     .storage = .{ .elems = &union_field_fields },
  17153                 } });
  17154             }
  17155 
  17156             const fields_val = v: {
  17157                 const array_fields_ty = try mod.arrayType(.{
  17158                     .len = union_field_vals.len,
  17159                     .child = union_field_ty.toIntern(),
  17160                 });
  17161                 const new_decl = try fields_anon_decl.finish(
  17162                     array_fields_ty,
  17163                     (try mod.intern(.{ .aggregate = .{
  17164                         .ty = array_fields_ty.toIntern(),
  17165                         .storage = .{ .elems = union_field_vals },
  17166                     } })).toValue(),
  17167                     .none, // default alignment
  17168                 );
  17169                 break :v try mod.intern(.{ .ptr = .{
  17170                     .ty = (try mod.ptrType(.{
  17171                         .child = union_field_ty.toIntern(),
  17172                         .flags = .{
  17173                             .size = .Slice,
  17174                             .is_const = true,
  17175                         },
  17176                     })).toIntern(),
  17177                     .addr = .{ .decl = new_decl },
  17178                     .len = (try mod.intValue(Type.usize, union_field_vals.len)).toIntern(),
  17179                 } });
  17180             };
  17181 
  17182             const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespaceIndex(mod));
  17183 
  17184             const enum_tag_ty_val = try mod.intern(.{ .opt = .{
  17185                 .ty = (try mod.optionalType(.type_type)).toIntern(),
  17186                 .val = if (union_ty.unionTagType(mod)) |tag_ty| tag_ty.toIntern() else .none,
  17187             } });
  17188 
  17189             const container_layout_ty = t: {
  17190                 const decl_index = (try sema.namespaceLookup(
  17191                     block,
  17192                     src,
  17193                     (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?,
  17194                     try ip.getOrPutString(gpa, "ContainerLayout"),
  17195                 )).?;
  17196                 try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
  17197                 try sema.ensureDeclAnalyzed(decl_index);
  17198                 const decl = mod.declPtr(decl_index);
  17199                 break :t decl.val.toType();
  17200             };
  17201 
  17202             const field_values = .{
  17203                 // layout: ContainerLayout,
  17204                 (try mod.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(),
  17205 
  17206                 // tag_type: ?type,
  17207                 enum_tag_ty_val,
  17208                 // fields: []const UnionField,
  17209                 fields_val,
  17210                 // decls: []const Declaration,
  17211                 decls_val,
  17212             };
  17213             return sema.addConstant((try mod.intern(.{ .un = .{
  17214                 .ty = type_info_ty.toIntern(),
  17215                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Union))).toIntern(),
  17216                 .val = try mod.intern(.{ .aggregate = .{
  17217                     .ty = type_union_ty.toIntern(),
  17218                     .storage = .{ .elems = &field_values },
  17219                 } }),
  17220             } })).toValue());
  17221         },
  17222         .Struct => {
  17223             // TODO: look into memoizing this result.
  17224 
  17225             var fields_anon_decl = try block.startAnonDecl();
  17226             defer fields_anon_decl.deinit();
  17227 
  17228             const type_struct_ty = t: {
  17229                 const type_struct_ty_decl_index = (try sema.namespaceLookup(
  17230                     block,
  17231                     src,
  17232                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17233                     try ip.getOrPutString(gpa, "Struct"),
  17234                 )).?;
  17235                 try mod.declareDeclDependency(sema.owner_decl_index, type_struct_ty_decl_index);
  17236                 try sema.ensureDeclAnalyzed(type_struct_ty_decl_index);
  17237                 const type_struct_ty_decl = mod.declPtr(type_struct_ty_decl_index);
  17238                 break :t type_struct_ty_decl.val.toType();
  17239             };
  17240 
  17241             const struct_field_ty = t: {
  17242                 const struct_field_ty_decl_index = (try sema.namespaceLookup(
  17243                     block,
  17244                     src,
  17245                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17246                     try ip.getOrPutString(gpa, "StructField"),
  17247                 )).?;
  17248                 try mod.declareDeclDependency(sema.owner_decl_index, struct_field_ty_decl_index);
  17249                 try sema.ensureDeclAnalyzed(struct_field_ty_decl_index);
  17250                 const struct_field_ty_decl = mod.declPtr(struct_field_ty_decl_index);
  17251                 break :t struct_field_ty_decl.val.toType();
  17252             };
  17253 
  17254             const struct_ty = try sema.resolveTypeFields(ty);
  17255             try sema.resolveTypeLayout(ty); // Getting alignment requires type layout
  17256             const layout = struct_ty.containerLayout(mod);
  17257 
  17258             var struct_field_vals: []InternPool.Index = &.{};
  17259             defer gpa.free(struct_field_vals);
  17260             fv: {
  17261                 const struct_type = switch (ip.indexToKey(struct_ty.toIntern())) {
  17262                     .anon_struct_type => |tuple| {
  17263                         struct_field_vals = try gpa.alloc(InternPool.Index, tuple.types.len);
  17264                         for (struct_field_vals, 0..) |*struct_field_val, i| {
  17265                             const anon_struct_type = ip.indexToKey(struct_ty.toIntern()).anon_struct_type;
  17266                             const field_ty = anon_struct_type.types[i];
  17267                             const field_val = anon_struct_type.values[i];
  17268                             const name_val = v: {
  17269                                 var anon_decl = try block.startAnonDecl();
  17270                                 defer anon_decl.deinit();
  17271                                 // TODO: write something like getCoercedInts to avoid needing to dupe
  17272                                 const bytes = if (tuple.names.len != 0)
  17273                                     // https://github.com/ziglang/zig/issues/15709
  17274                                     try sema.arena.dupe(u8, ip.stringToSlice(ip.indexToKey(struct_ty.toIntern()).anon_struct_type.names[i]))
  17275                                 else
  17276                                     try std.fmt.allocPrint(sema.arena, "{d}", .{i});
  17277                                 const new_decl_ty = try mod.arrayType(.{
  17278                                     .len = bytes.len,
  17279                                     .child = .u8_type,
  17280                                 });
  17281                                 const new_decl = try anon_decl.finish(
  17282                                     new_decl_ty,
  17283                                     (try mod.intern(.{ .aggregate = .{
  17284                                         .ty = new_decl_ty.toIntern(),
  17285                                         .storage = .{ .bytes = bytes },
  17286                                     } })).toValue(),
  17287                                     .none, // default alignment
  17288                                 );
  17289                                 break :v try mod.intern(.{ .ptr = .{
  17290                                     .ty = .slice_const_u8_type,
  17291                                     .addr = .{ .decl = new_decl },
  17292                                     .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(),
  17293                                 } });
  17294                             };
  17295 
  17296                             try sema.resolveTypeLayout(field_ty.toType());
  17297 
  17298                             const is_comptime = field_val != .none;
  17299                             const opt_default_val = if (is_comptime) field_val.toValue() else null;
  17300                             const default_val_ptr = try sema.optRefValue(block, field_ty.toType(), opt_default_val);
  17301                             const struct_field_fields = .{
  17302                                 // name: []const u8,
  17303                                 name_val,
  17304                                 // type: type,
  17305                                 field_ty,
  17306                                 // default_value: ?*const anyopaque,
  17307                                 default_val_ptr.toIntern(),
  17308                                 // is_comptime: bool,
  17309                                 Value.makeBool(is_comptime).toIntern(),
  17310                                 // alignment: comptime_int,
  17311                                 (try mod.intValue(Type.comptime_int, field_ty.toType().abiAlignment(mod))).toIntern(),
  17312                             };
  17313                             struct_field_val.* = try mod.intern(.{ .aggregate = .{
  17314                                 .ty = struct_field_ty.toIntern(),
  17315                                 .storage = .{ .elems = &struct_field_fields },
  17316                             } });
  17317                         }
  17318                         break :fv;
  17319                     },
  17320                     .struct_type => |s| s,
  17321                     else => unreachable,
  17322                 };
  17323                 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse break :fv;
  17324                 struct_field_vals = try gpa.alloc(InternPool.Index, struct_obj.fields.count());
  17325 
  17326                 for (
  17327                     struct_field_vals,
  17328                     struct_obj.fields.keys(),
  17329                     struct_obj.fields.values(),
  17330                 ) |*field_val, name_nts, field| {
  17331                     // TODO: write something like getCoercedInts to avoid needing to dupe
  17332                     const name = try sema.arena.dupe(u8, ip.stringToSlice(name_nts));
  17333                     const name_val = v: {
  17334                         var anon_decl = try block.startAnonDecl();
  17335                         defer anon_decl.deinit();
  17336                         const new_decl_ty = try mod.arrayType(.{
  17337                             .len = name.len,
  17338                             .child = .u8_type,
  17339                         });
  17340                         const new_decl = try anon_decl.finish(
  17341                             new_decl_ty,
  17342                             (try mod.intern(.{ .aggregate = .{
  17343                                 .ty = new_decl_ty.toIntern(),
  17344                                 .storage = .{ .bytes = name },
  17345                             } })).toValue(),
  17346                             .none, // default alignment
  17347                         );
  17348                         break :v try mod.intern(.{ .ptr = .{
  17349                             .ty = .slice_const_u8_type,
  17350                             .addr = .{ .decl = new_decl },
  17351                             .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  17352                         } });
  17353                     };
  17354 
  17355                     const opt_default_val = if (field.default_val == .none)
  17356                         null
  17357                     else
  17358                         field.default_val.toValue();
  17359                     const default_val_ptr = try sema.optRefValue(block, field.ty, opt_default_val);
  17360                     const alignment = field.alignment(mod, layout);
  17361 
  17362                     const struct_field_fields = .{
  17363                         // name: []const u8,
  17364                         name_val,
  17365                         // type: type,
  17366                         field.ty.toIntern(),
  17367                         // default_value: ?*const anyopaque,
  17368                         default_val_ptr.toIntern(),
  17369                         // is_comptime: bool,
  17370                         Value.makeBool(field.is_comptime).toIntern(),
  17371                         // alignment: comptime_int,
  17372                         (try mod.intValue(Type.comptime_int, alignment)).toIntern(),
  17373                     };
  17374                     field_val.* = try mod.intern(.{ .aggregate = .{
  17375                         .ty = struct_field_ty.toIntern(),
  17376                         .storage = .{ .elems = &struct_field_fields },
  17377                     } });
  17378                 }
  17379             }
  17380 
  17381             const fields_val = v: {
  17382                 const array_fields_ty = try mod.arrayType(.{
  17383                     .len = struct_field_vals.len,
  17384                     .child = struct_field_ty.toIntern(),
  17385                 });
  17386                 const new_decl = try fields_anon_decl.finish(
  17387                     array_fields_ty,
  17388                     (try mod.intern(.{ .aggregate = .{
  17389                         .ty = array_fields_ty.toIntern(),
  17390                         .storage = .{ .elems = struct_field_vals },
  17391                     } })).toValue(),
  17392                     .none, // default alignment
  17393                 );
  17394                 break :v try mod.intern(.{ .ptr = .{
  17395                     .ty = (try mod.ptrType(.{
  17396                         .child = struct_field_ty.toIntern(),
  17397                         .flags = .{
  17398                             .size = .Slice,
  17399                             .is_const = true,
  17400                         },
  17401                     })).toIntern(),
  17402                     .addr = .{ .decl = new_decl },
  17403                     .len = (try mod.intValue(Type.usize, struct_field_vals.len)).toIntern(),
  17404                 } });
  17405             };
  17406 
  17407             const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespaceIndex(mod));
  17408 
  17409             const backing_integer_val = try mod.intern(.{ .opt = .{
  17410                 .ty = (try mod.optionalType(.type_type)).toIntern(),
  17411                 .val = if (layout == .Packed) val: {
  17412                     const struct_obj = mod.typeToStruct(struct_ty).?;
  17413                     assert(struct_obj.haveLayout());
  17414                     assert(struct_obj.backing_int_ty.isInt(mod));
  17415                     break :val struct_obj.backing_int_ty.toIntern();
  17416                 } else .none,
  17417             } });
  17418 
  17419             const container_layout_ty = t: {
  17420                 const decl_index = (try sema.namespaceLookup(
  17421                     block,
  17422                     src,
  17423                     (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?,
  17424                     try ip.getOrPutString(gpa, "ContainerLayout"),
  17425                 )).?;
  17426                 try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
  17427                 try sema.ensureDeclAnalyzed(decl_index);
  17428                 const decl = mod.declPtr(decl_index);
  17429                 break :t decl.val.toType();
  17430             };
  17431 
  17432             const field_values = [_]InternPool.Index{
  17433                 // layout: ContainerLayout,
  17434                 (try mod.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(),
  17435                 // backing_integer: ?type,
  17436                 backing_integer_val,
  17437                 // fields: []const StructField,
  17438                 fields_val,
  17439                 // decls: []const Declaration,
  17440                 decls_val,
  17441                 // is_tuple: bool,
  17442                 Value.makeBool(struct_ty.isTuple(mod)).toIntern(),
  17443             };
  17444             return sema.addConstant((try mod.intern(.{ .un = .{
  17445                 .ty = type_info_ty.toIntern(),
  17446                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Struct))).toIntern(),
  17447                 .val = try mod.intern(.{ .aggregate = .{
  17448                     .ty = type_struct_ty.toIntern(),
  17449                     .storage = .{ .elems = &field_values },
  17450                 } }),
  17451             } })).toValue());
  17452         },
  17453         .Opaque => {
  17454             // TODO: look into memoizing this result.
  17455 
  17456             const type_opaque_ty = t: {
  17457                 const type_opaque_ty_decl_index = (try sema.namespaceLookup(
  17458                     block,
  17459                     src,
  17460                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17461                     try ip.getOrPutString(gpa, "Opaque"),
  17462                 )).?;
  17463                 try mod.declareDeclDependency(sema.owner_decl_index, type_opaque_ty_decl_index);
  17464                 try sema.ensureDeclAnalyzed(type_opaque_ty_decl_index);
  17465                 const type_opaque_ty_decl = mod.declPtr(type_opaque_ty_decl_index);
  17466                 break :t type_opaque_ty_decl.val.toType();
  17467             };
  17468 
  17469             const opaque_ty = try sema.resolveTypeFields(ty);
  17470             const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespaceIndex(mod));
  17471 
  17472             const field_values = .{
  17473                 // decls: []const Declaration,
  17474                 decls_val,
  17475             };
  17476             return sema.addConstant((try mod.intern(.{ .un = .{
  17477                 .ty = type_info_ty.toIntern(),
  17478                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Opaque))).toIntern(),
  17479                 .val = try mod.intern(.{ .aggregate = .{
  17480                     .ty = type_opaque_ty.toIntern(),
  17481                     .storage = .{ .elems = &field_values },
  17482                 } }),
  17483             } })).toValue());
  17484         },
  17485         .Frame => return sema.failWithUseOfAsync(block, src),
  17486         .AnyFrame => return sema.failWithUseOfAsync(block, src),
  17487     }
  17488 }
  17489 
  17490 fn typeInfoDecls(
  17491     sema: *Sema,
  17492     block: *Block,
  17493     src: LazySrcLoc,
  17494     type_info_ty: Type,
  17495     opt_namespace: Module.Namespace.OptionalIndex,
  17496 ) CompileError!InternPool.Index {
  17497     const mod = sema.mod;
  17498     const gpa = sema.gpa;
  17499 
  17500     var decls_anon_decl = try block.startAnonDecl();
  17501     defer decls_anon_decl.deinit();
  17502 
  17503     const declaration_ty = t: {
  17504         const declaration_ty_decl_index = (try sema.namespaceLookup(
  17505             block,
  17506             src,
  17507             type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17508             try mod.intern_pool.getOrPutString(gpa, "Declaration"),
  17509         )).?;
  17510         try mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index);
  17511         try sema.ensureDeclAnalyzed(declaration_ty_decl_index);
  17512         const declaration_ty_decl = mod.declPtr(declaration_ty_decl_index);
  17513         break :t declaration_ty_decl.val.toType();
  17514     };
  17515     try sema.queueFullTypeResolution(declaration_ty);
  17516 
  17517     var decl_vals = std.ArrayList(InternPool.Index).init(gpa);
  17518     defer decl_vals.deinit();
  17519 
  17520     var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa);
  17521     defer seen_namespaces.deinit();
  17522 
  17523     if (opt_namespace.unwrap()) |namespace_index| {
  17524         const namespace = mod.namespacePtr(namespace_index);
  17525         try sema.typeInfoNamespaceDecls(block, namespace, declaration_ty, &decl_vals, &seen_namespaces);
  17526     }
  17527 
  17528     const array_decl_ty = try mod.arrayType(.{
  17529         .len = decl_vals.items.len,
  17530         .child = declaration_ty.toIntern(),
  17531     });
  17532     const new_decl = try decls_anon_decl.finish(
  17533         array_decl_ty,
  17534         (try mod.intern(.{ .aggregate = .{
  17535             .ty = array_decl_ty.toIntern(),
  17536             .storage = .{ .elems = decl_vals.items },
  17537         } })).toValue(),
  17538         .none, // default alignment
  17539     );
  17540     return try mod.intern(.{ .ptr = .{
  17541         .ty = (try mod.ptrType(.{
  17542             .child = declaration_ty.toIntern(),
  17543             .flags = .{
  17544                 .size = .Slice,
  17545                 .is_const = true,
  17546             },
  17547         })).toIntern(),
  17548         .addr = .{ .decl = new_decl },
  17549         .len = (try mod.intValue(Type.usize, decl_vals.items.len)).toIntern(),
  17550     } });
  17551 }
  17552 
  17553 fn typeInfoNamespaceDecls(
  17554     sema: *Sema,
  17555     block: *Block,
  17556     namespace: *Namespace,
  17557     declaration_ty: Type,
  17558     decl_vals: *std.ArrayList(InternPool.Index),
  17559     seen_namespaces: *std.AutoHashMap(*Namespace, void),
  17560 ) !void {
  17561     const mod = sema.mod;
  17562     const ip = &mod.intern_pool;
  17563     const gop = try seen_namespaces.getOrPut(namespace);
  17564     if (gop.found_existing) return;
  17565     const decls = namespace.decls.keys();
  17566     for (decls) |decl_index| {
  17567         const decl = mod.declPtr(decl_index);
  17568         if (decl.kind == .@"usingnamespace") {
  17569             if (decl.analysis == .in_progress) continue;
  17570             try mod.ensureDeclAnalyzed(decl_index);
  17571             const new_ns = decl.val.toType().getNamespace(mod).?;
  17572             try sema.typeInfoNamespaceDecls(block, new_ns, declaration_ty, decl_vals, seen_namespaces);
  17573             continue;
  17574         }
  17575         if (decl.kind != .named) continue;
  17576         const name_val = v: {
  17577             var anon_decl = try block.startAnonDecl();
  17578             defer anon_decl.deinit();
  17579             // TODO: write something like getCoercedInts to avoid needing to dupe
  17580             const name = try sema.arena.dupe(u8, ip.stringToSlice(decl.name));
  17581             const new_decl_ty = try mod.arrayType(.{
  17582                 .len = name.len,
  17583                 .child = .u8_type,
  17584             });
  17585             const new_decl = try anon_decl.finish(
  17586                 new_decl_ty,
  17587                 (try mod.intern(.{ .aggregate = .{
  17588                     .ty = new_decl_ty.toIntern(),
  17589                     .storage = .{ .bytes = name },
  17590                 } })).toValue(),
  17591                 .none, // default alignment
  17592             );
  17593             break :v try mod.intern(.{ .ptr = .{
  17594                 .ty = .slice_const_u8_type,
  17595                 .addr = .{ .decl = new_decl },
  17596                 .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  17597             } });
  17598         };
  17599 
  17600         const fields = .{
  17601             //name: []const u8,
  17602             name_val,
  17603             //is_pub: bool,
  17604             Value.makeBool(decl.is_pub).toIntern(),
  17605         };
  17606         try decl_vals.append(try mod.intern(.{ .aggregate = .{
  17607             .ty = declaration_ty.toIntern(),
  17608             .storage = .{ .elems = &fields },
  17609         } }));
  17610     }
  17611 }
  17612 
  17613 fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17614     _ = block;
  17615     const zir_datas = sema.code.instructions.items(.data);
  17616     const inst_data = zir_datas[inst].un_node;
  17617     const operand = try sema.resolveInst(inst_data.operand);
  17618     const operand_ty = sema.typeOf(operand);
  17619     return sema.addType(operand_ty);
  17620 }
  17621 
  17622 fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17623     const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
  17624     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
  17625     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
  17626 
  17627     var child_block: Block = .{
  17628         .parent = block,
  17629         .sema = sema,
  17630         .src_decl = block.src_decl,
  17631         .namespace = block.namespace,
  17632         .wip_capture_scope = block.wip_capture_scope,
  17633         .instructions = .{},
  17634         .inlining = block.inlining,
  17635         .is_comptime = false,
  17636         .is_typeof = true,
  17637         .want_safety = false,
  17638         .error_return_trace_index = block.error_return_trace_index,
  17639     };
  17640     defer child_block.instructions.deinit(sema.gpa);
  17641 
  17642     const operand = try sema.resolveBody(&child_block, body, inst);
  17643     const operand_ty = sema.typeOf(operand);
  17644     if (operand_ty.isGenericPoison()) return error.GenericPoison;
  17645     return sema.addType(operand_ty);
  17646 }
  17647 
  17648 fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17649     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17650     const src = inst_data.src();
  17651     const operand = try sema.resolveInst(inst_data.operand);
  17652     const operand_ty = sema.typeOf(operand);
  17653     const res_ty = try sema.log2IntType(block, operand_ty, src);
  17654     return sema.addType(res_ty);
  17655 }
  17656 
  17657 fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type {
  17658     const mod = sema.mod;
  17659     switch (operand.zigTypeTag(mod)) {
  17660         .ComptimeInt => return Type.comptime_int,
  17661         .Int => {
  17662             const bits = operand.bitSize(mod);
  17663             const count = if (bits == 0)
  17664                 0
  17665             else blk: {
  17666                 var count: u16 = 0;
  17667                 var s = bits - 1;
  17668                 while (s != 0) : (s >>= 1) {
  17669                     count += 1;
  17670                 }
  17671                 break :blk count;
  17672             };
  17673             return mod.intType(.unsigned, count);
  17674         },
  17675         .Vector => {
  17676             const elem_ty = operand.elemType2(mod);
  17677             const log2_elem_ty = try sema.log2IntType(block, elem_ty, src);
  17678             return mod.vectorType(.{
  17679                 .len = operand.vectorLen(mod),
  17680                 .child = log2_elem_ty.toIntern(),
  17681             });
  17682         },
  17683         else => {},
  17684     }
  17685     return sema.fail(
  17686         block,
  17687         src,
  17688         "bit shifting operation expected integer type, found '{}'",
  17689         .{operand.fmt(mod)},
  17690     );
  17691 }
  17692 
  17693 fn zirTypeofPeer(
  17694     sema: *Sema,
  17695     block: *Block,
  17696     extended: Zir.Inst.Extended.InstData,
  17697 ) CompileError!Air.Inst.Ref {
  17698     const tracy = trace(@src());
  17699     defer tracy.end();
  17700 
  17701     const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand);
  17702     const src = LazySrcLoc.nodeOffset(extra.data.src_node);
  17703     const body = sema.code.extra[extra.data.body_index..][0..extra.data.body_len];
  17704 
  17705     var child_block: Block = .{
  17706         .parent = block,
  17707         .sema = sema,
  17708         .src_decl = block.src_decl,
  17709         .namespace = block.namespace,
  17710         .wip_capture_scope = block.wip_capture_scope,
  17711         .instructions = .{},
  17712         .inlining = block.inlining,
  17713         .is_comptime = false,
  17714         .is_typeof = true,
  17715         .runtime_cond = block.runtime_cond,
  17716         .runtime_loop = block.runtime_loop,
  17717         .runtime_index = block.runtime_index,
  17718     };
  17719     defer child_block.instructions.deinit(sema.gpa);
  17720     // Ignore the result, we only care about the instructions in `args`.
  17721     _ = try sema.analyzeBodyBreak(&child_block, body);
  17722 
  17723     const args = sema.code.refSlice(extra.end, extended.small);
  17724 
  17725     const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len);
  17726     defer sema.gpa.free(inst_list);
  17727 
  17728     for (args, 0..) |arg_ref, i| {
  17729         inst_list[i] = try sema.resolveInst(arg_ref);
  17730     }
  17731 
  17732     const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node });
  17733     return sema.addType(result_type);
  17734 }
  17735 
  17736 fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17737     const tracy = trace(@src());
  17738     defer tracy.end();
  17739 
  17740     const mod = sema.mod;
  17741     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17742     const src = inst_data.src();
  17743     const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
  17744     const uncasted_operand = try sema.resolveInst(inst_data.operand);
  17745 
  17746     const operand = try sema.coerce(block, Type.bool, uncasted_operand, operand_src);
  17747     if (try sema.resolveMaybeUndefVal(operand)) |val| {
  17748         return if (val.isUndef(mod))
  17749             sema.addConstUndef(Type.bool)
  17750         else if (val.toBool())
  17751             Air.Inst.Ref.bool_false
  17752         else
  17753             Air.Inst.Ref.bool_true;
  17754     }
  17755     try sema.requireRuntimeBlock(block, src, null);
  17756     return block.addTyOp(.not, Type.bool, operand);
  17757 }
  17758 
  17759 fn zirBoolBr(
  17760     sema: *Sema,
  17761     parent_block: *Block,
  17762     inst: Zir.Inst.Index,
  17763     is_bool_or: bool,
  17764 ) CompileError!Air.Inst.Ref {
  17765     const tracy = trace(@src());
  17766     defer tracy.end();
  17767 
  17768     const mod = sema.mod;
  17769     const datas = sema.code.instructions.items(.data);
  17770     const inst_data = datas[inst].bool_br;
  17771     const lhs = try sema.resolveInst(inst_data.lhs);
  17772     const lhs_src = sema.src;
  17773     const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
  17774     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
  17775     const gpa = sema.gpa;
  17776 
  17777     if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| {
  17778         if (is_bool_or and lhs_val.toBool()) {
  17779             return Air.Inst.Ref.bool_true;
  17780         } else if (!is_bool_or and !lhs_val.toBool()) {
  17781             return Air.Inst.Ref.bool_false;
  17782         }
  17783         // comptime-known left-hand side. No need for a block here; the result
  17784         // is simply the rhs expression. Here we rely on there only being 1
  17785         // break instruction (`break_inline`).
  17786         return sema.resolveBody(parent_block, body, inst);
  17787     }
  17788 
  17789     const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
  17790     try sema.air_instructions.append(gpa, .{
  17791         .tag = .block,
  17792         .data = .{ .ty_pl = .{
  17793             .ty = .bool_type,
  17794             .payload = undefined,
  17795         } },
  17796     });
  17797 
  17798     var child_block = parent_block.makeSubBlock();
  17799     child_block.runtime_loop = null;
  17800     child_block.runtime_cond = lhs_src;
  17801     child_block.runtime_index.increment();
  17802     defer child_block.instructions.deinit(gpa);
  17803 
  17804     var then_block = child_block.makeSubBlock();
  17805     defer then_block.instructions.deinit(gpa);
  17806 
  17807     var else_block = child_block.makeSubBlock();
  17808     defer else_block.instructions.deinit(gpa);
  17809 
  17810     const lhs_block = if (is_bool_or) &then_block else &else_block;
  17811     const rhs_block = if (is_bool_or) &else_block else &then_block;
  17812 
  17813     const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false;
  17814     _ = try lhs_block.addBr(block_inst, lhs_result);
  17815 
  17816     const rhs_result = try sema.resolveBody(rhs_block, body, inst);
  17817     if (!sema.typeOf(rhs_result).isNoReturn(mod)) {
  17818         _ = try rhs_block.addBr(block_inst, rhs_result);
  17819     }
  17820 
  17821     const result = sema.finishCondBr(parent_block, &child_block, &then_block, &else_block, lhs, block_inst);
  17822     if (!sema.typeOf(rhs_result).isNoReturn(mod)) {
  17823         if (try sema.resolveDefinedValue(rhs_block, sema.src, rhs_result)) |rhs_val| {
  17824             if (is_bool_or and rhs_val.toBool()) {
  17825                 return Air.Inst.Ref.bool_true;
  17826             } else if (!is_bool_or and !rhs_val.toBool()) {
  17827                 return Air.Inst.Ref.bool_false;
  17828             }
  17829         }
  17830     }
  17831 
  17832     return result;
  17833 }
  17834 
  17835 fn finishCondBr(
  17836     sema: *Sema,
  17837     parent_block: *Block,
  17838     child_block: *Block,
  17839     then_block: *Block,
  17840     else_block: *Block,
  17841     cond: Air.Inst.Ref,
  17842     block_inst: Air.Inst.Index,
  17843 ) !Air.Inst.Ref {
  17844     const gpa = sema.gpa;
  17845 
  17846     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
  17847         then_block.instructions.items.len + else_block.instructions.items.len +
  17848         @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len + 1);
  17849 
  17850     const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
  17851         .then_body_len = @as(u32, @intCast(then_block.instructions.items.len)),
  17852         .else_body_len = @as(u32, @intCast(else_block.instructions.items.len)),
  17853     });
  17854     sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items);
  17855     sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items);
  17856 
  17857     _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
  17858         .operand = cond,
  17859         .payload = cond_br_payload,
  17860     } } });
  17861 
  17862     sema.air_instructions.items(.data)[block_inst].ty_pl.payload = sema.addExtraAssumeCapacity(
  17863         Air.Block{ .body_len = @as(u32, @intCast(child_block.instructions.items.len)) },
  17864     );
  17865     sema.air_extra.appendSliceAssumeCapacity(child_block.instructions.items);
  17866 
  17867     try parent_block.instructions.append(gpa, block_inst);
  17868     return Air.indexToRef(block_inst);
  17869 }
  17870 
  17871 fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  17872     const mod = sema.mod;
  17873     switch (ty.zigTypeTag(mod)) {
  17874         .Optional, .Null, .Undefined => return,
  17875         .Pointer => if (ty.isPtrLikeOptional(mod)) return,
  17876         else => {},
  17877     }
  17878     return sema.failWithExpectedOptionalType(block, src, ty);
  17879 }
  17880 
  17881 fn zirIsNonNull(
  17882     sema: *Sema,
  17883     block: *Block,
  17884     inst: Zir.Inst.Index,
  17885 ) CompileError!Air.Inst.Ref {
  17886     const tracy = trace(@src());
  17887     defer tracy.end();
  17888 
  17889     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17890     const src = inst_data.src();
  17891     const operand = try sema.resolveInst(inst_data.operand);
  17892     try sema.checkNullableType(block, src, sema.typeOf(operand));
  17893     return sema.analyzeIsNull(block, src, operand, true);
  17894 }
  17895 
  17896 fn zirIsNonNullPtr(
  17897     sema: *Sema,
  17898     block: *Block,
  17899     inst: Zir.Inst.Index,
  17900 ) CompileError!Air.Inst.Ref {
  17901     const tracy = trace(@src());
  17902     defer tracy.end();
  17903 
  17904     const mod = sema.mod;
  17905     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17906     const src = inst_data.src();
  17907     const ptr = try sema.resolveInst(inst_data.operand);
  17908     try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(mod));
  17909     if ((try sema.resolveMaybeUndefVal(ptr)) == null) {
  17910         return block.addUnOp(.is_non_null_ptr, ptr);
  17911     }
  17912     const loaded = try sema.analyzeLoad(block, src, ptr, src);
  17913     return sema.analyzeIsNull(block, src, loaded, true);
  17914 }
  17915 
  17916 fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  17917     const mod = sema.mod;
  17918     switch (ty.zigTypeTag(mod)) {
  17919         .ErrorSet, .ErrorUnion, .Undefined => return,
  17920         else => return sema.fail(block, src, "expected error union type, found '{}'", .{
  17921             ty.fmt(mod),
  17922         }),
  17923     }
  17924 }
  17925 
  17926 fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17927     const tracy = trace(@src());
  17928     defer tracy.end();
  17929 
  17930     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17931     const src = inst_data.src();
  17932     const operand = try sema.resolveInst(inst_data.operand);
  17933     try sema.checkErrorType(block, src, sema.typeOf(operand));
  17934     return sema.analyzeIsNonErr(block, src, operand);
  17935 }
  17936 
  17937 fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17938     const tracy = trace(@src());
  17939     defer tracy.end();
  17940 
  17941     const mod = sema.mod;
  17942     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17943     const src = inst_data.src();
  17944     const ptr = try sema.resolveInst(inst_data.operand);
  17945     try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(mod));
  17946     const loaded = try sema.analyzeLoad(block, src, ptr, src);
  17947     return sema.analyzeIsNonErr(block, src, loaded);
  17948 }
  17949 
  17950 fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17951     const tracy = trace(@src());
  17952     defer tracy.end();
  17953 
  17954     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17955     const src = inst_data.src();
  17956     const operand = try sema.resolveInst(inst_data.operand);
  17957     return sema.analyzeIsNonErr(block, src, operand);
  17958 }
  17959 
  17960 fn zirCondbr(
  17961     sema: *Sema,
  17962     parent_block: *Block,
  17963     inst: Zir.Inst.Index,
  17964 ) CompileError!Zir.Inst.Index {
  17965     const tracy = trace(@src());
  17966     defer tracy.end();
  17967 
  17968     const mod = sema.mod;
  17969     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  17970     const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
  17971     const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
  17972 
  17973     const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
  17974     const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
  17975 
  17976     const uncasted_cond = try sema.resolveInst(extra.data.condition);
  17977     const cond = try sema.coerce(parent_block, Type.bool, uncasted_cond, cond_src);
  17978 
  17979     if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| {
  17980         const body = if (cond_val.toBool()) then_body else else_body;
  17981 
  17982         try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src);
  17983         // We use `analyzeBodyInner` since we want to propagate any possible
  17984         // `error.ComptimeBreak` to the caller.
  17985         return sema.analyzeBodyInner(parent_block, body);
  17986     }
  17987 
  17988     const gpa = sema.gpa;
  17989 
  17990     // We'll re-use the sub block to save on memory bandwidth, and yank out the
  17991     // instructions array in between using it for the then block and else block.
  17992     var sub_block = parent_block.makeSubBlock();
  17993     sub_block.runtime_loop = null;
  17994     sub_block.runtime_cond = cond_src;
  17995     sub_block.runtime_index.increment();
  17996     defer sub_block.instructions.deinit(gpa);
  17997 
  17998     try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
  17999     const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
  18000     defer gpa.free(true_instructions);
  18001 
  18002     const err_cond = blk: {
  18003         const index = Zir.refToIndex(extra.data.condition) orelse break :blk null;
  18004         if (sema.code.instructions.items(.tag)[index] != .is_non_err) break :blk null;
  18005 
  18006         const err_inst_data = sema.code.instructions.items(.data)[index].un_node;
  18007         const err_operand = try sema.resolveInst(err_inst_data.operand);
  18008         const operand_ty = sema.typeOf(err_operand);
  18009         assert(operand_ty.zigTypeTag(mod) == .ErrorUnion);
  18010         const result_ty = operand_ty.errorUnionSet(mod);
  18011         break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand);
  18012     };
  18013 
  18014     if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?)) {
  18015         // nothing to do
  18016     } else {
  18017         try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
  18018     }
  18019     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
  18020         true_instructions.len + sub_block.instructions.items.len);
  18021     _ = try parent_block.addInst(.{
  18022         .tag = .cond_br,
  18023         .data = .{ .pl_op = .{
  18024             .operand = cond,
  18025             .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18026                 .then_body_len = @as(u32, @intCast(true_instructions.len)),
  18027                 .else_body_len = @as(u32, @intCast(sub_block.instructions.items.len)),
  18028             }),
  18029         } },
  18030     });
  18031     sema.air_extra.appendSliceAssumeCapacity(true_instructions);
  18032     sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
  18033     return always_noreturn;
  18034 }
  18035 
  18036 fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18037     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  18038     const src = inst_data.src();
  18039     const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  18040     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
  18041     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
  18042     const err_union = try sema.resolveInst(extra.data.operand);
  18043     const err_union_ty = sema.typeOf(err_union);
  18044     const mod = sema.mod;
  18045     if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
  18046         return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
  18047             err_union_ty.fmt(mod),
  18048         });
  18049     }
  18050     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
  18051     if (is_non_err != .none) {
  18052         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
  18053         if (is_non_err_val.toBool()) {
  18054             return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
  18055         }
  18056         // We can analyze the body directly in the parent block because we know there are
  18057         // no breaks from the body possible, and that the body is noreturn.
  18058         return sema.resolveBody(parent_block, body, inst);
  18059     }
  18060 
  18061     var sub_block = parent_block.makeSubBlock();
  18062     defer sub_block.instructions.deinit(sema.gpa);
  18063 
  18064     // This body is guaranteed to end with noreturn and has no breaks.
  18065     _ = try sema.analyzeBodyInner(&sub_block, body);
  18066 
  18067     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len +
  18068         sub_block.instructions.items.len);
  18069     const try_inst = try parent_block.addInst(.{
  18070         .tag = .@"try",
  18071         .data = .{ .pl_op = .{
  18072             .operand = err_union,
  18073             .payload = sema.addExtraAssumeCapacity(Air.Try{
  18074                 .body_len = @as(u32, @intCast(sub_block.instructions.items.len)),
  18075             }),
  18076         } },
  18077     });
  18078     sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
  18079     return try_inst;
  18080 }
  18081 
  18082 fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18083     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  18084     const src = inst_data.src();
  18085     const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  18086     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
  18087     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
  18088     const operand = try sema.resolveInst(extra.data.operand);
  18089     const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src);
  18090     const err_union_ty = sema.typeOf(err_union);
  18091     const mod = sema.mod;
  18092     if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
  18093         return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
  18094             err_union_ty.fmt(mod),
  18095         });
  18096     }
  18097     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
  18098     if (is_non_err != .none) {
  18099         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
  18100         if (is_non_err_val.toBool()) {
  18101             return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
  18102         }
  18103         // We can analyze the body directly in the parent block because we know there are
  18104         // no breaks from the body possible, and that the body is noreturn.
  18105         return sema.resolveBody(parent_block, body, inst);
  18106     }
  18107 
  18108     var sub_block = parent_block.makeSubBlock();
  18109     defer sub_block.instructions.deinit(sema.gpa);
  18110 
  18111     // This body is guaranteed to end with noreturn and has no breaks.
  18112     _ = try sema.analyzeBodyInner(&sub_block, body);
  18113 
  18114     const operand_ty = sema.typeOf(operand);
  18115     const ptr_info = operand_ty.ptrInfo(mod);
  18116     const res_ty = try mod.ptrType(.{
  18117         .child = err_union_ty.errorUnionPayload(mod).toIntern(),
  18118         .flags = .{
  18119             .is_const = ptr_info.flags.is_const,
  18120             .is_volatile = ptr_info.flags.is_volatile,
  18121             .is_allowzero = ptr_info.flags.is_allowzero,
  18122             .address_space = ptr_info.flags.address_space,
  18123         },
  18124     });
  18125     const res_ty_ref = try sema.addType(res_ty);
  18126     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len +
  18127         sub_block.instructions.items.len);
  18128     const try_inst = try parent_block.addInst(.{
  18129         .tag = .try_ptr,
  18130         .data = .{ .ty_pl = .{
  18131             .ty = res_ty_ref,
  18132             .payload = sema.addExtraAssumeCapacity(Air.TryPtr{
  18133                 .ptr = operand,
  18134                 .body_len = @as(u32, @intCast(sub_block.instructions.items.len)),
  18135             }),
  18136         } },
  18137     });
  18138     sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
  18139     return try_inst;
  18140 }
  18141 
  18142 // A `break` statement is inside a runtime condition, but trying to
  18143 // break from an inline loop. In such case we must convert it to
  18144 // a runtime break.
  18145 fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !void {
  18146     const gop = sema.inst_map.getOrPutAssumeCapacity(break_data.block_inst);
  18147     const labeled_block = if (!gop.found_existing) blk: {
  18148         try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1);
  18149 
  18150         const new_block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
  18151         gop.value_ptr.* = Air.indexToRef(new_block_inst);
  18152         try sema.air_instructions.append(sema.gpa, .{
  18153             .tag = .block,
  18154             .data = undefined,
  18155         });
  18156         const labeled_block = try sema.gpa.create(LabeledBlock);
  18157         labeled_block.* = .{
  18158             .label = .{
  18159                 .zir_block = break_data.block_inst,
  18160                 .merges = .{
  18161                     .src_locs = .{},
  18162                     .results = .{},
  18163                     .br_list = .{},
  18164                     .block_inst = new_block_inst,
  18165                 },
  18166             },
  18167             .block = .{
  18168                 .parent = child_block,
  18169                 .sema = sema,
  18170                 .src_decl = child_block.src_decl,
  18171                 .namespace = child_block.namespace,
  18172                 .wip_capture_scope = child_block.wip_capture_scope,
  18173                 .instructions = .{},
  18174                 .label = &labeled_block.label,
  18175                 .inlining = child_block.inlining,
  18176                 .is_comptime = child_block.is_comptime,
  18177             },
  18178         };
  18179         sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block);
  18180         break :blk labeled_block;
  18181     } else blk: {
  18182         const new_block_inst = Air.refToIndex(gop.value_ptr.*).?;
  18183         const labeled_block = sema.post_hoc_blocks.get(new_block_inst).?;
  18184         break :blk labeled_block;
  18185     };
  18186 
  18187     const operand = try sema.resolveInst(break_data.operand);
  18188     const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand);
  18189     try labeled_block.label.merges.results.append(sema.gpa, operand);
  18190     try labeled_block.label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?);
  18191     labeled_block.block.runtime_index.increment();
  18192     if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) {
  18193         labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop;
  18194         labeled_block.block.runtime_loop = child_block.runtime_loop;
  18195     }
  18196 }
  18197 
  18198 fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
  18199     const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
  18200     const src = inst_data.src();
  18201 
  18202     if (block.is_comptime) {
  18203         return sema.fail(block, src, "reached unreachable code", .{});
  18204     }
  18205     // TODO Add compile error for @optimizeFor occurring too late in a scope.
  18206     try block.addUnreachable(true);
  18207     return always_noreturn;
  18208 }
  18209 
  18210 fn zirRetErrValue(
  18211     sema: *Sema,
  18212     block: *Block,
  18213     inst: Zir.Inst.Index,
  18214 ) CompileError!Zir.Inst.Index {
  18215     const mod = sema.mod;
  18216     const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
  18217     const err_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
  18218     _ = try mod.getErrorValue(err_name);
  18219     const src = inst_data.src();
  18220     // Return the error code from the function.
  18221     const error_set_type = try mod.singleErrorSetType(err_name);
  18222     const result_inst = try sema.addConstant((try mod.intern(.{ .err = .{
  18223         .ty = error_set_type.toIntern(),
  18224         .name = err_name,
  18225     } })).toValue());
  18226     return sema.analyzeRet(block, result_inst, src);
  18227 }
  18228 
  18229 fn zirRetImplicit(
  18230     sema: *Sema,
  18231     block: *Block,
  18232     inst: Zir.Inst.Index,
  18233 ) CompileError!Zir.Inst.Index {
  18234     const tracy = trace(@src());
  18235     defer tracy.end();
  18236 
  18237     const mod = sema.mod;
  18238     const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
  18239     const operand = try sema.resolveInst(inst_data.operand);
  18240 
  18241     const r_brace_src = inst_data.src();
  18242     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
  18243     const base_tag = sema.fn_ret_ty.baseZigTypeTag(mod);
  18244     if (base_tag == .NoReturn) {
  18245         const msg = msg: {
  18246             const msg = try sema.errMsg(block, ret_ty_src, "function declared '{}' implicitly returns", .{
  18247                 sema.fn_ret_ty.fmt(mod),
  18248             });
  18249             errdefer msg.destroy(sema.gpa);
  18250             try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{});
  18251             break :msg msg;
  18252         };
  18253         return sema.failWithOwnedErrorMsg(msg);
  18254     } else if (base_tag != .Void) {
  18255         const msg = msg: {
  18256             const msg = try sema.errMsg(block, ret_ty_src, "function with non-void return type '{}' implicitly returns", .{
  18257                 sema.fn_ret_ty.fmt(mod),
  18258             });
  18259             errdefer msg.destroy(sema.gpa);
  18260             try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{});
  18261             break :msg msg;
  18262         };
  18263         return sema.failWithOwnedErrorMsg(msg);
  18264     }
  18265 
  18266     return sema.analyzeRet(block, operand, .unneeded);
  18267 }
  18268 
  18269 fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
  18270     const tracy = trace(@src());
  18271     defer tracy.end();
  18272 
  18273     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  18274     const operand = try sema.resolveInst(inst_data.operand);
  18275     const src = inst_data.src();
  18276 
  18277     return sema.analyzeRet(block, operand, src);
  18278 }
  18279 
  18280 fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
  18281     const tracy = trace(@src());
  18282     defer tracy.end();
  18283 
  18284     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  18285     const src = inst_data.src();
  18286     const ret_ptr = try sema.resolveInst(inst_data.operand);
  18287 
  18288     if (block.is_comptime or block.inlining != null) {
  18289         const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
  18290         return sema.analyzeRet(block, operand, src);
  18291     }
  18292 
  18293     if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
  18294         const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr);
  18295         return sema.retWithErrTracing(block, is_non_err, .ret_load, ret_ptr);
  18296     }
  18297 
  18298     _ = try block.addUnOp(.ret_load, ret_ptr);
  18299     return always_noreturn;
  18300 }
  18301 
  18302 fn retWithErrTracing(
  18303     sema: *Sema,
  18304     block: *Block,
  18305     is_non_err: Air.Inst.Ref,
  18306     ret_tag: Air.Inst.Tag,
  18307     operand: Air.Inst.Ref,
  18308 ) CompileError!Zir.Inst.Index {
  18309     const mod = sema.mod;
  18310     const need_check = switch (is_non_err) {
  18311         .bool_true => {
  18312             _ = try block.addUnOp(ret_tag, operand);
  18313             return always_noreturn;
  18314         },
  18315         .bool_false => false,
  18316         else => true,
  18317     };
  18318     const gpa = sema.gpa;
  18319     const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
  18320     const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
  18321     const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty);
  18322     const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
  18323     const return_err_fn = try sema.getBuiltin("returnError");
  18324     const args: [1]Air.Inst.Ref = .{err_return_trace};
  18325 
  18326     if (!need_check) {
  18327         try sema.callBuiltin(block, return_err_fn, .never_inline, &args);
  18328         _ = try block.addUnOp(ret_tag, operand);
  18329         return always_noreturn;
  18330     }
  18331 
  18332     var then_block = block.makeSubBlock();
  18333     defer then_block.instructions.deinit(gpa);
  18334     _ = try then_block.addUnOp(ret_tag, operand);
  18335 
  18336     var else_block = block.makeSubBlock();
  18337     defer else_block.instructions.deinit(gpa);
  18338     try sema.callBuiltin(&else_block, return_err_fn, .never_inline, &args);
  18339     _ = try else_block.addUnOp(ret_tag, operand);
  18340 
  18341     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
  18342         then_block.instructions.items.len + else_block.instructions.items.len +
  18343         @typeInfo(Air.Block).Struct.fields.len + 1);
  18344 
  18345     const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18346         .then_body_len = @as(u32, @intCast(then_block.instructions.items.len)),
  18347         .else_body_len = @as(u32, @intCast(else_block.instructions.items.len)),
  18348     });
  18349     sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items);
  18350     sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items);
  18351 
  18352     _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
  18353         .operand = is_non_err,
  18354         .payload = cond_br_payload,
  18355     } } });
  18356 
  18357     return always_noreturn;
  18358 }
  18359 
  18360 fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool {
  18361     const mod = sema.mod;
  18362     if (!mod.backendSupportsFeature(.error_return_trace)) return false;
  18363 
  18364     return fn_ret_ty.isError(mod) and
  18365         mod.comp.bin_file.options.error_return_tracing;
  18366 }
  18367 
  18368 fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18369     const mod = sema.mod;
  18370     const inst_data = sema.code.instructions.items(.data)[inst].save_err_ret_index;
  18371 
  18372     if (!mod.backendSupportsFeature(.error_return_trace)) return;
  18373     if (!mod.comp.bin_file.options.error_return_tracing) return;
  18374 
  18375     // This is only relevant at runtime.
  18376     if (block.is_comptime or block.is_typeof) return;
  18377 
  18378     const save_index = inst_data.operand == .none or b: {
  18379         const operand = try sema.resolveInst(inst_data.operand);
  18380         const operand_ty = sema.typeOf(operand);
  18381         break :b operand_ty.isError(mod);
  18382     };
  18383 
  18384     if (save_index)
  18385         block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block);
  18386 }
  18387 
  18388 fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18389     const inst_data = sema.code.instructions.items(.data)[inst].restore_err_ret_index;
  18390     const src = sema.src; // TODO
  18391 
  18392     // This is only relevant at runtime.
  18393     if (start_block.is_comptime or start_block.is_typeof) return;
  18394 
  18395     if (!sema.mod.backendSupportsFeature(.error_return_trace)) return;
  18396     if (!sema.owner_func.?.calls_or_awaits_errorable_fn) return;
  18397     if (!sema.mod.comp.bin_file.options.error_return_tracing) return;
  18398 
  18399     const tracy = trace(@src());
  18400     defer tracy.end();
  18401 
  18402     const saved_index = if (Zir.refToIndexAllowNone(inst_data.block)) |zir_block| b: {
  18403         var block = start_block;
  18404         while (true) {
  18405             if (block.label) |label| {
  18406                 if (label.zir_block == zir_block) {
  18407                     const target_trace_index = if (block.parent) |parent_block| tgt: {
  18408                         break :tgt parent_block.error_return_trace_index;
  18409                     } else sema.error_return_trace_index_on_fn_entry;
  18410 
  18411                     if (start_block.error_return_trace_index != target_trace_index)
  18412                         break :b target_trace_index;
  18413 
  18414                     return; // No need to restore
  18415                 }
  18416             }
  18417             block = block.parent.?;
  18418         }
  18419     } else b: {
  18420         if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry)
  18421             break :b sema.error_return_trace_index_on_fn_entry;
  18422 
  18423         return; // No need to restore
  18424     };
  18425 
  18426     assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere
  18427 
  18428     const operand = try sema.resolveInstAllowNone(inst_data.operand);
  18429     return sema.popErrorReturnTrace(start_block, src, operand, saved_index);
  18430 }
  18431 
  18432 fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
  18433     const mod = sema.mod;
  18434     const gpa = sema.gpa;
  18435     const ip = &mod.intern_pool;
  18436     assert(sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion);
  18437 
  18438     if (mod.typeToInferredErrorSet(sema.fn_ret_ty.errorUnionSet(mod))) |ies| {
  18439         const op_ty = sema.typeOf(uncasted_operand);
  18440         switch (op_ty.zigTypeTag(mod)) {
  18441             .ErrorSet => try ies.addErrorSet(op_ty, ip, gpa),
  18442             .ErrorUnion => try ies.addErrorSet(op_ty.errorUnionSet(mod), ip, gpa),
  18443             else => {},
  18444         }
  18445     }
  18446 }
  18447 
  18448 fn analyzeRet(
  18449     sema: *Sema,
  18450     block: *Block,
  18451     uncasted_operand: Air.Inst.Ref,
  18452     src: LazySrcLoc,
  18453 ) CompileError!Zir.Inst.Index {
  18454     // Special case for returning an error to an inferred error set; we need to
  18455     // add the error tag to the inferred error set of the in-scope function, so
  18456     // that the coercion below works correctly.
  18457     const mod = sema.mod;
  18458     if (sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion) {
  18459         try sema.addToInferredErrorSet(uncasted_operand);
  18460     }
  18461     const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, .{ .is_ret = true }) catch |err| switch (err) {
  18462         error.NotCoercible => unreachable,
  18463         else => |e| return e,
  18464     };
  18465 
  18466     if (block.inlining) |inlining| {
  18467         if (block.is_comptime) {
  18468             _ = try sema.resolveConstMaybeUndefVal(block, src, operand, "value being returned at comptime must be comptime-known");
  18469             inlining.comptime_result = operand;
  18470             return error.ComptimeReturn;
  18471         }
  18472         // We are inlining a function call; rewrite the `ret` as a `break`.
  18473         try inlining.merges.results.append(sema.gpa, operand);
  18474         _ = try block.addBr(inlining.merges.block_inst, operand);
  18475         return always_noreturn;
  18476     } else if (block.is_comptime) {
  18477         return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{});
  18478     }
  18479 
  18480     try sema.resolveTypeLayout(sema.fn_ret_ty);
  18481 
  18482     if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
  18483         // Avoid adding a frame to the error return trace in case the value is comptime-known
  18484         // to be not an error.
  18485         const is_non_err = try sema.analyzeIsNonErr(block, src, operand);
  18486         return sema.retWithErrTracing(block, is_non_err, .ret, operand);
  18487     }
  18488 
  18489     _ = try block.addUnOp(.ret, operand);
  18490 
  18491     return always_noreturn;
  18492 }
  18493 
  18494 fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
  18495     // extend this swich as additional operators are implemented
  18496     return switch (tag) {
  18497         .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true,
  18498         else => false,
  18499     };
  18500 }
  18501 
  18502 fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18503     const tracy = trace(@src());
  18504     defer tracy.end();
  18505 
  18506     const mod = sema.mod;
  18507     const inst_data = sema.code.instructions.items(.data)[inst].ptr_type;
  18508     const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index);
  18509     const elem_ty_src: LazySrcLoc = .{ .node_offset_ptr_elem = extra.data.src_node };
  18510     const sentinel_src: LazySrcLoc = .{ .node_offset_ptr_sentinel = extra.data.src_node };
  18511     const align_src: LazySrcLoc = .{ .node_offset_ptr_align = extra.data.src_node };
  18512     const addrspace_src: LazySrcLoc = .{ .node_offset_ptr_addrspace = extra.data.src_node };
  18513     const bitoffset_src: LazySrcLoc = .{ .node_offset_ptr_bitoffset = extra.data.src_node };
  18514     const hostsize_src: LazySrcLoc = .{ .node_offset_ptr_hostsize = extra.data.src_node };
  18515 
  18516     const elem_ty = blk: {
  18517         const air_inst = try sema.resolveInst(extra.data.elem_type);
  18518         const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| {
  18519             if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(mod)) {
  18520                 try sema.errNote(block, elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{});
  18521             }
  18522             return err;
  18523         };
  18524         if (ty.isGenericPoison()) return error.GenericPoison;
  18525         break :blk ty;
  18526     };
  18527 
  18528     if (elem_ty.zigTypeTag(mod) == .NoReturn)
  18529         return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{});
  18530 
  18531     const target = mod.getTarget();
  18532 
  18533     var extra_i = extra.end;
  18534 
  18535     const sentinel = if (inst_data.flags.has_sentinel) blk: {
  18536         const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i]));
  18537         extra_i += 1;
  18538         const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src);
  18539         const val = try sema.resolveConstValue(block, sentinel_src, coerced, "pointer sentinel value must be comptime-known");
  18540         break :blk val.toIntern();
  18541     } else .none;
  18542 
  18543     const abi_align: Alignment = if (inst_data.flags.has_align) blk: {
  18544         const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i]));
  18545         extra_i += 1;
  18546         const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), align_src);
  18547         const val = try sema.resolveConstValue(block, align_src, coerced, "pointer alignment must be comptime-known");
  18548         // Check if this happens to be the lazy alignment of our element type, in
  18549         // which case we can make this 0 without resolving it.
  18550         switch (mod.intern_pool.indexToKey(val.toIntern())) {
  18551             .int => |int| switch (int.storage) {
  18552                 .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none,
  18553                 else => {},
  18554             },
  18555             else => {},
  18556         }
  18557         const abi_align = @as(u32, @intCast((try val.getUnsignedIntAdvanced(mod, sema)).?));
  18558         try sema.validateAlign(block, align_src, abi_align);
  18559         break :blk Alignment.fromByteUnits(abi_align);
  18560     } else .none;
  18561 
  18562     const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: {
  18563         const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i]));
  18564         extra_i += 1;
  18565         break :blk try sema.analyzeAddressSpace(block, addrspace_src, ref, .pointer);
  18566     } else if (elem_ty.zigTypeTag(mod) == .Fn and target.cpu.arch == .avr) .flash else .generic;
  18567 
  18568     const bit_offset = if (inst_data.flags.has_bit_range) blk: {
  18569         const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i]));
  18570         extra_i += 1;
  18571         const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, Type.u16, "pointer bit-offset must be comptime-known");
  18572         break :blk @as(u16, @intCast(bit_offset));
  18573     } else 0;
  18574 
  18575     const host_size: u16 = if (inst_data.flags.has_bit_range) blk: {
  18576         const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i]));
  18577         extra_i += 1;
  18578         const host_size = try sema.resolveInt(block, hostsize_src, ref, Type.u16, "pointer host size must be comptime-known");
  18579         break :blk @as(u16, @intCast(host_size));
  18580     } else 0;
  18581 
  18582     if (host_size != 0 and bit_offset >= host_size * 8) {
  18583         return sema.fail(block, bitoffset_src, "bit offset starts after end of host integer", .{});
  18584     }
  18585 
  18586     if (elem_ty.zigTypeTag(mod) == .Fn) {
  18587         if (inst_data.size != .One) {
  18588             return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{});
  18589         }
  18590         const fn_align = mod.typeToFunc(elem_ty).?.alignment;
  18591         if (inst_data.flags.has_align and abi_align != .none and fn_align != .none and
  18592             abi_align != fn_align)
  18593         {
  18594             return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{});
  18595         }
  18596     } else if (inst_data.size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) {
  18597         return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
  18598     } else if (inst_data.size == .C) {
  18599         if (!try sema.validateExternType(elem_ty, .other)) {
  18600             const msg = msg: {
  18601                 const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)});
  18602                 errdefer msg.destroy(sema.gpa);
  18603 
  18604                 const src_decl = mod.declPtr(block.src_decl);
  18605                 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl, mod), elem_ty, .other);
  18606 
  18607                 try sema.addDeclaredHereNote(msg, elem_ty);
  18608                 break :msg msg;
  18609             };
  18610             return sema.failWithOwnedErrorMsg(msg);
  18611         }
  18612         if (elem_ty.zigTypeTag(mod) == .Opaque) {
  18613             return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{});
  18614         }
  18615     }
  18616 
  18617     const ty = try mod.ptrType(.{
  18618         .child = elem_ty.toIntern(),
  18619         .sentinel = sentinel,
  18620         .flags = .{
  18621             .alignment = abi_align,
  18622             .address_space = address_space,
  18623             .is_const = !inst_data.flags.is_mutable,
  18624             .is_allowzero = inst_data.flags.is_allowzero,
  18625             .is_volatile = inst_data.flags.is_volatile,
  18626             .size = inst_data.size,
  18627         },
  18628         .packed_offset = .{
  18629             .bit_offset = bit_offset,
  18630             .host_size = host_size,
  18631         },
  18632     });
  18633     return sema.addType(ty);
  18634 }
  18635 
  18636 fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18637     const tracy = trace(@src());
  18638     defer tracy.end();
  18639 
  18640     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  18641     const src = inst_data.src();
  18642     const obj_ty = try sema.resolveType(block, src, inst_data.operand);
  18643     const mod = sema.mod;
  18644 
  18645     switch (obj_ty.zigTypeTag(mod)) {
  18646         .Struct => return sema.structInitEmpty(block, obj_ty, src, src),
  18647         .Array, .Vector => return sema.arrayInitEmpty(block, src, obj_ty),
  18648         .Void => return sema.addConstant(Value.void),
  18649         .Union => return sema.fail(block, src, "union initializer must initialize one field", .{}),
  18650         else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
  18651     }
  18652 }
  18653 
  18654 fn structInitEmpty(
  18655     sema: *Sema,
  18656     block: *Block,
  18657     obj_ty: Type,
  18658     dest_src: LazySrcLoc,
  18659     init_src: LazySrcLoc,
  18660 ) CompileError!Air.Inst.Ref {
  18661     const mod = sema.mod;
  18662     const gpa = sema.gpa;
  18663     // This logic must be synchronized with that in `zirStructInit`.
  18664     const struct_ty = try sema.resolveTypeFields(obj_ty);
  18665 
  18666     // The init values to use for the struct instance.
  18667     const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(mod));
  18668     defer gpa.free(field_inits);
  18669     @memset(field_inits, .none);
  18670 
  18671     return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, false);
  18672 }
  18673 
  18674 fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref {
  18675     const mod = sema.mod;
  18676     const arr_len = obj_ty.arrayLen(mod);
  18677     if (arr_len != 0) {
  18678         if (obj_ty.zigTypeTag(mod) == .Array) {
  18679             return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len});
  18680         } else {
  18681             return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len});
  18682         }
  18683     }
  18684     return sema.addConstant((try mod.intern(.{ .aggregate = .{
  18685         .ty = obj_ty.toIntern(),
  18686         .storage = .{ .elems = &.{} },
  18687     } })).toValue());
  18688 }
  18689 
  18690 fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18691     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  18692     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  18693     const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  18694     const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  18695     const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
  18696     const union_ty = try sema.resolveType(block, ty_src, extra.union_type);
  18697     const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, "name of field being initialized must be comptime-known");
  18698     const init = try sema.resolveInst(extra.init);
  18699     return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src);
  18700 }
  18701 
  18702 fn unionInit(
  18703     sema: *Sema,
  18704     block: *Block,
  18705     uncasted_init: Air.Inst.Ref,
  18706     init_src: LazySrcLoc,
  18707     union_ty: Type,
  18708     union_ty_src: LazySrcLoc,
  18709     field_name: InternPool.NullTerminatedString,
  18710     field_src: LazySrcLoc,
  18711 ) CompileError!Air.Inst.Ref {
  18712     const mod = sema.mod;
  18713     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
  18714     const field = union_ty.unionFields(mod).values()[field_index];
  18715     const init = try sema.coerce(block, field.ty, uncasted_init, init_src);
  18716 
  18717     if (try sema.resolveMaybeUndefVal(init)) |init_val| {
  18718         const tag_ty = union_ty.unionTagTypeHypothetical(mod);
  18719         const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?));
  18720         const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index);
  18721         return sema.addConstant((try mod.intern(.{ .un = .{
  18722             .ty = union_ty.toIntern(),
  18723             .tag = try tag_val.intern(tag_ty, mod),
  18724             .val = try init_val.intern(field.ty, mod),
  18725         } })).toValue());
  18726     }
  18727 
  18728     try sema.requireRuntimeBlock(block, init_src, null);
  18729     _ = union_ty_src;
  18730     try sema.queueFullTypeResolution(union_ty);
  18731     return block.addUnionInit(union_ty, field_index, init);
  18732 }
  18733 
  18734 fn zirStructInit(
  18735     sema: *Sema,
  18736     block: *Block,
  18737     inst: Zir.Inst.Index,
  18738     is_ref: bool,
  18739 ) CompileError!Air.Inst.Ref {
  18740     const gpa = sema.gpa;
  18741     const zir_datas = sema.code.instructions.items(.data);
  18742     const inst_data = zir_datas[inst].pl_node;
  18743     const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index);
  18744     const src = inst_data.src();
  18745 
  18746     const mod = sema.mod;
  18747     const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;
  18748     const first_field_type_data = zir_datas[first_item.field_type].pl_node;
  18749     const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;
  18750     const resolved_ty = try sema.resolveType(block, src, first_field_type_extra.container_type);
  18751     try sema.resolveTypeLayout(resolved_ty);
  18752 
  18753     if (resolved_ty.zigTypeTag(mod) == .Struct) {
  18754         // This logic must be synchronized with that in `zirStructInitEmpty`.
  18755 
  18756         // Maps field index to field_type index of where it was already initialized.
  18757         // For making sure all fields are accounted for and no fields are duplicated.
  18758         const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(mod));
  18759         defer gpa.free(found_fields);
  18760 
  18761         // The init values to use for the struct instance.
  18762         const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(mod));
  18763         defer gpa.free(field_inits);
  18764         @memset(field_inits, .none);
  18765 
  18766         var field_i: u32 = 0;
  18767         var extra_index = extra.end;
  18768 
  18769         const is_packed = resolved_ty.containerLayout(mod) == .Packed;
  18770         while (field_i < extra.data.fields_len) : (field_i += 1) {
  18771             const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index);
  18772             extra_index = item.end;
  18773 
  18774             const field_type_data = zir_datas[item.data.field_type].pl_node;
  18775             const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node };
  18776             const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
  18777             const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start));
  18778             const field_index = if (resolved_ty.isTuple(mod))
  18779                 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)
  18780             else
  18781                 try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
  18782             if (field_inits[field_index] != .none) {
  18783                 const other_field_type = found_fields[field_index];
  18784                 const other_field_type_data = zir_datas[other_field_type].pl_node;
  18785                 const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_type_data.src_node };
  18786                 const msg = msg: {
  18787                     const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
  18788                     errdefer msg.destroy(gpa);
  18789                     try sema.errNote(block, other_field_src, msg, "other field here", .{});
  18790                     break :msg msg;
  18791                 };
  18792                 return sema.failWithOwnedErrorMsg(msg);
  18793             }
  18794             found_fields[field_index] = item.data.field_type;
  18795             field_inits[field_index] = try sema.resolveInst(item.data.init);
  18796             if (!is_packed) if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| {
  18797                 const init_val = (try sema.resolveMaybeUndefVal(field_inits[field_index])) orelse {
  18798                     return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known");
  18799                 };
  18800 
  18801                 if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) {
  18802                     return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
  18803                 }
  18804             };
  18805         }
  18806 
  18807         return sema.finishStructInit(block, src, src, field_inits, resolved_ty, is_ref);
  18808     } else if (resolved_ty.zigTypeTag(mod) == .Union) {
  18809         if (extra.data.fields_len != 1) {
  18810             return sema.fail(block, src, "union initialization expects exactly one field", .{});
  18811         }
  18812 
  18813         const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end);
  18814 
  18815         const field_type_data = zir_datas[item.data.field_type].pl_node;
  18816         const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node };
  18817         const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
  18818         const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start));
  18819         const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src);
  18820         const tag_ty = resolved_ty.unionTagTypeHypothetical(mod);
  18821         const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?));
  18822         const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index);
  18823 
  18824         const init_inst = try sema.resolveInst(item.data.init);
  18825         if (try sema.resolveMaybeUndefVal(init_inst)) |val| {
  18826             const field = resolved_ty.unionFields(mod).values()[field_index];
  18827             return sema.addConstantMaybeRef(block, resolved_ty, (try mod.intern(.{ .un = .{
  18828                 .ty = resolved_ty.toIntern(),
  18829                 .tag = try tag_val.intern(tag_ty, mod),
  18830                 .val = try val.intern(field.ty, mod),
  18831             } })).toValue(), is_ref);
  18832         }
  18833 
  18834         if (is_ref) {
  18835             const target = mod.getTarget();
  18836             const alloc_ty = try mod.ptrType(.{
  18837                 .child = resolved_ty.toIntern(),
  18838                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  18839             });
  18840             const alloc = try block.addTy(.alloc, alloc_ty);
  18841             const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty, true);
  18842             try sema.storePtr(block, src, field_ptr, init_inst);
  18843             const new_tag = try sema.addConstant(tag_val);
  18844             _ = try block.addBinOp(.set_union_tag, alloc, new_tag);
  18845             return sema.makePtrConst(block, alloc);
  18846         }
  18847 
  18848         try sema.requireRuntimeBlock(block, src, null);
  18849         try sema.queueFullTypeResolution(resolved_ty);
  18850         return block.addUnionInit(resolved_ty, field_index, init_inst);
  18851     } else if (resolved_ty.isAnonStruct(mod)) {
  18852         return sema.fail(block, src, "TODO anon struct init validation", .{});
  18853     }
  18854     unreachable;
  18855 }
  18856 
  18857 fn finishStructInit(
  18858     sema: *Sema,
  18859     block: *Block,
  18860     init_src: LazySrcLoc,
  18861     dest_src: LazySrcLoc,
  18862     field_inits: []Air.Inst.Ref,
  18863     struct_ty: Type,
  18864     is_ref: bool,
  18865 ) CompileError!Air.Inst.Ref {
  18866     const mod = sema.mod;
  18867     const ip = &mod.intern_pool;
  18868 
  18869     var root_msg: ?*Module.ErrorMsg = null;
  18870     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  18871 
  18872     switch (ip.indexToKey(struct_ty.toIntern())) {
  18873         .anon_struct_type => |anon_struct| {
  18874             for (anon_struct.values, 0..) |default_val, i| {
  18875                 if (field_inits[i] != .none) continue;
  18876 
  18877                 if (default_val == .none) {
  18878                     if (anon_struct.names.len == 0) {
  18879                         const template = "missing tuple field with index {d}";
  18880                         if (root_msg) |msg| {
  18881                             try sema.errNote(block, init_src, msg, template, .{i});
  18882                         } else {
  18883                             root_msg = try sema.errMsg(block, init_src, template, .{i});
  18884                         }
  18885                     } else {
  18886                         const field_name = anon_struct.names[i];
  18887                         const template = "missing struct field: {}";
  18888                         const args = .{field_name.fmt(ip)};
  18889                         if (root_msg) |msg| {
  18890                             try sema.errNote(block, init_src, msg, template, args);
  18891                         } else {
  18892                             root_msg = try sema.errMsg(block, init_src, template, args);
  18893                         }
  18894                     }
  18895                 } else {
  18896                     field_inits[i] = try sema.addConstant(default_val.toValue());
  18897                 }
  18898             }
  18899         },
  18900         .struct_type => |struct_type| {
  18901             const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
  18902             for (struct_obj.fields.values(), 0..) |field, i| {
  18903                 if (field_inits[i] != .none) continue;
  18904 
  18905                 if (field.default_val == .none) {
  18906                     const field_name = struct_obj.fields.keys()[i];
  18907                     const template = "missing struct field: {}";
  18908                     const args = .{field_name.fmt(ip)};
  18909                     if (root_msg) |msg| {
  18910                         try sema.errNote(block, init_src, msg, template, args);
  18911                     } else {
  18912                         root_msg = try sema.errMsg(block, init_src, template, args);
  18913                     }
  18914                 } else {
  18915                     field_inits[i] = try sema.addConstant(field.default_val.toValue());
  18916                 }
  18917             }
  18918         },
  18919         else => unreachable,
  18920     }
  18921 
  18922     if (root_msg) |msg| {
  18923         if (mod.typeToStruct(struct_ty)) |struct_obj| {
  18924             const fqn = try struct_obj.getFullyQualifiedName(mod);
  18925             try mod.errNoteNonLazy(
  18926                 struct_obj.srcLoc(mod),
  18927                 msg,
  18928                 "struct '{}' declared here",
  18929                 .{fqn.fmt(ip)},
  18930             );
  18931         }
  18932         root_msg = null;
  18933         return sema.failWithOwnedErrorMsg(msg);
  18934     }
  18935 
  18936     // Find which field forces the expression to be runtime, if any.
  18937     const opt_runtime_index = for (field_inits, 0..) |field_init, i| {
  18938         if (!(try sema.isComptimeKnown(field_init))) {
  18939             break i;
  18940         }
  18941     } else null;
  18942 
  18943     const runtime_index = opt_runtime_index orelse {
  18944         const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
  18945         for (elems, field_inits, 0..) |*elem, field_init, field_i| {
  18946             elem.* = try (sema.resolveMaybeUndefVal(field_init) catch unreachable).?
  18947                 .intern(struct_ty.structFieldType(field_i, mod), mod);
  18948         }
  18949         const struct_val = try mod.intern(.{ .aggregate = .{
  18950             .ty = struct_ty.toIntern(),
  18951             .storage = .{ .elems = elems },
  18952         } });
  18953         return sema.addConstantMaybeRef(block, struct_ty, struct_val.toValue(), is_ref);
  18954     };
  18955 
  18956     if (is_ref) {
  18957         try sema.resolveStructLayout(struct_ty);
  18958         const target = sema.mod.getTarget();
  18959         const alloc_ty = try mod.ptrType(.{
  18960             .child = struct_ty.toIntern(),
  18961             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  18962         });
  18963         const alloc = try block.addTy(.alloc, alloc_ty);
  18964         for (field_inits, 0..) |field_init, i_usize| {
  18965             const i = @as(u32, @intCast(i_usize));
  18966             const field_src = dest_src;
  18967             const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, alloc, i, field_src, struct_ty, true);
  18968             try sema.storePtr(block, dest_src, field_ptr, field_init);
  18969         }
  18970 
  18971         return sema.makePtrConst(block, alloc);
  18972     }
  18973 
  18974     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
  18975         error.NeededSourceLocation => {
  18976             const decl = mod.declPtr(block.src_decl);
  18977             const field_src = mod.initSrc(dest_src.node_offset.x, decl, runtime_index);
  18978             try sema.requireRuntimeBlock(block, dest_src, field_src);
  18979             unreachable;
  18980         },
  18981         else => |e| return e,
  18982     };
  18983     try sema.queueFullTypeResolution(struct_ty);
  18984     return block.addAggregateInit(struct_ty, field_inits);
  18985 }
  18986 
  18987 fn zirStructInitAnon(
  18988     sema: *Sema,
  18989     block: *Block,
  18990     inst: Zir.Inst.Index,
  18991     is_ref: bool,
  18992 ) CompileError!Air.Inst.Ref {
  18993     const mod = sema.mod;
  18994     const gpa = sema.gpa;
  18995     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  18996     const src = inst_data.src();
  18997     const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
  18998     const types = try sema.arena.alloc(InternPool.Index, extra.data.fields_len);
  18999     const values = try sema.arena.alloc(InternPool.Index, types.len);
  19000     var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena);
  19001     try fields.ensureUnusedCapacity(types.len);
  19002 
  19003     // Find which field forces the expression to be runtime, if any.
  19004     const opt_runtime_index = rs: {
  19005         var runtime_index: ?usize = null;
  19006         var extra_index = extra.end;
  19007         for (types, 0..) |*field_ty, i_usize| {
  19008             const i = @as(u32, @intCast(i_usize));
  19009             const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
  19010             extra_index = item.end;
  19011 
  19012             const name = sema.code.nullTerminatedString(item.data.field_name);
  19013             const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
  19014             const gop = fields.getOrPutAssumeCapacity(name_ip);
  19015             if (gop.found_existing) {
  19016                 const msg = msg: {
  19017                     const decl = mod.declPtr(block.src_decl);
  19018                     const field_src = mod.initSrc(src.node_offset.x, decl, i);
  19019                     const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
  19020                     errdefer msg.destroy(gpa);
  19021 
  19022                     const prev_source = mod.initSrc(src.node_offset.x, decl, gop.value_ptr.*);
  19023                     try sema.errNote(block, prev_source, msg, "other field here", .{});
  19024                     break :msg msg;
  19025                 };
  19026                 return sema.failWithOwnedErrorMsg(msg);
  19027             }
  19028             gop.value_ptr.* = i;
  19029 
  19030             const init = try sema.resolveInst(item.data.init);
  19031             field_ty.* = sema.typeOf(init).toIntern();
  19032             if (field_ty.toType().zigTypeTag(mod) == .Opaque) {
  19033                 const msg = msg: {
  19034                     const decl = mod.declPtr(block.src_decl);
  19035                     const field_src = mod.initSrc(src.node_offset.x, decl, i);
  19036                     const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  19037                     errdefer msg.destroy(sema.gpa);
  19038 
  19039                     try sema.addDeclaredHereNote(msg, field_ty.toType());
  19040                     break :msg msg;
  19041                 };
  19042                 return sema.failWithOwnedErrorMsg(msg);
  19043             }
  19044             if (try sema.resolveMaybeUndefVal(init)) |init_val| {
  19045                 values[i] = try init_val.intern(field_ty.toType(), mod);
  19046             } else {
  19047                 values[i] = .none;
  19048                 runtime_index = i;
  19049             }
  19050         }
  19051         break :rs runtime_index;
  19052     };
  19053 
  19054     const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
  19055         .names = fields.keys(),
  19056         .types = types,
  19057         .values = values,
  19058     } });
  19059 
  19060     const runtime_index = opt_runtime_index orelse {
  19061         const tuple_val = try mod.intern(.{ .aggregate = .{
  19062             .ty = tuple_ty,
  19063             .storage = .{ .elems = values },
  19064         } });
  19065         return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref);
  19066     };
  19067 
  19068     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
  19069         error.NeededSourceLocation => {
  19070             const decl = mod.declPtr(block.src_decl);
  19071             const field_src = mod.initSrc(src.node_offset.x, decl, runtime_index);
  19072             try sema.requireRuntimeBlock(block, src, field_src);
  19073             unreachable;
  19074         },
  19075         else => |e| return e,
  19076     };
  19077 
  19078     if (is_ref) {
  19079         const target = mod.getTarget();
  19080         const alloc_ty = try mod.ptrType(.{
  19081             .child = tuple_ty,
  19082             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19083         });
  19084         const alloc = try block.addTy(.alloc, alloc_ty);
  19085         var extra_index = extra.end;
  19086         for (types, 0..) |field_ty, i_usize| {
  19087             const i = @as(u32, @intCast(i_usize));
  19088             const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
  19089             extra_index = item.end;
  19090 
  19091             const field_ptr_ty = try mod.ptrType(.{
  19092                 .child = field_ty,
  19093                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19094             });
  19095             if (values[i] == .none) {
  19096                 const init = try sema.resolveInst(item.data.init);
  19097                 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
  19098                 _ = try block.addBinOp(.store, field_ptr, init);
  19099             }
  19100         }
  19101 
  19102         return sema.makePtrConst(block, alloc);
  19103     }
  19104 
  19105     const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len);
  19106     var extra_index = extra.end;
  19107     for (types, 0..) |_, i| {
  19108         const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
  19109         extra_index = item.end;
  19110         element_refs[i] = try sema.resolveInst(item.data.init);
  19111     }
  19112 
  19113     return block.addAggregateInit(tuple_ty.toType(), element_refs);
  19114 }
  19115 
  19116 fn zirArrayInit(
  19117     sema: *Sema,
  19118     block: *Block,
  19119     inst: Zir.Inst.Index,
  19120     is_ref: bool,
  19121 ) CompileError!Air.Inst.Ref {
  19122     const mod = sema.mod;
  19123     const gpa = sema.gpa;
  19124     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  19125     const src = inst_data.src();
  19126 
  19127     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
  19128     const args = sema.code.refSlice(extra.end, extra.data.operands_len);
  19129     assert(args.len >= 2); // array_ty + at least one element
  19130 
  19131     const array_ty = try sema.resolveType(block, src, args[0]);
  19132     const sentinel_val = array_ty.sentinel(mod);
  19133 
  19134     const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @intFromBool(sentinel_val != null));
  19135     defer gpa.free(resolved_args);
  19136     for (args[1..], 0..) |arg, i| {
  19137         const resolved_arg = try sema.resolveInst(arg);
  19138         const elem_ty = if (array_ty.zigTypeTag(mod) == .Struct)
  19139             array_ty.structFieldType(i, mod)
  19140         else
  19141             array_ty.elemType2(mod);
  19142         resolved_args[i] = sema.coerce(block, elem_ty, resolved_arg, .unneeded) catch |err| switch (err) {
  19143             error.NeededSourceLocation => {
  19144                 const decl = mod.declPtr(block.src_decl);
  19145                 const elem_src = mod.initSrc(src.node_offset.x, decl, i);
  19146                 _ = try sema.coerce(block, elem_ty, resolved_arg, elem_src);
  19147                 unreachable;
  19148             },
  19149             else => return err,
  19150         };
  19151     }
  19152 
  19153     if (sentinel_val) |some| {
  19154         resolved_args[resolved_args.len - 1] = try sema.addConstant(some);
  19155     }
  19156 
  19157     const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| {
  19158         const comptime_known = try sema.isComptimeKnown(arg);
  19159         if (!comptime_known) break @as(u32, @intCast(i));
  19160     } else null;
  19161 
  19162     const runtime_index = opt_runtime_index orelse {
  19163         const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len);
  19164         for (elem_vals, resolved_args, 0..) |*val, arg, i| {
  19165             const elem_ty = if (array_ty.zigTypeTag(mod) == .Struct)
  19166                 array_ty.structFieldType(i, mod)
  19167             else
  19168                 array_ty.elemType2(mod);
  19169             // We checked that all args are comptime above.
  19170             val.* = try ((sema.resolveMaybeUndefVal(arg) catch unreachable).?).intern(elem_ty, mod);
  19171         }
  19172         return sema.addConstantMaybeRef(block, array_ty, (try mod.intern(.{ .aggregate = .{
  19173             .ty = array_ty.toIntern(),
  19174             .storage = .{ .elems = elem_vals },
  19175         } })).toValue(), is_ref);
  19176     };
  19177 
  19178     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
  19179         error.NeededSourceLocation => {
  19180             const decl = mod.declPtr(block.src_decl);
  19181             const elem_src = mod.initSrc(src.node_offset.x, decl, runtime_index);
  19182             try sema.requireRuntimeBlock(block, src, elem_src);
  19183             unreachable;
  19184         },
  19185         else => return err,
  19186     };
  19187     try sema.queueFullTypeResolution(array_ty);
  19188 
  19189     if (is_ref) {
  19190         const target = mod.getTarget();
  19191         const alloc_ty = try mod.ptrType(.{
  19192             .child = array_ty.toIntern(),
  19193             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19194         });
  19195         const alloc = try block.addTy(.alloc, alloc_ty);
  19196 
  19197         if (array_ty.isTuple(mod)) {
  19198             for (resolved_args, 0..) |arg, i| {
  19199                 const elem_ptr_ty = try mod.ptrType(.{
  19200                     .child = array_ty.structFieldType(i, mod).toIntern(),
  19201                     .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19202                 });
  19203                 const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty);
  19204 
  19205                 const index = try sema.addIntUnsigned(Type.usize, i);
  19206                 const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref);
  19207                 _ = try block.addBinOp(.store, elem_ptr, arg);
  19208             }
  19209             return sema.makePtrConst(block, alloc);
  19210         }
  19211 
  19212         const elem_ptr_ty = try mod.ptrType(.{
  19213             .child = array_ty.elemType2(mod).toIntern(),
  19214             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19215         });
  19216         const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty);
  19217 
  19218         for (resolved_args, 0..) |arg, i| {
  19219             const index = try sema.addIntUnsigned(Type.usize, i);
  19220             const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref);
  19221             _ = try block.addBinOp(.store, elem_ptr, arg);
  19222         }
  19223         return sema.makePtrConst(block, alloc);
  19224     }
  19225 
  19226     return block.addAggregateInit(array_ty, resolved_args);
  19227 }
  19228 
  19229 fn zirArrayInitAnon(
  19230     sema: *Sema,
  19231     block: *Block,
  19232     inst: Zir.Inst.Index,
  19233     is_ref: bool,
  19234 ) CompileError!Air.Inst.Ref {
  19235     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  19236     const src = inst_data.src();
  19237     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
  19238     const operands = sema.code.refSlice(extra.end, extra.data.operands_len);
  19239     const mod = sema.mod;
  19240 
  19241     const types = try sema.arena.alloc(InternPool.Index, operands.len);
  19242     const values = try sema.arena.alloc(InternPool.Index, operands.len);
  19243 
  19244     const opt_runtime_src = rs: {
  19245         var runtime_src: ?LazySrcLoc = null;
  19246         for (operands, 0..) |operand, i| {
  19247             const operand_src = src; // TODO better source location
  19248             const elem = try sema.resolveInst(operand);
  19249             types[i] = sema.typeOf(elem).toIntern();
  19250             if (types[i].toType().zigTypeTag(mod) == .Opaque) {
  19251                 const msg = msg: {
  19252                     const msg = try sema.errMsg(block, operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  19253                     errdefer msg.destroy(sema.gpa);
  19254 
  19255                     try sema.addDeclaredHereNote(msg, types[i].toType());
  19256                     break :msg msg;
  19257                 };
  19258                 return sema.failWithOwnedErrorMsg(msg);
  19259             }
  19260             if (try sema.resolveMaybeUndefVal(elem)) |val| {
  19261                 values[i] = val.toIntern();
  19262             } else {
  19263                 values[i] = .none;
  19264                 runtime_src = operand_src;
  19265             }
  19266         }
  19267         break :rs runtime_src;
  19268     };
  19269 
  19270     const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
  19271         .types = types,
  19272         .values = values,
  19273         .names = &.{},
  19274     } });
  19275 
  19276     const runtime_src = opt_runtime_src orelse {
  19277         const tuple_val = try mod.intern(.{ .aggregate = .{
  19278             .ty = tuple_ty,
  19279             .storage = .{ .elems = values },
  19280         } });
  19281         return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref);
  19282     };
  19283 
  19284     try sema.requireRuntimeBlock(block, src, runtime_src);
  19285 
  19286     if (is_ref) {
  19287         const target = sema.mod.getTarget();
  19288         const alloc_ty = try mod.ptrType(.{
  19289             .child = tuple_ty,
  19290             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19291         });
  19292         const alloc = try block.addTy(.alloc, alloc_ty);
  19293         for (operands, 0..) |operand, i_usize| {
  19294             const i = @as(u32, @intCast(i_usize));
  19295             const field_ptr_ty = try mod.ptrType(.{
  19296                 .child = types[i],
  19297                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19298             });
  19299             if (values[i] == .none) {
  19300                 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
  19301                 _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand));
  19302             }
  19303         }
  19304 
  19305         return sema.makePtrConst(block, alloc);
  19306     }
  19307 
  19308     const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
  19309     for (operands, 0..) |operand, i| {
  19310         element_refs[i] = try sema.resolveInst(operand);
  19311     }
  19312 
  19313     return block.addAggregateInit(tuple_ty.toType(), element_refs);
  19314 }
  19315 
  19316 fn addConstantMaybeRef(
  19317     sema: *Sema,
  19318     block: *Block,
  19319     ty: Type,
  19320     val: Value,
  19321     is_ref: bool,
  19322 ) !Air.Inst.Ref {
  19323     if (!is_ref) return sema.addConstant(val);
  19324 
  19325     var anon_decl = try block.startAnonDecl();
  19326     defer anon_decl.deinit();
  19327     const decl = try anon_decl.finish(
  19328         ty,
  19329         val,
  19330         .none, // default alignment
  19331     );
  19332     return sema.analyzeDeclRef(decl);
  19333 }
  19334 
  19335 fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19336     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  19337     const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data;
  19338     const ty_src = inst_data.src();
  19339     const field_src = inst_data.src();
  19340     const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
  19341     const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, "field name must be comptime-known");
  19342     return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
  19343 }
  19344 
  19345 fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19346     const mod = sema.mod;
  19347     const ip = &mod.intern_pool;
  19348     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  19349     const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
  19350     const ty_src = inst_data.src();
  19351     const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
  19352     const aggregate_ty = sema.resolveType(block, ty_src, extra.container_type) catch |err| switch (err) {
  19353         // Since this is a ZIR instruction that returns a type, encountering
  19354         // generic poison should not result in a failed compilation, but the
  19355         // generic poison type. This prevents unnecessary failures when
  19356         // constructing types at compile-time.
  19357         error.GenericPoison => return Air.Inst.Ref.generic_poison_type,
  19358         else => |e| return e,
  19359     };
  19360     const zir_field_name = sema.code.nullTerminatedString(extra.name_start);
  19361     const field_name = try ip.getOrPutString(sema.gpa, zir_field_name);
  19362     return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src);
  19363 }
  19364 
  19365 fn fieldType(
  19366     sema: *Sema,
  19367     block: *Block,
  19368     aggregate_ty: Type,
  19369     field_name: InternPool.NullTerminatedString,
  19370     field_src: LazySrcLoc,
  19371     ty_src: LazySrcLoc,
  19372 ) CompileError!Air.Inst.Ref {
  19373     const mod = sema.mod;
  19374     var cur_ty = aggregate_ty;
  19375     while (true) {
  19376         const resolved_ty = try sema.resolveTypeFields(cur_ty);
  19377         cur_ty = resolved_ty;
  19378         switch (cur_ty.zigTypeTag(mod)) {
  19379             .Struct => switch (mod.intern_pool.indexToKey(cur_ty.toIntern())) {
  19380                 .anon_struct_type => |anon_struct| {
  19381                     const field_index = try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src);
  19382                     return sema.addType(anon_struct.types[field_index].toType());
  19383                 },
  19384                 .struct_type => |struct_type| {
  19385                     const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
  19386                     const field = struct_obj.fields.get(field_name) orelse
  19387                         return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
  19388                     return sema.addType(field.ty);
  19389                 },
  19390                 else => unreachable,
  19391             },
  19392             .Union => {
  19393                 const union_obj = mod.typeToUnion(cur_ty).?;
  19394                 const field = union_obj.fields.get(field_name) orelse
  19395                     return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
  19396                 return sema.addType(field.ty);
  19397             },
  19398             .Optional => {
  19399                 // Struct/array init through optional requires the child type to not be a pointer.
  19400                 // If the child of .optional is a pointer it'll error on the next loop.
  19401                 cur_ty = mod.intern_pool.indexToKey(cur_ty.toIntern()).opt_type.toType();
  19402                 continue;
  19403             },
  19404             .ErrorUnion => {
  19405                 cur_ty = cur_ty.errorUnionPayload(mod);
  19406                 continue;
  19407             },
  19408             else => {},
  19409         }
  19410         return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{
  19411             resolved_ty.fmt(sema.mod),
  19412         });
  19413     }
  19414 }
  19415 
  19416 fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
  19417     return sema.getErrorReturnTrace(block);
  19418 }
  19419 
  19420 fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
  19421     const mod = sema.mod;
  19422     const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
  19423     const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
  19424     const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty);
  19425     const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern());
  19426 
  19427     if (sema.owner_func != null and
  19428         sema.owner_func.?.calls_or_awaits_errorable_fn and
  19429         mod.comp.bin_file.options.error_return_tracing and
  19430         mod.backendSupportsFeature(.error_return_trace))
  19431     {
  19432         return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
  19433     }
  19434     return sema.addConstant((try mod.intern(.{ .opt = .{
  19435         .ty = opt_ptr_stack_trace_ty.toIntern(),
  19436         .val = .none,
  19437     } })).toValue());
  19438 }
  19439 
  19440 fn zirFrame(
  19441     sema: *Sema,
  19442     block: *Block,
  19443     extended: Zir.Inst.Extended.InstData,
  19444 ) CompileError!Air.Inst.Ref {
  19445     const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand)));
  19446     return sema.failWithUseOfAsync(block, src);
  19447 }
  19448 
  19449 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19450     const mod = sema.mod;
  19451     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  19452     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  19453     const ty = try sema.resolveType(block, operand_src, inst_data.operand);
  19454     if (ty.isNoReturn(mod)) {
  19455         return sema.fail(block, operand_src, "no align available for type '{}'", .{ty.fmt(sema.mod)});
  19456     }
  19457     const val = try ty.lazyAbiAlignment(mod);
  19458     if (val.isLazyAlign(mod)) {
  19459         try sema.queueFullTypeResolution(ty);
  19460     }
  19461     return sema.addConstant(val);
  19462 }
  19463 
  19464 fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19465     const mod = sema.mod;
  19466     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  19467     const operand = try sema.resolveInst(inst_data.operand);
  19468     if (try sema.resolveMaybeUndefVal(operand)) |val| {
  19469         if (val.isUndef(mod)) return sema.addConstUndef(Type.u1);
  19470         if (val.toBool()) return sema.addConstant(try mod.intValue(Type.u1, 1));
  19471         return sema.addConstant(try mod.intValue(Type.u1, 0));
  19472     }
  19473     return block.addUnOp(.int_from_bool, operand);
  19474 }
  19475 
  19476 fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19477     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  19478     const operand = try sema.resolveInst(inst_data.operand);
  19479     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  19480 
  19481     if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
  19482         const err_name = sema.mod.intern_pool.indexToKey(val.toIntern()).err.name;
  19483         return sema.addStrLit(block, sema.mod.intern_pool.stringToSlice(err_name));
  19484     }
  19485 
  19486     // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass
  19487     // might be able to resolve the result at compile time.
  19488     return block.addUnOp(.error_name, operand);
  19489 }
  19490 
  19491 fn zirUnaryMath(
  19492     sema: *Sema,
  19493     block: *Block,
  19494     inst: Zir.Inst.Index,
  19495     air_tag: Air.Inst.Tag,
  19496     comptime eval: fn (Value, Type, Allocator, *Module) Allocator.Error!Value,
  19497 ) CompileError!Air.Inst.Ref {
  19498     const tracy = trace(@src());
  19499     defer tracy.end();
  19500 
  19501     const mod = sema.mod;
  19502     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  19503     const operand = try sema.resolveInst(inst_data.operand);
  19504     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  19505     const operand_ty = sema.typeOf(operand);
  19506 
  19507     switch (operand_ty.zigTypeTag(mod)) {
  19508         .ComptimeFloat, .Float => {},
  19509         .Vector => {
  19510             const scalar_ty = operand_ty.scalarType(mod);
  19511             switch (scalar_ty.zigTypeTag(mod)) {
  19512                 .ComptimeFloat, .Float => {},
  19513                 else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(sema.mod)}),
  19514             }
  19515         },
  19516         else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(sema.mod)}),
  19517     }
  19518 
  19519     switch (operand_ty.zigTypeTag(mod)) {
  19520         .Vector => {
  19521             const scalar_ty = operand_ty.scalarType(mod);
  19522             const vec_len = operand_ty.vectorLen(mod);
  19523             const result_ty = try mod.vectorType(.{
  19524                 .len = vec_len,
  19525                 .child = scalar_ty.toIntern(),
  19526             });
  19527             if (try sema.resolveMaybeUndefVal(operand)) |val| {
  19528                 if (val.isUndef(mod))
  19529                     return sema.addConstUndef(result_ty);
  19530 
  19531                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  19532                 for (elems, 0..) |*elem, i| {
  19533                     const elem_val = try val.elemValue(sema.mod, i);
  19534                     elem.* = try (try eval(elem_val, scalar_ty, sema.arena, sema.mod)).intern(scalar_ty, mod);
  19535                 }
  19536                 return sema.addConstant((try mod.intern(.{ .aggregate = .{
  19537                     .ty = result_ty.toIntern(),
  19538                     .storage = .{ .elems = elems },
  19539                 } })).toValue());
  19540             }
  19541 
  19542             try sema.requireRuntimeBlock(block, operand_src, null);
  19543             return block.addUnOp(air_tag, operand);
  19544         },
  19545         .ComptimeFloat, .Float => {
  19546             if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
  19547                 if (operand_val.isUndef(mod))
  19548                     return sema.addConstUndef(operand_ty);
  19549                 const result_val = try eval(operand_val, operand_ty, sema.arena, sema.mod);
  19550                 return sema.addConstant(result_val);
  19551             }
  19552 
  19553             try sema.requireRuntimeBlock(block, operand_src, null);
  19554             return block.addUnOp(air_tag, operand);
  19555         },
  19556         else => unreachable,
  19557     }
  19558 }
  19559 
  19560 fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19561     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  19562     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  19563     const src = inst_data.src();
  19564     const operand = try sema.resolveInst(inst_data.operand);
  19565     const operand_ty = sema.typeOf(operand);
  19566     const mod = sema.mod;
  19567     const ip = &mod.intern_pool;
  19568 
  19569     try sema.resolveTypeLayout(operand_ty);
  19570     const enum_ty = switch (operand_ty.zigTypeTag(mod)) {
  19571         .EnumLiteral => {
  19572             const val = try sema.resolveConstValue(block, .unneeded, operand, "");
  19573             const tag_name = ip.indexToKey(val.toIntern()).enum_literal;
  19574             return sema.addStrLit(block, ip.stringToSlice(tag_name));
  19575         },
  19576         .Enum => operand_ty,
  19577         .Union => operand_ty.unionTagType(mod) orelse {
  19578             const msg = msg: {
  19579                 const msg = try sema.errMsg(block, src, "union '{}' is untagged", .{
  19580                     operand_ty.fmt(sema.mod),
  19581                 });
  19582                 errdefer msg.destroy(sema.gpa);
  19583                 try sema.addDeclaredHereNote(msg, operand_ty);
  19584                 break :msg msg;
  19585             };
  19586             return sema.failWithOwnedErrorMsg(msg);
  19587         },
  19588         else => return sema.fail(block, operand_src, "expected enum or union; found '{}'", .{
  19589             operand_ty.fmt(mod),
  19590         }),
  19591     };
  19592     if (enum_ty.enumFieldCount(mod) == 0) {
  19593         // TODO I don't think this is the correct way to handle this but
  19594         // it prevents a crash.
  19595         return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{}'", .{
  19596             enum_ty.fmt(mod),
  19597         });
  19598     }
  19599     const enum_decl_index = enum_ty.getOwnerDecl(mod);
  19600     const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);
  19601     if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {
  19602         const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse {
  19603             const enum_decl = mod.declPtr(enum_decl_index);
  19604             const msg = msg: {
  19605                 const msg = try sema.errMsg(block, src, "no field with value '{}' in enum '{}'", .{
  19606                     val.fmtValue(enum_ty, sema.mod), enum_decl.name.fmt(ip),
  19607                 });
  19608                 errdefer msg.destroy(sema.gpa);
  19609                 try mod.errNoteNonLazy(enum_decl.srcLoc(mod), msg, "declared here", .{});
  19610                 break :msg msg;
  19611             };
  19612             return sema.failWithOwnedErrorMsg(msg);
  19613         };
  19614         // TODO: write something like getCoercedInts to avoid needing to dupe
  19615         const field_name = enum_ty.enumFieldName(field_index, mod);
  19616         return sema.addStrLit(block, ip.stringToSlice(field_name));
  19617     }
  19618     try sema.requireRuntimeBlock(block, src, operand_src);
  19619     if (block.wantSafety() and sema.mod.backendSupportsFeature(.is_named_enum_value)) {
  19620         const ok = try block.addUnOp(.is_named_enum_value, casted_operand);
  19621         try sema.addSafetyCheck(block, ok, .invalid_enum_value);
  19622     }
  19623     // In case the value is runtime-known, we have an AIR instruction for this instead
  19624     // of trying to lower it in Sema because an optimization pass may result in the operand
  19625     // being comptime-known, which would let us elide the `tag_name` AIR instruction.
  19626     return block.addUnOp(.tag_name, casted_operand);
  19627 }
  19628 
  19629 fn zirReify(
  19630     sema: *Sema,
  19631     block: *Block,
  19632     extended: Zir.Inst.Extended.InstData,
  19633     inst: Zir.Inst.Index,
  19634 ) CompileError!Air.Inst.Ref {
  19635     const mod = sema.mod;
  19636     const gpa = sema.gpa;
  19637     const ip = &mod.intern_pool;
  19638     const name_strategy = @as(Zir.Inst.NameStrategy, @enumFromInt(extended.small));
  19639     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  19640     const src = LazySrcLoc.nodeOffset(extra.node);
  19641     const type_info_ty = try sema.getBuiltinType("Type");
  19642     const uncasted_operand = try sema.resolveInst(extra.operand);
  19643     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  19644     const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
  19645     const val = try sema.resolveConstValue(block, operand_src, type_info, "operand to @Type must be comptime-known");
  19646     const union_val = ip.indexToKey(val.toIntern()).un;
  19647     const target = mod.getTarget();
  19648     if (try union_val.val.toValue().anyUndef(mod)) return sema.failWithUseOfUndef(block, src);
  19649     const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag.toValue(), mod).?;
  19650     switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) {
  19651         .Type => return Air.Inst.Ref.type_type,
  19652         .Void => return Air.Inst.Ref.void_type,
  19653         .Bool => return Air.Inst.Ref.bool_type,
  19654         .NoReturn => return Air.Inst.Ref.noreturn_type,
  19655         .ComptimeFloat => return Air.Inst.Ref.comptime_float_type,
  19656         .ComptimeInt => return Air.Inst.Ref.comptime_int_type,
  19657         .Undefined => return Air.Inst.Ref.undefined_type,
  19658         .Null => return Air.Inst.Ref.null_type,
  19659         .AnyFrame => return sema.failWithUseOfAsync(block, src),
  19660         .EnumLiteral => return Air.Inst.Ref.enum_literal_type,
  19661         .Int => {
  19662             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19663             const signedness_val = try union_val.val.toValue().fieldValue(
  19664                 mod,
  19665                 fields.getIndex(try ip.getOrPutString(gpa, "signedness")).?,
  19666             );
  19667             const bits_val = try union_val.val.toValue().fieldValue(
  19668                 mod,
  19669                 fields.getIndex(try ip.getOrPutString(gpa, "bits")).?,
  19670             );
  19671 
  19672             const signedness = mod.toEnum(std.builtin.Signedness, signedness_val);
  19673             const bits = @as(u16, @intCast(bits_val.toUnsignedInt(mod)));
  19674             const ty = try mod.intType(signedness, bits);
  19675             return sema.addType(ty);
  19676         },
  19677         .Vector => {
  19678             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19679             const len_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19680                 try ip.getOrPutString(gpa, "len"),
  19681             ).?);
  19682             const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19683                 try ip.getOrPutString(gpa, "child"),
  19684             ).?);
  19685 
  19686             const len = @as(u32, @intCast(len_val.toUnsignedInt(mod)));
  19687             const child_ty = child_val.toType();
  19688 
  19689             try sema.checkVectorElemType(block, src, child_ty);
  19690 
  19691             const ty = try mod.vectorType(.{
  19692                 .len = len,
  19693                 .child = child_ty.toIntern(),
  19694             });
  19695             return sema.addType(ty);
  19696         },
  19697         .Float => {
  19698             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19699             const bits_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19700                 try ip.getOrPutString(gpa, "bits"),
  19701             ).?);
  19702 
  19703             const bits = @as(u16, @intCast(bits_val.toUnsignedInt(mod)));
  19704             const ty = switch (bits) {
  19705                 16 => Type.f16,
  19706                 32 => Type.f32,
  19707                 64 => Type.f64,
  19708                 80 => Type.f80,
  19709                 128 => Type.f128,
  19710                 else => return sema.fail(block, src, "{}-bit float unsupported", .{bits}),
  19711             };
  19712             return sema.addType(ty);
  19713         },
  19714         .Pointer => {
  19715             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19716             const size_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19717                 try ip.getOrPutString(gpa, "size"),
  19718             ).?);
  19719             const is_const_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19720                 try ip.getOrPutString(gpa, "is_const"),
  19721             ).?);
  19722             const is_volatile_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19723                 try ip.getOrPutString(gpa, "is_volatile"),
  19724             ).?);
  19725             const alignment_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19726                 try ip.getOrPutString(gpa, "alignment"),
  19727             ).?);
  19728             const address_space_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19729                 try ip.getOrPutString(gpa, "address_space"),
  19730             ).?);
  19731             const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19732                 try ip.getOrPutString(gpa, "child"),
  19733             ).?);
  19734             const is_allowzero_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19735                 try ip.getOrPutString(gpa, "is_allowzero"),
  19736             ).?);
  19737             const sentinel_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19738                 try ip.getOrPutString(gpa, "sentinel"),
  19739             ).?);
  19740 
  19741             if (!try sema.intFitsInType(alignment_val, Type.u32, null)) {
  19742                 return sema.fail(block, src, "alignment must fit in 'u32'", .{});
  19743             }
  19744 
  19745             const abi_align = Alignment.fromByteUnits(
  19746                 (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?,
  19747             );
  19748 
  19749             const unresolved_elem_ty = child_val.toType();
  19750             const elem_ty = if (abi_align == .none)
  19751                 unresolved_elem_ty
  19752             else t: {
  19753                 const elem_ty = try sema.resolveTypeFields(unresolved_elem_ty);
  19754                 try sema.resolveTypeLayout(elem_ty);
  19755                 break :t elem_ty;
  19756             };
  19757 
  19758             const ptr_size = mod.toEnum(std.builtin.Type.Pointer.Size, size_val);
  19759 
  19760             const actual_sentinel: InternPool.Index = s: {
  19761                 if (!sentinel_val.isNull(mod)) {
  19762                     if (ptr_size == .One or ptr_size == .C) {
  19763                         return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{});
  19764                     }
  19765                     const sentinel_ptr_val = sentinel_val.optionalValue(mod).?;
  19766                     const ptr_ty = try mod.singleMutPtrType(elem_ty);
  19767                     const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?;
  19768                     break :s sent_val.toIntern();
  19769                 }
  19770                 break :s .none;
  19771             };
  19772 
  19773             if (elem_ty.zigTypeTag(mod) == .NoReturn) {
  19774                 return sema.fail(block, src, "pointer to noreturn not allowed", .{});
  19775             } else if (elem_ty.zigTypeTag(mod) == .Fn) {
  19776                 if (ptr_size != .One) {
  19777                     return sema.fail(block, src, "function pointers must be single pointers", .{});
  19778                 }
  19779                 const fn_align = mod.typeToFunc(elem_ty).?.alignment;
  19780                 if (abi_align != .none and fn_align != .none and
  19781                     abi_align != fn_align)
  19782                 {
  19783                     return sema.fail(block, src, "function pointer alignment disagrees with function alignment", .{});
  19784                 }
  19785             } else if (ptr_size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) {
  19786                 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});
  19787             } else if (ptr_size == .C) {
  19788                 if (!try sema.validateExternType(elem_ty, .other)) {
  19789                     const msg = msg: {
  19790                         const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)});
  19791                         errdefer msg.destroy(gpa);
  19792 
  19793                         const src_decl = mod.declPtr(block.src_decl);
  19794                         try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), elem_ty, .other);
  19795 
  19796                         try sema.addDeclaredHereNote(msg, elem_ty);
  19797                         break :msg msg;
  19798                     };
  19799                     return sema.failWithOwnedErrorMsg(msg);
  19800                 }
  19801                 if (elem_ty.zigTypeTag(mod) == .Opaque) {
  19802                     return sema.fail(block, src, "C pointers cannot point to opaque types", .{});
  19803                 }
  19804             }
  19805 
  19806             const ty = try mod.ptrType(.{
  19807                 .child = elem_ty.toIntern(),
  19808                 .sentinel = actual_sentinel,
  19809                 .flags = .{
  19810                     .size = ptr_size,
  19811                     .is_const = is_const_val.toBool(),
  19812                     .is_volatile = is_volatile_val.toBool(),
  19813                     .alignment = abi_align,
  19814                     .address_space = mod.toEnum(std.builtin.AddressSpace, address_space_val),
  19815                     .is_allowzero = is_allowzero_val.toBool(),
  19816                 },
  19817             });
  19818             return sema.addType(ty);
  19819         },
  19820         .Array => {
  19821             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19822             const len_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19823                 try ip.getOrPutString(gpa, "len"),
  19824             ).?);
  19825             const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19826                 try ip.getOrPutString(gpa, "child"),
  19827             ).?);
  19828             const sentinel_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19829                 try ip.getOrPutString(gpa, "sentinel"),
  19830             ).?);
  19831 
  19832             const len = len_val.toUnsignedInt(mod);
  19833             const child_ty = child_val.toType();
  19834             const sentinel = if (sentinel_val.optionalValue(mod)) |p| blk: {
  19835                 const ptr_ty = try mod.singleMutPtrType(child_ty);
  19836                 break :blk (try sema.pointerDeref(block, src, p, ptr_ty)).?;
  19837             } else null;
  19838 
  19839             const ty = try mod.arrayType(.{
  19840                 .len = len,
  19841                 .sentinel = if (sentinel) |s| s.toIntern() else .none,
  19842                 .child = child_ty.toIntern(),
  19843             });
  19844             return sema.addType(ty);
  19845         },
  19846         .Optional => {
  19847             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19848             const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19849                 try ip.getOrPutString(gpa, "child"),
  19850             ).?);
  19851 
  19852             const child_ty = child_val.toType();
  19853 
  19854             const ty = try mod.optionalType(child_ty.toIntern());
  19855             return sema.addType(ty);
  19856         },
  19857         .ErrorUnion => {
  19858             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19859             const error_set_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19860                 try ip.getOrPutString(gpa, "error_set"),
  19861             ).?);
  19862             const payload_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19863                 try ip.getOrPutString(gpa, "payload"),
  19864             ).?);
  19865 
  19866             const error_set_ty = error_set_val.toType();
  19867             const payload_ty = payload_val.toType();
  19868 
  19869             if (error_set_ty.zigTypeTag(mod) != .ErrorSet) {
  19870                 return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{});
  19871             }
  19872 
  19873             const ty = try mod.errorUnionType(error_set_ty, payload_ty);
  19874             return sema.addType(ty);
  19875         },
  19876         .ErrorSet => {
  19877             const payload_val = union_val.val.toValue().optionalValue(mod) orelse
  19878                 return sema.addType(Type.anyerror);
  19879 
  19880             const len = try sema.usizeCast(block, src, payload_val.sliceLen(mod));
  19881             var names: Module.Fn.InferredErrorSet.NameMap = .{};
  19882             try names.ensureUnusedCapacity(sema.arena, len);
  19883             for (0..len) |i| {
  19884                 const elem_val = try payload_val.elemValue(mod, i);
  19885                 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod);
  19886                 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  19887                     try ip.getOrPutString(gpa, "name"),
  19888                 ).?);
  19889 
  19890                 const name = try name_val.toIpString(Type.slice_const_u8, mod);
  19891                 _ = try mod.getErrorValue(name);
  19892                 const gop = names.getOrPutAssumeCapacity(name);
  19893                 if (gop.found_existing) {
  19894                     return sema.fail(block, src, "duplicate error '{}'", .{
  19895                         name.fmt(ip),
  19896                     });
  19897                 }
  19898             }
  19899 
  19900             const ty = try mod.errorSetFromUnsortedNames(names.keys());
  19901             return sema.addType(ty);
  19902         },
  19903         .Struct => {
  19904             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19905             const layout_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19906                 try ip.getOrPutString(gpa, "layout"),
  19907             ).?);
  19908             const backing_integer_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19909                 try ip.getOrPutString(gpa, "backing_integer"),
  19910             ).?);
  19911             const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19912                 try ip.getOrPutString(gpa, "fields"),
  19913             ).?);
  19914             const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19915                 try ip.getOrPutString(gpa, "decls"),
  19916             ).?);
  19917             const is_tuple_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19918                 try ip.getOrPutString(gpa, "is_tuple"),
  19919             ).?);
  19920 
  19921             const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val);
  19922 
  19923             // Decls
  19924             if (decls_val.sliceLen(mod) > 0) {
  19925                 return sema.fail(block, src, "reified structs must have no decls", .{});
  19926             }
  19927 
  19928             if (layout != .Packed and !backing_integer_val.isNull(mod)) {
  19929                 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{});
  19930             }
  19931 
  19932             return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_val, name_strategy, is_tuple_val.toBool());
  19933         },
  19934         .Enum => {
  19935             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19936             const tag_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19937                 try ip.getOrPutString(gpa, "tag_type"),
  19938             ).?);
  19939             const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19940                 try ip.getOrPutString(gpa, "fields"),
  19941             ).?);
  19942             const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19943                 try ip.getOrPutString(gpa, "decls"),
  19944             ).?);
  19945             const is_exhaustive_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19946                 try ip.getOrPutString(gpa, "is_exhaustive"),
  19947             ).?);
  19948 
  19949             // Decls
  19950             if (decls_val.sliceLen(mod) > 0) {
  19951                 return sema.fail(block, src, "reified enums must have no decls", .{});
  19952             }
  19953 
  19954             const int_tag_ty = tag_type_val.toType();
  19955             if (int_tag_ty.zigTypeTag(mod) != .Int) {
  19956                 return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{});
  19957             }
  19958 
  19959             // Because these things each reference each other, `undefined`
  19960             // placeholders are used before being set after the enum type gains
  19961             // an InternPool index.
  19962 
  19963             const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  19964                 .ty = Type.noreturn,
  19965                 .val = Value.@"unreachable",
  19966             }, name_strategy, "enum", inst);
  19967             const new_decl = mod.declPtr(new_decl_index);
  19968             new_decl.owns_tv = true;
  19969             errdefer {
  19970                 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  19971                 mod.abortAnonDecl(new_decl_index);
  19972             }
  19973 
  19974             // Define our empty enum decl
  19975             const fields_len = @as(u32, @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))));
  19976             const incomplete_enum = try ip.getIncompleteEnum(gpa, .{
  19977                 .decl = new_decl_index,
  19978                 .namespace = .none,
  19979                 .fields_len = fields_len,
  19980                 .has_values = true,
  19981                 .tag_mode = if (!is_exhaustive_val.toBool())
  19982                     .nonexhaustive
  19983                 else
  19984                     .explicit,
  19985                 .tag_ty = int_tag_ty.toIntern(),
  19986             });
  19987             // TODO: figure out InternPool removals for incremental compilation
  19988             //errdefer ip.remove(incomplete_enum.index);
  19989 
  19990             new_decl.ty = Type.type;
  19991             new_decl.val = incomplete_enum.index.toValue();
  19992 
  19993             for (0..fields_len) |field_i| {
  19994                 const elem_val = try fields_val.elemValue(mod, field_i);
  19995                 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod);
  19996                 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  19997                     try ip.getOrPutString(gpa, "name"),
  19998                 ).?);
  19999                 const value_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20000                     try ip.getOrPutString(gpa, "value"),
  20001                 ).?);
  20002 
  20003                 const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
  20004 
  20005                 if (!try sema.intFitsInType(value_val, int_tag_ty, null)) {
  20006                     // TODO: better source location
  20007                     return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{
  20008                         field_name.fmt(ip),
  20009                         value_val.fmtValue(Type.comptime_int, mod),
  20010                         int_tag_ty.fmt(mod),
  20011                     });
  20012                 }
  20013 
  20014                 if (try incomplete_enum.addFieldName(ip, gpa, field_name)) |other_index| {
  20015                     const msg = msg: {
  20016                         const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{
  20017                             field_name.fmt(ip),
  20018                         });
  20019                         errdefer msg.destroy(gpa);
  20020                         _ = other_index; // TODO: this note is incorrect
  20021                         try sema.errNote(block, src, msg, "other field here", .{});
  20022                         break :msg msg;
  20023                     };
  20024                     return sema.failWithOwnedErrorMsg(msg);
  20025                 }
  20026 
  20027                 if (try incomplete_enum.addFieldValue(ip, gpa, (try mod.getCoerced(value_val, int_tag_ty)).toIntern())) |other| {
  20028                     const msg = msg: {
  20029                         const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{value_val.fmtValue(Type.comptime_int, mod)});
  20030                         errdefer msg.destroy(gpa);
  20031                         _ = other; // TODO: this note is incorrect
  20032                         try sema.errNote(block, src, msg, "other enum tag value here", .{});
  20033                         break :msg msg;
  20034                     };
  20035                     return sema.failWithOwnedErrorMsg(msg);
  20036                 }
  20037             }
  20038 
  20039             const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  20040             try mod.finalizeAnonDecl(new_decl_index);
  20041             return decl_val;
  20042         },
  20043         .Opaque => {
  20044             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  20045             const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20046                 try ip.getOrPutString(gpa, "decls"),
  20047             ).?);
  20048 
  20049             // Decls
  20050             if (decls_val.sliceLen(mod) > 0) {
  20051                 return sema.fail(block, src, "reified opaque must have no decls", .{});
  20052             }
  20053 
  20054             // Because these three things each reference each other,
  20055             // `undefined` placeholders are used in two places before being set
  20056             // after the opaque type gains an InternPool index.
  20057 
  20058             const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  20059                 .ty = Type.noreturn,
  20060                 .val = Value.@"unreachable",
  20061             }, name_strategy, "opaque", inst);
  20062             const new_decl = mod.declPtr(new_decl_index);
  20063             new_decl.owns_tv = true;
  20064             errdefer {
  20065                 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  20066                 mod.abortAnonDecl(new_decl_index);
  20067             }
  20068 
  20069             const new_namespace_index = try mod.createNamespace(.{
  20070                 .parent = block.namespace.toOptional(),
  20071                 .ty = undefined,
  20072                 .file_scope = block.getFileScope(mod),
  20073             });
  20074             const new_namespace = mod.namespacePtr(new_namespace_index);
  20075             errdefer mod.destroyNamespace(new_namespace_index);
  20076 
  20077             const opaque_ty = try mod.intern(.{ .opaque_type = .{
  20078                 .decl = new_decl_index,
  20079                 .namespace = new_namespace_index,
  20080             } });
  20081             // TODO: figure out InternPool removals for incremental compilation
  20082             //errdefer ip.remove(opaque_ty);
  20083 
  20084             new_decl.ty = Type.type;
  20085             new_decl.val = opaque_ty.toValue();
  20086             new_namespace.ty = opaque_ty.toType();
  20087 
  20088             const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  20089             try mod.finalizeAnonDecl(new_decl_index);
  20090             return decl_val;
  20091         },
  20092         .Union => {
  20093             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  20094             const layout_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20095                 try ip.getOrPutString(gpa, "layout"),
  20096             ).?);
  20097             const tag_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20098                 try ip.getOrPutString(gpa, "tag_type"),
  20099             ).?);
  20100             const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20101                 try ip.getOrPutString(gpa, "fields"),
  20102             ).?);
  20103             const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20104                 try ip.getOrPutString(gpa, "decls"),
  20105             ).?);
  20106 
  20107             // Decls
  20108             if (decls_val.sliceLen(mod) > 0) {
  20109                 return sema.fail(block, src, "reified unions must have no decls", .{});
  20110             }
  20111             const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val);
  20112 
  20113             // Because these three things each reference each other, `undefined`
  20114             // placeholders are used before being set after the union type gains an
  20115             // InternPool index.
  20116 
  20117             const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  20118                 .ty = Type.noreturn,
  20119                 .val = Value.@"unreachable",
  20120             }, name_strategy, "union", inst);
  20121             const new_decl = mod.declPtr(new_decl_index);
  20122             new_decl.owns_tv = true;
  20123             errdefer {
  20124                 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  20125                 mod.abortAnonDecl(new_decl_index);
  20126             }
  20127 
  20128             const new_namespace_index = try mod.createNamespace(.{
  20129                 .parent = block.namespace.toOptional(),
  20130                 .ty = undefined,
  20131                 .file_scope = block.getFileScope(mod),
  20132             });
  20133             const new_namespace = mod.namespacePtr(new_namespace_index);
  20134             errdefer mod.destroyNamespace(new_namespace_index);
  20135 
  20136             const union_index = try mod.createUnion(.{
  20137                 .owner_decl = new_decl_index,
  20138                 .tag_ty = Type.null,
  20139                 .fields = .{},
  20140                 .zir_index = inst,
  20141                 .layout = layout,
  20142                 .status = .have_field_types,
  20143                 .namespace = new_namespace_index,
  20144             });
  20145             const union_obj = mod.unionPtr(union_index);
  20146             errdefer mod.destroyUnion(union_index);
  20147 
  20148             const union_ty = try ip.get(gpa, .{ .union_type = .{
  20149                 .index = union_index,
  20150                 .runtime_tag = if (!tag_type_val.isNull(mod))
  20151                     .tagged
  20152                 else if (layout != .Auto)
  20153                     .none
  20154                 else switch (mod.optimizeMode()) {
  20155                     .Debug, .ReleaseSafe => .safety,
  20156                     .ReleaseFast, .ReleaseSmall => .none,
  20157                 },
  20158             } });
  20159             // TODO: figure out InternPool removals for incremental compilation
  20160             //errdefer ip.remove(union_ty);
  20161 
  20162             new_decl.ty = Type.type;
  20163             new_decl.val = union_ty.toValue();
  20164             new_namespace.ty = union_ty.toType();
  20165 
  20166             // Tag type
  20167             const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
  20168             var explicit_tags_seen: []bool = &.{};
  20169             var enum_field_names: []InternPool.NullTerminatedString = &.{};
  20170             if (tag_type_val.optionalValue(mod)) |payload_val| {
  20171                 union_obj.tag_ty = payload_val.toType();
  20172 
  20173                 const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) {
  20174                     .enum_type => |x| x,
  20175                     else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}),
  20176                 };
  20177 
  20178                 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
  20179                 @memset(explicit_tags_seen, false);
  20180             } else {
  20181                 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  20182             }
  20183 
  20184             // Fields
  20185             try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len);
  20186 
  20187             for (0..fields_len) |i| {
  20188                 const elem_val = try fields_val.elemValue(mod, i);
  20189                 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod);
  20190                 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20191                     try ip.getOrPutString(gpa, "name"),
  20192                 ).?);
  20193                 const type_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20194                     try ip.getOrPutString(gpa, "type"),
  20195                 ).?);
  20196                 const alignment_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20197                     try ip.getOrPutString(gpa, "alignment"),
  20198                 ).?);
  20199 
  20200                 const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
  20201 
  20202                 if (enum_field_names.len != 0) {
  20203                     enum_field_names[i] = field_name;
  20204                 }
  20205 
  20206                 if (explicit_tags_seen.len > 0) {
  20207                     const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
  20208                     const enum_index = tag_info.nameIndex(ip, field_name) orelse {
  20209                         const msg = msg: {
  20210                             const msg = try sema.errMsg(block, src, "no field named '{}' in enum '{}'", .{
  20211                                 field_name.fmt(ip),
  20212                                 union_obj.tag_ty.fmt(mod),
  20213                             });
  20214                             errdefer msg.destroy(gpa);
  20215                             try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
  20216                             break :msg msg;
  20217                         };
  20218                         return sema.failWithOwnedErrorMsg(msg);
  20219                     };
  20220                     // No check for duplicate because the check already happened in order
  20221                     // to create the enum type in the first place.
  20222                     assert(!explicit_tags_seen[enum_index]);
  20223                     explicit_tags_seen[enum_index] = true;
  20224                 }
  20225 
  20226                 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
  20227                 if (gop.found_existing) {
  20228                     // TODO: better source location
  20229                     return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)});
  20230                 }
  20231 
  20232                 const field_ty = type_val.toType();
  20233                 gop.value_ptr.* = .{
  20234                     .ty = field_ty,
  20235                     .abi_align = Alignment.fromByteUnits((try alignment_val.getUnsignedIntAdvanced(mod, sema)).?),
  20236                 };
  20237 
  20238                 if (field_ty.zigTypeTag(mod) == .Opaque) {
  20239                     const msg = msg: {
  20240                         const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
  20241                         errdefer msg.destroy(gpa);
  20242 
  20243                         try sema.addDeclaredHereNote(msg, field_ty);
  20244                         break :msg msg;
  20245                     };
  20246                     return sema.failWithOwnedErrorMsg(msg);
  20247                 }
  20248                 if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) {
  20249                     const msg = msg: {
  20250                         const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  20251                         errdefer msg.destroy(gpa);
  20252 
  20253                         const src_decl = mod.declPtr(block.src_decl);
  20254                         try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field);
  20255 
  20256                         try sema.addDeclaredHereNote(msg, field_ty);
  20257                         break :msg msg;
  20258                     };
  20259                     return sema.failWithOwnedErrorMsg(msg);
  20260                 } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) {
  20261                     const msg = msg: {
  20262                         const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  20263                         errdefer msg.destroy(gpa);
  20264 
  20265                         const src_decl = mod.declPtr(block.src_decl);
  20266                         try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty);
  20267 
  20268                         try sema.addDeclaredHereNote(msg, field_ty);
  20269                         break :msg msg;
  20270                     };
  20271                     return sema.failWithOwnedErrorMsg(msg);
  20272                 }
  20273             }
  20274 
  20275             if (explicit_tags_seen.len > 0) {
  20276                 const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
  20277                 if (tag_info.names.len > fields_len) {
  20278                     const msg = msg: {
  20279                         const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
  20280                         errdefer msg.destroy(gpa);
  20281 
  20282                         const enum_ty = union_obj.tag_ty;
  20283                         for (tag_info.names, 0..) |field_name, field_index| {
  20284                             if (explicit_tags_seen[field_index]) continue;
  20285                             try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{
  20286                                 field_name.fmt(ip),
  20287                             });
  20288                         }
  20289                         try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
  20290                         break :msg msg;
  20291                     };
  20292                     return sema.failWithOwnedErrorMsg(msg);
  20293                 }
  20294             } else {
  20295                 union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, null);
  20296             }
  20297 
  20298             const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  20299             try mod.finalizeAnonDecl(new_decl_index);
  20300             return decl_val;
  20301         },
  20302         .Fn => {
  20303             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  20304             const calling_convention_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20305                 try ip.getOrPutString(gpa, "calling_convention"),
  20306             ).?);
  20307             const alignment_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20308                 try ip.getOrPutString(gpa, "alignment"),
  20309             ).?);
  20310             const is_generic_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20311                 try ip.getOrPutString(gpa, "is_generic"),
  20312             ).?);
  20313             const is_var_args_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20314                 try ip.getOrPutString(gpa, "is_var_args"),
  20315             ).?);
  20316             const return_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20317                 try ip.getOrPutString(gpa, "return_type"),
  20318             ).?);
  20319             const params_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20320                 try ip.getOrPutString(gpa, "params"),
  20321             ).?);
  20322 
  20323             const is_generic = is_generic_val.toBool();
  20324             if (is_generic) {
  20325                 return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{});
  20326             }
  20327 
  20328             const is_var_args = is_var_args_val.toBool();
  20329             const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val);
  20330             if (is_var_args and cc != .C) {
  20331                 return sema.fail(block, src, "varargs functions must have C calling convention", .{});
  20332             }
  20333 
  20334             const alignment = alignment: {
  20335                 if (!try sema.intFitsInType(alignment_val, Type.u32, null)) {
  20336                     return sema.fail(block, src, "alignment must fit in 'u32'", .{});
  20337                 }
  20338                 const alignment = @as(u29, @intCast(alignment_val.toUnsignedInt(mod)));
  20339                 if (alignment == target_util.defaultFunctionAlignment(target)) {
  20340                     break :alignment .none;
  20341                 } else {
  20342                     break :alignment Alignment.fromByteUnits(alignment);
  20343                 }
  20344             };
  20345             const return_type = return_type_val.optionalValue(mod) orelse
  20346                 return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{});
  20347 
  20348             const args_len = try sema.usizeCast(block, src, params_val.sliceLen(mod));
  20349             const param_types = try sema.arena.alloc(InternPool.Index, args_len);
  20350 
  20351             var noalias_bits: u32 = 0;
  20352             for (param_types, 0..) |*param_type, i| {
  20353                 const elem_val = try params_val.elemValue(mod, i);
  20354                 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod);
  20355                 const param_is_generic_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20356                     try ip.getOrPutString(gpa, "is_generic"),
  20357                 ).?);
  20358                 const param_is_noalias_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20359                     try ip.getOrPutString(gpa, "is_noalias"),
  20360                 ).?);
  20361                 const opt_param_type_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20362                     try ip.getOrPutString(gpa, "type"),
  20363                 ).?);
  20364 
  20365                 if (param_is_generic_val.toBool()) {
  20366                     return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{});
  20367                 }
  20368 
  20369                 const param_type_val = opt_param_type_val.optionalValue(mod) orelse
  20370                     return sema.fail(block, src, "Type.Fn.Param.arg_type must be non-null for @Type", .{});
  20371                 param_type.* = param_type_val.toIntern();
  20372 
  20373                 if (param_is_noalias_val.toBool()) {
  20374                     if (!param_type.toType().isPtrAtRuntime(mod)) {
  20375                         return sema.fail(block, src, "non-pointer parameter declared noalias", .{});
  20376                     }
  20377                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, i) orelse
  20378                         return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
  20379                 }
  20380             }
  20381 
  20382             const ty = try mod.funcType(.{
  20383                 .param_types = param_types,
  20384                 .comptime_bits = 0,
  20385                 .noalias_bits = noalias_bits,
  20386                 .return_type = return_type.toIntern(),
  20387                 .alignment = alignment,
  20388                 .cc = cc,
  20389                 .is_var_args = is_var_args,
  20390                 .is_generic = false,
  20391                 .is_noinline = false,
  20392                 .align_is_generic = false,
  20393                 .cc_is_generic = false,
  20394                 .section_is_generic = false,
  20395                 .addrspace_is_generic = false,
  20396             });
  20397             return sema.addType(ty);
  20398         },
  20399         .Frame => return sema.failWithUseOfAsync(block, src),
  20400     }
  20401 }
  20402 
  20403 fn reifyStruct(
  20404     sema: *Sema,
  20405     block: *Block,
  20406     inst: Zir.Inst.Index,
  20407     src: LazySrcLoc,
  20408     layout: std.builtin.Type.ContainerLayout,
  20409     backing_int_val: Value,
  20410     fields_val: Value,
  20411     name_strategy: Zir.Inst.NameStrategy,
  20412     is_tuple: bool,
  20413 ) CompileError!Air.Inst.Ref {
  20414     const mod = sema.mod;
  20415     const gpa = sema.gpa;
  20416     const ip = &mod.intern_pool;
  20417 
  20418     // Because these three things each reference each other, `undefined`
  20419     // placeholders are used before being set after the struct type gains an
  20420     // InternPool index.
  20421 
  20422     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  20423         .ty = Type.noreturn,
  20424         .val = Value.@"unreachable",
  20425     }, name_strategy, "struct", inst);
  20426     const new_decl = mod.declPtr(new_decl_index);
  20427     new_decl.owns_tv = true;
  20428     errdefer {
  20429         new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  20430         mod.abortAnonDecl(new_decl_index);
  20431     }
  20432 
  20433     const new_namespace_index = try mod.createNamespace(.{
  20434         .parent = block.namespace.toOptional(),
  20435         .ty = undefined,
  20436         .file_scope = block.getFileScope(mod),
  20437     });
  20438     const new_namespace = mod.namespacePtr(new_namespace_index);
  20439     errdefer mod.destroyNamespace(new_namespace_index);
  20440 
  20441     const struct_index = try mod.createStruct(.{
  20442         .owner_decl = new_decl_index,
  20443         .fields = .{},
  20444         .zir_index = inst,
  20445         .layout = layout,
  20446         .status = .have_field_types,
  20447         .known_non_opv = false,
  20448         .is_tuple = is_tuple,
  20449         .namespace = new_namespace_index,
  20450     });
  20451     const struct_obj = mod.structPtr(struct_index);
  20452     errdefer mod.destroyStruct(struct_index);
  20453 
  20454     const struct_ty = try ip.get(gpa, .{ .struct_type = .{
  20455         .index = struct_index.toOptional(),
  20456         .namespace = new_namespace_index.toOptional(),
  20457     } });
  20458     // TODO: figure out InternPool removals for incremental compilation
  20459     //errdefer ip.remove(struct_ty);
  20460 
  20461     new_decl.ty = Type.type;
  20462     new_decl.val = struct_ty.toValue();
  20463     new_namespace.ty = struct_ty.toType();
  20464 
  20465     // Fields
  20466     const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
  20467     try struct_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len);
  20468     var i: usize = 0;
  20469     while (i < fields_len) : (i += 1) {
  20470         const elem_val = try fields_val.elemValue(mod, i);
  20471         const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod);
  20472         const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20473             try ip.getOrPutString(gpa, "name"),
  20474         ).?);
  20475         const type_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20476             try ip.getOrPutString(gpa, "type"),
  20477         ).?);
  20478         const default_value_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20479             try ip.getOrPutString(gpa, "default_value"),
  20480         ).?);
  20481         const is_comptime_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20482             try ip.getOrPutString(gpa, "is_comptime"),
  20483         ).?);
  20484         const alignment_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20485             try ip.getOrPutString(gpa, "alignment"),
  20486         ).?);
  20487 
  20488         if (!try sema.intFitsInType(alignment_val, Type.u32, null)) {
  20489             return sema.fail(block, src, "alignment must fit in 'u32'", .{});
  20490         }
  20491         const abi_align = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?;
  20492 
  20493         if (layout == .Packed) {
  20494             if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
  20495             if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{});
  20496         }
  20497         if (layout == .Extern and is_comptime_val.toBool()) {
  20498             return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{});
  20499         }
  20500 
  20501         const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
  20502 
  20503         if (is_tuple) {
  20504             const field_index = field_name.toUnsigned(ip) orelse return sema.fail(
  20505                 block,
  20506                 src,
  20507                 "tuple cannot have non-numeric field '{}'",
  20508                 .{field_name.fmt(ip)},
  20509             );
  20510 
  20511             if (field_index >= fields_len) {
  20512                 return sema.fail(
  20513                     block,
  20514                     src,
  20515                     "tuple field {} exceeds tuple field count",
  20516                     .{field_index},
  20517                 );
  20518             }
  20519         }
  20520         const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
  20521         if (gop.found_existing) {
  20522             // TODO: better source location
  20523             return sema.fail(block, src, "duplicate struct field {}", .{field_name.fmt(ip)});
  20524         }
  20525 
  20526         const field_ty = type_val.toType();
  20527         const default_val = if (default_value_val.optionalValue(mod)) |opt_val|
  20528             (try sema.pointerDeref(block, src, opt_val, try mod.singleConstPtrType(field_ty)) orelse
  20529                 return sema.failWithNeededComptime(block, src, "struct field default value must be comptime-known")).toIntern()
  20530         else
  20531             .none;
  20532         if (is_comptime_val.toBool() and default_val == .none) {
  20533             return sema.fail(block, src, "comptime field without default initialization value", .{});
  20534         }
  20535 
  20536         gop.value_ptr.* = .{
  20537             .ty = field_ty,
  20538             .abi_align = Alignment.fromByteUnits(abi_align),
  20539             .default_val = default_val,
  20540             .is_comptime = is_comptime_val.toBool(),
  20541             .offset = undefined,
  20542         };
  20543 
  20544         if (field_ty.zigTypeTag(mod) == .Opaque) {
  20545             const msg = msg: {
  20546                 const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  20547                 errdefer msg.destroy(gpa);
  20548 
  20549                 try sema.addDeclaredHereNote(msg, field_ty);
  20550                 break :msg msg;
  20551             };
  20552             return sema.failWithOwnedErrorMsg(msg);
  20553         }
  20554         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  20555             const msg = msg: {
  20556                 const msg = try sema.errMsg(block, src, "struct fields cannot be 'noreturn'", .{});
  20557                 errdefer msg.destroy(gpa);
  20558 
  20559                 try sema.addDeclaredHereNote(msg, field_ty);
  20560                 break :msg msg;
  20561             };
  20562             return sema.failWithOwnedErrorMsg(msg);
  20563         }
  20564         if (struct_obj.layout == .Extern and !try sema.validateExternType(field_ty, .struct_field)) {
  20565             const msg = msg: {
  20566                 const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
  20567                 errdefer msg.destroy(gpa);
  20568 
  20569                 const src_decl = sema.mod.declPtr(block.src_decl);
  20570                 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .struct_field);
  20571 
  20572                 try sema.addDeclaredHereNote(msg, field_ty);
  20573                 break :msg msg;
  20574             };
  20575             return sema.failWithOwnedErrorMsg(msg);
  20576         } else if (struct_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) {
  20577             const msg = msg: {
  20578                 const msg = try sema.errMsg(block, src, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
  20579                 errdefer msg.destroy(gpa);
  20580 
  20581                 const src_decl = sema.mod.declPtr(block.src_decl);
  20582                 try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty);
  20583 
  20584                 try sema.addDeclaredHereNote(msg, field_ty);
  20585                 break :msg msg;
  20586             };
  20587             return sema.failWithOwnedErrorMsg(msg);
  20588         }
  20589     }
  20590 
  20591     if (layout == .Packed) {
  20592         struct_obj.status = .layout_wip;
  20593 
  20594         for (struct_obj.fields.values(), 0..) |field, index| {
  20595             sema.resolveTypeLayout(field.ty) catch |err| switch (err) {
  20596                 error.AnalysisFail => {
  20597                     const msg = sema.err orelse return err;
  20598                     try sema.addFieldErrNote(struct_ty.toType(), index, msg, "while checking this field", .{});
  20599                     return err;
  20600                 },
  20601                 else => return err,
  20602             };
  20603         }
  20604 
  20605         var fields_bit_sum: u64 = 0;
  20606         for (struct_obj.fields.values()) |field| {
  20607             fields_bit_sum += field.ty.bitSize(mod);
  20608         }
  20609 
  20610         if (backing_int_val.optionalValue(mod)) |payload| {
  20611             const backing_int_ty = payload.toType();
  20612             try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum);
  20613             struct_obj.backing_int_ty = backing_int_ty;
  20614         } else {
  20615             struct_obj.backing_int_ty = try mod.intType(.unsigned, @as(u16, @intCast(fields_bit_sum)));
  20616         }
  20617 
  20618         struct_obj.status = .have_layout;
  20619     }
  20620 
  20621     const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  20622     try mod.finalizeAnonDecl(new_decl_index);
  20623     return decl_val;
  20624 }
  20625 
  20626 fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
  20627     const va_list_ty = try sema.getBuiltinType("VaList");
  20628     const va_list_ptr = try sema.mod.singleMutPtrType(va_list_ty);
  20629 
  20630     const inst = try sema.resolveInst(zir_ref);
  20631     return sema.coerce(block, va_list_ptr, inst, src);
  20632 }
  20633 
  20634 fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20635     const mod = sema.mod;
  20636     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  20637     const src = LazySrcLoc.nodeOffset(extra.node);
  20638     const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  20639     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  20640 
  20641     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs);
  20642     const arg_ty = try sema.resolveType(block, ty_src, extra.rhs);
  20643 
  20644     if (!try sema.validateExternType(arg_ty, .param_ty)) {
  20645         const msg = msg: {
  20646             const msg = try sema.errMsg(block, ty_src, "cannot get '{}' from variadic argument", .{arg_ty.fmt(sema.mod)});
  20647             errdefer msg.destroy(sema.gpa);
  20648 
  20649             const src_decl = sema.mod.declPtr(block.src_decl);
  20650             try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), arg_ty, .param_ty);
  20651 
  20652             try sema.addDeclaredHereNote(msg, arg_ty);
  20653             break :msg msg;
  20654         };
  20655         return sema.failWithOwnedErrorMsg(msg);
  20656     }
  20657 
  20658     try sema.requireRuntimeBlock(block, src, null);
  20659     return block.addTyOp(.c_va_arg, arg_ty, va_list_ref);
  20660 }
  20661 
  20662 fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20663     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  20664     const src = LazySrcLoc.nodeOffset(extra.node);
  20665     const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  20666 
  20667     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
  20668     const va_list_ty = try sema.getBuiltinType("VaList");
  20669 
  20670     try sema.requireRuntimeBlock(block, src, null);
  20671     return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref);
  20672 }
  20673 
  20674 fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20675     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  20676     const src = LazySrcLoc.nodeOffset(extra.node);
  20677     const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  20678 
  20679     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
  20680 
  20681     try sema.requireRuntimeBlock(block, src, null);
  20682     return block.addUnOp(.c_va_end, va_list_ref);
  20683 }
  20684 
  20685 fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20686     const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand)));
  20687 
  20688     const va_list_ty = try sema.getBuiltinType("VaList");
  20689     try sema.requireRuntimeBlock(block, src, null);
  20690     return block.addInst(.{
  20691         .tag = .c_va_start,
  20692         .data = .{ .ty = va_list_ty },
  20693     });
  20694 }
  20695 
  20696 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20697     const mod = sema.mod;
  20698     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  20699     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20700     const ty = try sema.resolveType(block, ty_src, inst_data.operand);
  20701 
  20702     var anon_decl = try block.startAnonDecl();
  20703     defer anon_decl.deinit();
  20704 
  20705     var bytes = std.ArrayList(u8).init(sema.arena);
  20706     defer bytes.deinit();
  20707     try ty.print(bytes.writer(), mod);
  20708 
  20709     const decl_ty = try mod.arrayType(.{
  20710         .len = bytes.items.len,
  20711         .sentinel = .zero_u8,
  20712         .child = .u8_type,
  20713     });
  20714     const new_decl = try anon_decl.finish(
  20715         decl_ty,
  20716         (try mod.intern(.{ .aggregate = .{
  20717             .ty = decl_ty.toIntern(),
  20718             .storage = .{ .bytes = bytes.items },
  20719         } })).toValue(),
  20720         .none, // default alignment
  20721     );
  20722 
  20723     return sema.analyzeDeclRef(new_decl);
  20724 }
  20725 
  20726 fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20727     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  20728     const src = inst_data.src();
  20729     return sema.failWithUseOfAsync(block, src);
  20730 }
  20731 
  20732 fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20733     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  20734     const src = inst_data.src();
  20735     return sema.failWithUseOfAsync(block, src);
  20736 }
  20737 
  20738 fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20739     const mod = sema.mod;
  20740     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  20741     const src = inst_data.src();
  20742     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  20743     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20744     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat");
  20745     const operand = try sema.resolveInst(extra.rhs);
  20746     const operand_ty = sema.typeOf(operand);
  20747 
  20748     _ = try sema.checkIntType(block, src, dest_ty);
  20749     try sema.checkFloatType(block, operand_src, operand_ty);
  20750 
  20751     if (try sema.resolveMaybeUndefVal(operand)) |val| {
  20752         const result_val = try sema.intFromFloat(block, operand_src, val, operand_ty, dest_ty);
  20753         return sema.addConstant(result_val);
  20754     } else if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
  20755         return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_int' must be comptime-known");
  20756     }
  20757 
  20758     try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
  20759     if (dest_ty.intInfo(mod).bits == 0) {
  20760         if (block.wantSafety()) {
  20761             const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, try sema.addConstant(try mod.floatValue(operand_ty, 0.0)));
  20762             try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds);
  20763         }
  20764         return sema.addConstant(try mod.intValue(dest_ty, 0));
  20765     }
  20766     const result = try block.addTyOp(if (block.float_mode == .Optimized) .int_from_float_optimized else .int_from_float, dest_ty, operand);
  20767     if (block.wantSafety()) {
  20768         const back = try block.addTyOp(.float_from_int, operand_ty, result);
  20769         const diff = try block.addBinOp(.sub, operand, back);
  20770         const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, try sema.addConstant(try mod.floatValue(operand_ty, 1.0)));
  20771         const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, try sema.addConstant(try mod.floatValue(operand_ty, -1.0)));
  20772         const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg);
  20773         try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds);
  20774     }
  20775     return result;
  20776 }
  20777 
  20778 fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20779     const mod = sema.mod;
  20780     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  20781     const src = inst_data.src();
  20782     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  20783     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20784     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt");
  20785     const operand = try sema.resolveInst(extra.rhs);
  20786     const operand_ty = sema.typeOf(operand);
  20787 
  20788     try sema.checkFloatType(block, src, dest_ty);
  20789     _ = try sema.checkIntType(block, operand_src, operand_ty);
  20790 
  20791     if (try sema.resolveMaybeUndefVal(operand)) |val| {
  20792         const result_val = try val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, sema.mod, sema);
  20793         return sema.addConstant(result_val);
  20794     } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) {
  20795         return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_float' must be comptime-known");
  20796     }
  20797 
  20798     try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
  20799     return block.addTyOp(.float_from_int, dest_ty, operand);
  20800 }
  20801 
  20802 fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20803     const mod = sema.mod;
  20804     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  20805     const src = inst_data.src();
  20806 
  20807     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  20808 
  20809     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20810     const operand_res = try sema.resolveInst(extra.rhs);
  20811     const operand_coerced = try sema.coerce(block, Type.usize, operand_res, operand_src);
  20812 
  20813     const ptr_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt");
  20814     try sema.checkPtrType(block, src, ptr_ty);
  20815     const elem_ty = ptr_ty.elemType2(mod);
  20816     const ptr_align = try ptr_ty.ptrAlignmentAdvanced(mod, sema);
  20817 
  20818     if (ptr_ty.isSlice(mod)) {
  20819         const msg = msg: {
  20820             const msg = try sema.errMsg(block, src, "integer cannot be converted to slice type '{}'", .{ptr_ty.fmt(sema.mod)});
  20821             errdefer msg.destroy(sema.gpa);
  20822             try sema.errNote(block, src, msg, "slice length cannot be inferred from address", .{});
  20823             break :msg msg;
  20824         };
  20825         return sema.failWithOwnedErrorMsg(msg);
  20826     }
  20827 
  20828     if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
  20829         const addr = val.toUnsignedInt(mod);
  20830         if (!ptr_ty.isAllowzeroPtr(mod) and addr == 0)
  20831             return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{ptr_ty.fmt(sema.mod)});
  20832         if (addr != 0 and ptr_align != 0 and addr % ptr_align != 0)
  20833             return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)});
  20834 
  20835         const ptr_val = switch (ptr_ty.zigTypeTag(mod)) {
  20836             .Optional => (try mod.intern(.{ .opt = .{
  20837                 .ty = ptr_ty.toIntern(),
  20838                 .val = if (addr == 0) .none else (try mod.ptrIntValue(ptr_ty.childType(mod), addr)).toIntern(),
  20839             } })).toValue(),
  20840             .Pointer => try mod.ptrIntValue(ptr_ty, addr),
  20841             else => unreachable,
  20842         };
  20843         return sema.addConstant(ptr_val);
  20844     }
  20845 
  20846     try sema.requireRuntimeBlock(block, src, operand_src);
  20847     if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) {
  20848         if (!ptr_ty.isAllowzeroPtr(mod)) {
  20849             const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
  20850             try sema.addSafetyCheck(block, is_non_zero, .cast_to_null);
  20851         }
  20852 
  20853         if (ptr_align > 1) {
  20854             const align_minus_1 = try sema.addConstant(
  20855                 try mod.intValue(Type.usize, ptr_align - 1),
  20856             );
  20857             const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1);
  20858             const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  20859             try sema.addSafetyCheck(block, is_aligned, .incorrect_alignment);
  20860         }
  20861     }
  20862     return block.addBitCast(ptr_ty, operand_coerced);
  20863 }
  20864 
  20865 fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20866     const mod = sema.mod;
  20867     const ip = &mod.intern_pool;
  20868     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  20869     const src = LazySrcLoc.nodeOffset(extra.node);
  20870     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  20871     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@errSetCast");
  20872     const operand = try sema.resolveInst(extra.rhs);
  20873     const operand_ty = sema.typeOf(operand);
  20874     try sema.checkErrorSetType(block, src, dest_ty);
  20875     try sema.checkErrorSetType(block, operand_src, operand_ty);
  20876 
  20877     // operand must be defined since it can be an invalid error value
  20878     const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand);
  20879 
  20880     if (disjoint: {
  20881         // Try avoiding resolving inferred error sets if we can
  20882         if (!dest_ty.isAnyError(mod) and dest_ty.errorSetNames(mod).len == 0) break :disjoint true;
  20883         if (!operand_ty.isAnyError(mod) and operand_ty.errorSetNames(mod).len == 0) break :disjoint true;
  20884         if (dest_ty.isAnyError(mod)) break :disjoint false;
  20885         if (operand_ty.isAnyError(mod)) break :disjoint false;
  20886         for (dest_ty.errorSetNames(mod)) |dest_err_name| {
  20887             if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name))
  20888                 break :disjoint false;
  20889         }
  20890 
  20891         if (!ip.isInferredErrorSetType(dest_ty.toIntern()) and
  20892             !ip.isInferredErrorSetType(operand_ty.toIntern()))
  20893         {
  20894             break :disjoint true;
  20895         }
  20896 
  20897         try sema.resolveInferredErrorSetTy(block, src, dest_ty);
  20898         try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty);
  20899         for (dest_ty.errorSetNames(mod)) |dest_err_name| {
  20900             if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name))
  20901                 break :disjoint false;
  20902         }
  20903 
  20904         break :disjoint true;
  20905     }) {
  20906         const msg = msg: {
  20907             const msg = try sema.errMsg(
  20908                 block,
  20909                 src,
  20910                 "error sets '{}' and '{}' have no common errors",
  20911                 .{ operand_ty.fmt(sema.mod), dest_ty.fmt(sema.mod) },
  20912             );
  20913             errdefer msg.destroy(sema.gpa);
  20914             try sema.addDeclaredHereNote(msg, operand_ty);
  20915             try sema.addDeclaredHereNote(msg, dest_ty);
  20916             break :msg msg;
  20917         };
  20918         return sema.failWithOwnedErrorMsg(msg);
  20919     }
  20920 
  20921     if (maybe_operand_val) |val| {
  20922         if (!dest_ty.isAnyError(mod)) {
  20923             const error_name = mod.intern_pool.indexToKey(val.toIntern()).err.name;
  20924             if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) {
  20925                 const msg = msg: {
  20926                     const msg = try sema.errMsg(
  20927                         block,
  20928                         src,
  20929                         "'error.{}' not a member of error set '{}'",
  20930                         .{ error_name.fmt(ip), dest_ty.fmt(sema.mod) },
  20931                     );
  20932                     errdefer msg.destroy(sema.gpa);
  20933                     try sema.addDeclaredHereNote(msg, dest_ty);
  20934                     break :msg msg;
  20935                 };
  20936                 return sema.failWithOwnedErrorMsg(msg);
  20937             }
  20938         }
  20939 
  20940         return sema.addConstant(try mod.getCoerced(val, dest_ty));
  20941     }
  20942 
  20943     try sema.requireRuntimeBlock(block, src, operand_src);
  20944     if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) {
  20945         const err_int_inst = try block.addBitCast(Type.err_int, operand);
  20946         const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
  20947         try sema.addSafetyCheck(block, ok, .invalid_error_code);
  20948     }
  20949     return block.addBitCast(dest_ty, operand);
  20950 }
  20951 
  20952 fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20953     const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(u5, @truncate(extended.small)));
  20954     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  20955     const src = LazySrcLoc.nodeOffset(extra.node);
  20956     const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node };
  20957     const operand = try sema.resolveInst(extra.rhs);
  20958     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName());
  20959     return sema.ptrCastFull(
  20960         block,
  20961         flags,
  20962         src,
  20963         operand,
  20964         operand_src,
  20965         dest_ty,
  20966     );
  20967 }
  20968 
  20969 fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20970     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  20971     const src = inst_data.src();
  20972     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20973     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  20974     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, "@ptrCast");
  20975     const operand = try sema.resolveInst(extra.rhs);
  20976 
  20977     return sema.ptrCastFull(
  20978         block,
  20979         .{ .ptr_cast = true },
  20980         src,
  20981         operand,
  20982         operand_src,
  20983         dest_ty,
  20984     );
  20985 }
  20986 
  20987 fn ptrCastFull(
  20988     sema: *Sema,
  20989     block: *Block,
  20990     flags: Zir.Inst.FullPtrCastFlags,
  20991     src: LazySrcLoc,
  20992     operand: Air.Inst.Ref,
  20993     operand_src: LazySrcLoc,
  20994     dest_ty: Type,
  20995 ) CompileError!Air.Inst.Ref {
  20996     const mod = sema.mod;
  20997     const operand_ty = sema.typeOf(operand);
  20998 
  20999     try sema.checkPtrType(block, src, dest_ty);
  21000     try sema.checkPtrOperand(block, operand_src, operand_ty);
  21001 
  21002     const src_info = operand_ty.ptrInfo(mod);
  21003     const dest_info = dest_ty.ptrInfo(mod);
  21004 
  21005     try sema.resolveTypeLayout(src_info.child.toType());
  21006     try sema.resolveTypeLayout(dest_info.child.toType());
  21007 
  21008     const src_slice_like = src_info.flags.size == .Slice or
  21009         (src_info.flags.size == .One and src_info.child.toType().zigTypeTag(mod) == .Array);
  21010 
  21011     const dest_slice_like = dest_info.flags.size == .Slice or
  21012         (dest_info.flags.size == .One and dest_info.child.toType().zigTypeTag(mod) == .Array);
  21013 
  21014     if (dest_info.flags.size == .Slice and !src_slice_like) {
  21015         return sema.fail(block, src, "illegal pointer cast to slice", .{});
  21016     }
  21017 
  21018     if (dest_info.flags.size == .Slice) {
  21019         const src_elem_size = switch (src_info.flags.size) {
  21020             .Slice => src_info.child.toType().abiSize(mod),
  21021             // pointer to array
  21022             .One => src_info.child.toType().childType(mod).abiSize(mod),
  21023             else => unreachable,
  21024         };
  21025         const dest_elem_size = dest_info.child.toType().abiSize(mod);
  21026         if (src_elem_size != dest_elem_size) {
  21027             return sema.fail(block, src, "TODO: implement @ptrCast between slices changing the length", .{});
  21028         }
  21029     }
  21030 
  21031     // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs
  21032 
  21033     if (!flags.ptr_cast) {
  21034         check_size: {
  21035             if (src_info.flags.size == dest_info.flags.size) break :check_size;
  21036             if (src_slice_like and dest_slice_like) break :check_size;
  21037             if (src_info.flags.size == .C) break :check_size;
  21038             if (dest_info.flags.size == .C) break :check_size;
  21039             return sema.failWithOwnedErrorMsg(msg: {
  21040                 const msg = try sema.errMsg(block, src, "cannot implicitly convert {s} pointer to {s} pointer", .{
  21041                     pointerSizeString(src_info.flags.size),
  21042                     pointerSizeString(dest_info.flags.size),
  21043                 });
  21044                 errdefer msg.destroy(sema.gpa);
  21045                 if (dest_info.flags.size == .Many and
  21046                     (src_info.flags.size == .Slice or
  21047                     (src_info.flags.size == .One and src_info.child.toType().zigTypeTag(mod) == .Array)))
  21048                 {
  21049                     try sema.errNote(block, src, msg, "use 'ptr' field to convert slice to many pointer", .{});
  21050                 } else {
  21051                     try sema.errNote(block, src, msg, "use @ptrCast to change pointer size", .{});
  21052                 }
  21053                 break :msg msg;
  21054             });
  21055         }
  21056 
  21057         check_child: {
  21058             const src_child = if (dest_info.flags.size == .Slice and src_info.flags.size == .One) blk: {
  21059                 // *[n]T -> []T
  21060                 break :blk src_info.child.toType().childType(mod);
  21061             } else src_info.child.toType();
  21062 
  21063             const dest_child = dest_info.child.toType();
  21064 
  21065             const imc_res = try sema.coerceInMemoryAllowed(
  21066                 block,
  21067                 dest_child,
  21068                 src_child,
  21069                 !dest_info.flags.is_const,
  21070                 mod.getTarget(),
  21071                 src,
  21072                 operand_src,
  21073             );
  21074             if (imc_res == .ok) break :check_child;
  21075             return sema.failWithOwnedErrorMsg(msg: {
  21076                 const msg = try sema.errMsg(block, src, "pointer element type '{}' cannot coerce into element type '{}'", .{
  21077                     src_child.fmt(mod),
  21078                     dest_child.fmt(mod),
  21079                 });
  21080                 errdefer msg.destroy(sema.gpa);
  21081                 try imc_res.report(sema, block, src, msg);
  21082                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer element type", .{});
  21083                 break :msg msg;
  21084             });
  21085         }
  21086 
  21087         check_sent: {
  21088             if (dest_info.sentinel == .none) break :check_sent;
  21089             if (src_info.flags.size == .C) break :check_sent;
  21090             if (src_info.sentinel != .none) {
  21091                 const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child);
  21092                 if (dest_info.sentinel == coerced_sent) break :check_sent;
  21093             }
  21094             if (src_slice_like and src_info.flags.size == .One and dest_info.flags.size == .Slice) {
  21095                 // [*]nT -> []T
  21096                 const arr_ty = src_info.child.toType();
  21097                 if (arr_ty.sentinel(mod)) |src_sentinel| {
  21098                     const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_sentinel.toIntern(), dest_info.child);
  21099                     if (dest_info.sentinel == coerced_sent) break :check_sent;
  21100                 }
  21101             }
  21102             return sema.failWithOwnedErrorMsg(msg: {
  21103                 const msg = if (src_info.sentinel == .none) blk: {
  21104                     break :blk try sema.errMsg(block, src, "destination pointer requires '{}' sentinel", .{
  21105                         dest_info.sentinel.toValue().fmtValue(dest_info.child.toType(), mod),
  21106                     });
  21107                 } else blk: {
  21108                     break :blk try sema.errMsg(block, src, "pointer sentinel '{}' cannot coerce into pointer sentinel '{}'", .{
  21109                         src_info.sentinel.toValue().fmtValue(src_info.child.toType(), mod),
  21110                         dest_info.sentinel.toValue().fmtValue(dest_info.child.toType(), mod),
  21111                     });
  21112                 };
  21113                 errdefer msg.destroy(sema.gpa);
  21114                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer sentinel", .{});
  21115                 break :msg msg;
  21116             });
  21117         }
  21118 
  21119         if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) {
  21120             return sema.failWithOwnedErrorMsg(msg: {
  21121                 const msg = try sema.errMsg(block, src, "pointer host size '{}' cannot coerce into pointer host size '{}'", .{
  21122                     src_info.packed_offset.host_size,
  21123                     dest_info.packed_offset.host_size,
  21124                 });
  21125                 errdefer msg.destroy(sema.gpa);
  21126                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer host size", .{});
  21127                 break :msg msg;
  21128             });
  21129         }
  21130 
  21131         if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) {
  21132             return sema.failWithOwnedErrorMsg(msg: {
  21133                 const msg = try sema.errMsg(block, src, "pointer bit offset '{}' cannot coerce into pointer bit offset '{}'", .{
  21134                     src_info.packed_offset.bit_offset,
  21135                     dest_info.packed_offset.bit_offset,
  21136                 });
  21137                 errdefer msg.destroy(sema.gpa);
  21138                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer bit offset", .{});
  21139                 break :msg msg;
  21140             });
  21141         }
  21142 
  21143         check_allowzero: {
  21144             const src_allows_zero = operand_ty.ptrAllowsZero(mod);
  21145             const dest_allows_zero = dest_ty.ptrAllowsZero(mod);
  21146             if (!src_allows_zero) break :check_allowzero;
  21147             if (dest_allows_zero) break :check_allowzero;
  21148 
  21149             return sema.failWithOwnedErrorMsg(msg: {
  21150                 const msg = try sema.errMsg(block, src, "'{}' could have null values which are illegal in type '{}'", .{
  21151                     operand_ty.fmt(mod),
  21152                     dest_ty.fmt(mod),
  21153                 });
  21154                 errdefer msg.destroy(sema.gpa);
  21155                 try sema.errNote(block, src, msg, "use @ptrCast to assert the pointer is not null", .{});
  21156                 break :msg msg;
  21157             });
  21158         }
  21159 
  21160         // TODO: vector index?
  21161     }
  21162 
  21163     const src_align = src_info.flags.alignment.toByteUnitsOptional() orelse src_info.child.toType().abiAlignment(mod);
  21164     const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse dest_info.child.toType().abiAlignment(mod);
  21165     if (!flags.align_cast) {
  21166         if (dest_align > src_align) {
  21167             return sema.failWithOwnedErrorMsg(msg: {
  21168                 const msg = try sema.errMsg(block, src, "cast increases pointer alignment", .{});
  21169                 errdefer msg.destroy(sema.gpa);
  21170                 try sema.errNote(block, operand_src, msg, "'{}' has alignment '{d}'", .{
  21171                     operand_ty.fmt(mod), src_align,
  21172                 });
  21173                 try sema.errNote(block, src, msg, "'{}' has alignment '{d}'", .{
  21174                     dest_ty.fmt(mod), dest_align,
  21175                 });
  21176                 try sema.errNote(block, src, msg, "use @alignCast to assert pointer alignment", .{});
  21177                 break :msg msg;
  21178             });
  21179         }
  21180     }
  21181 
  21182     if (!flags.addrspace_cast) {
  21183         if (src_info.flags.address_space != dest_info.flags.address_space) {
  21184             return sema.failWithOwnedErrorMsg(msg: {
  21185                 const msg = try sema.errMsg(block, src, "cast changes pointer address space", .{});
  21186                 errdefer msg.destroy(sema.gpa);
  21187                 try sema.errNote(block, operand_src, msg, "'{}' has address space '{s}'", .{
  21188                     operand_ty.fmt(mod), @tagName(src_info.flags.address_space),
  21189                 });
  21190                 try sema.errNote(block, src, msg, "'{}' has address space '{s}'", .{
  21191                     dest_ty.fmt(mod), @tagName(dest_info.flags.address_space),
  21192                 });
  21193                 try sema.errNote(block, src, msg, "use @addrSpaceCast to cast pointer address space", .{});
  21194                 break :msg msg;
  21195             });
  21196         }
  21197     } else {
  21198         // Some address space casts are always disallowed
  21199         if (!target_util.addrSpaceCastIsValid(mod.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) {
  21200             return sema.failWithOwnedErrorMsg(msg: {
  21201                 const msg = try sema.errMsg(block, src, "invalid address space cast", .{});
  21202                 errdefer msg.destroy(sema.gpa);
  21203                 try sema.errNote(block, operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{
  21204                     @tagName(src_info.flags.address_space),
  21205                     @tagName(dest_info.flags.address_space),
  21206                 });
  21207                 break :msg msg;
  21208             });
  21209         }
  21210     }
  21211 
  21212     if (!flags.const_cast) {
  21213         if (src_info.flags.is_const and !dest_info.flags.is_const) {
  21214             return sema.failWithOwnedErrorMsg(msg: {
  21215                 const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{});
  21216                 errdefer msg.destroy(sema.gpa);
  21217                 try sema.errNote(block, src, msg, "use @constCast to discard const qualifier", .{});
  21218                 break :msg msg;
  21219             });
  21220         }
  21221     }
  21222 
  21223     if (!flags.volatile_cast) {
  21224         if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) {
  21225             return sema.failWithOwnedErrorMsg(msg: {
  21226                 const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{});
  21227                 errdefer msg.destroy(sema.gpa);
  21228                 try sema.errNote(block, src, msg, "use @volatileCast to discard volatile qualifier", .{});
  21229                 break :msg msg;
  21230             });
  21231         }
  21232     }
  21233 
  21234     const ptr = if (src_info.flags.size == .Slice and dest_info.flags.size != .Slice) ptr: {
  21235         break :ptr try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty);
  21236     } else operand;
  21237 
  21238     const dest_ptr_ty = if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) blk: {
  21239         // Only convert to a many-pointer at first
  21240         var info = dest_info;
  21241         info.flags.size = .Many;
  21242         const ty = try mod.ptrType(info);
  21243         if (dest_ty.zigTypeTag(mod) == .Optional) {
  21244             break :blk try mod.optionalType(ty.toIntern());
  21245         } else {
  21246             break :blk ty;
  21247         }
  21248     } else dest_ty;
  21249 
  21250     // Cannot do @addrSpaceCast at comptime
  21251     if (!flags.addrspace_cast) {
  21252         if (try sema.resolveMaybeUndefVal(ptr)) |ptr_val| {
  21253             if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isUndef(mod)) {
  21254                 return sema.failWithUseOfUndef(block, operand_src);
  21255             }
  21256             if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isNull(mod)) {
  21257                 return sema.fail(block, operand_src, "null pointer casted to type '{}'", .{dest_ty.fmt(mod)});
  21258             }
  21259             if (dest_align > src_align) {
  21260                 if (try ptr_val.getUnsignedIntAdvanced(mod, null)) |addr| {
  21261                     if (addr % dest_align != 0) {
  21262                         return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ addr, dest_align });
  21263                     }
  21264                 }
  21265             }
  21266             if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) {
  21267                 if (ptr_val.isUndef(mod)) return sema.addConstUndef(dest_ty);
  21268                 const arr_len = try mod.intValue(Type.usize, src_info.child.toType().arrayLen(mod));
  21269                 return sema.addConstant((try mod.intern(.{ .ptr = .{
  21270                     .ty = dest_ty.toIntern(),
  21271                     .addr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr,
  21272                     .len = arr_len.toIntern(),
  21273                 } })).toValue());
  21274             } else {
  21275                 assert(dest_ptr_ty.eql(dest_ty, mod));
  21276                 return sema.addConstant(try mod.getCoerced(ptr_val, dest_ty));
  21277             }
  21278         }
  21279     }
  21280 
  21281     try sema.requireRuntimeBlock(block, src, null);
  21282 
  21283     if (block.wantSafety() and operand_ty.ptrAllowsZero(mod) and !dest_ty.ptrAllowsZero(mod) and
  21284         (try sema.typeHasRuntimeBits(dest_info.child.toType()) or dest_info.child.toType().zigTypeTag(mod) == .Fn))
  21285     {
  21286         const ptr_int = try block.addUnOp(.int_from_ptr, ptr);
  21287         const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
  21288         const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: {
  21289             const len = try sema.analyzeSliceLen(block, operand_src, ptr);
  21290             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
  21291             break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero);
  21292         } else is_non_zero;
  21293         try sema.addSafetyCheck(block, ok, .cast_to_null);
  21294     }
  21295 
  21296     if (block.wantSafety() and dest_align > src_align and try sema.typeHasRuntimeBits(dest_info.child.toType())) {
  21297         const align_minus_1 = try sema.addConstant(
  21298             try mod.intValue(Type.usize, dest_align - 1),
  21299         );
  21300         const ptr_int = try block.addUnOp(.int_from_ptr, ptr);
  21301         const remainder = try block.addBinOp(.bit_and, ptr_int, align_minus_1);
  21302         const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  21303         const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: {
  21304             const len = try sema.analyzeSliceLen(block, operand_src, ptr);
  21305             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
  21306             break :ok try block.addBinOp(.bit_or, len_zero, is_aligned);
  21307         } else is_aligned;
  21308         try sema.addSafetyCheck(block, ok, .incorrect_alignment);
  21309     }
  21310 
  21311     // If we're going from an array pointer to a slice, this will only be the pointer part!
  21312     const result_ptr = if (flags.addrspace_cast) ptr: {
  21313         // We can't change address spaces with a bitcast, so this requires two instructions
  21314         var intermediate_info = src_info;
  21315         intermediate_info.flags.address_space = dest_info.flags.address_space;
  21316         const intermediate_ptr_ty = try mod.ptrType(intermediate_info);
  21317         const intermediate_ty = if (dest_ptr_ty.zigTypeTag(mod) == .Optional) blk: {
  21318             break :blk try mod.optionalType(intermediate_ptr_ty.toIntern());
  21319         } else intermediate_ptr_ty;
  21320         const intermediate = try block.addInst(.{
  21321             .tag = .addrspace_cast,
  21322             .data = .{ .ty_op = .{
  21323                 .ty = try sema.addType(intermediate_ty),
  21324                 .operand = ptr,
  21325             } },
  21326         });
  21327         if (intermediate_ty.eql(dest_ptr_ty, mod)) {
  21328             // We only changed the address space, so no need for a bitcast
  21329             break :ptr intermediate;
  21330         }
  21331         break :ptr try block.addBitCast(dest_ptr_ty, intermediate);
  21332     } else ptr: {
  21333         break :ptr try block.addBitCast(dest_ptr_ty, ptr);
  21334     };
  21335 
  21336     if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) {
  21337         // We have to construct a slice using the operand's child's array length
  21338         // Note that we know from the check at the start of the function that operand_ty is slice-like
  21339         const arr_len = try sema.addConstant(
  21340             try mod.intValue(Type.usize, src_info.child.toType().arrayLen(mod)),
  21341         );
  21342         return block.addInst(.{
  21343             .tag = .slice,
  21344             .data = .{ .ty_pl = .{
  21345                 .ty = try sema.addType(dest_ty),
  21346                 .payload = try sema.addExtra(Air.Bin{
  21347                     .lhs = result_ptr,
  21348                     .rhs = arr_len,
  21349                 }),
  21350             } },
  21351         });
  21352     } else {
  21353         assert(dest_ptr_ty.eql(dest_ty, mod));
  21354         return result_ptr;
  21355     }
  21356 }
  21357 
  21358 fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21359     const mod = sema.mod;
  21360     const flags = @as(Zir.Inst.FullPtrCastFlags, @bitCast(@as(u5, @truncate(extended.small))));
  21361     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  21362     const src = LazySrcLoc.nodeOffset(extra.node);
  21363     const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node };
  21364     const operand = try sema.resolveInst(extra.operand);
  21365     const operand_ty = sema.typeOf(operand);
  21366     try sema.checkPtrOperand(block, operand_src, operand_ty);
  21367 
  21368     var ptr_info = operand_ty.ptrInfo(mod);
  21369     if (flags.const_cast) ptr_info.flags.is_const = false;
  21370     if (flags.volatile_cast) ptr_info.flags.is_volatile = false;
  21371     const dest_ty = try mod.ptrType(ptr_info);
  21372 
  21373     if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
  21374         return sema.addConstant(try mod.getCoerced(operand_val, dest_ty));
  21375     }
  21376 
  21377     try sema.requireRuntimeBlock(block, src, null);
  21378     return block.addBitCast(dest_ty, operand);
  21379 }
  21380 
  21381 fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21382     const mod = sema.mod;
  21383     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  21384     const src = inst_data.src();
  21385     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21386     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21387     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate");
  21388     const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src);
  21389     const operand = try sema.resolveInst(extra.rhs);
  21390     const operand_ty = sema.typeOf(operand);
  21391     const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
  21392 
  21393     const operand_is_vector = operand_ty.zigTypeTag(mod) == .Vector;
  21394     const dest_is_vector = dest_ty.zigTypeTag(mod) == .Vector;
  21395     if (operand_is_vector != dest_is_vector) {
  21396         return sema.fail(block, operand_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), operand_ty.fmt(mod) });
  21397     }
  21398 
  21399     if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
  21400         return sema.coerce(block, dest_ty, operand, operand_src);
  21401     }
  21402 
  21403     const dest_info = dest_scalar_ty.intInfo(mod);
  21404 
  21405     if (try sema.typeHasOnePossibleValue(dest_ty)) |val| {
  21406         return sema.addConstant(val);
  21407     }
  21408 
  21409     if (operand_scalar_ty.zigTypeTag(mod) != .ComptimeInt) {
  21410         const operand_info = operand_ty.intInfo(mod);
  21411         if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  21412             return sema.addConstant(val);
  21413         }
  21414 
  21415         if (operand_info.signedness != dest_info.signedness) {
  21416             return sema.fail(block, operand_src, "expected {s} integer type, found '{}'", .{
  21417                 @tagName(dest_info.signedness), operand_ty.fmt(mod),
  21418             });
  21419         }
  21420         if (operand_info.bits < dest_info.bits) {
  21421             const msg = msg: {
  21422                 const msg = try sema.errMsg(
  21423                     block,
  21424                     src,
  21425                     "destination type '{}' has more bits than source type '{}'",
  21426                     .{ dest_ty.fmt(mod), operand_ty.fmt(mod) },
  21427                 );
  21428                 errdefer msg.destroy(sema.gpa);
  21429                 try sema.errNote(block, src, msg, "destination type has {d} bits", .{
  21430                     dest_info.bits,
  21431                 });
  21432                 try sema.errNote(block, operand_src, msg, "operand type has {d} bits", .{
  21433                     operand_info.bits,
  21434                 });
  21435                 break :msg msg;
  21436             };
  21437             return sema.failWithOwnedErrorMsg(msg);
  21438         }
  21439     }
  21440 
  21441     if (try sema.resolveMaybeUndefValIntable(operand)) |val| {
  21442         if (val.isUndef(mod)) return sema.addConstUndef(dest_ty);
  21443         if (!dest_is_vector) {
  21444             return sema.addConstant(try mod.getCoerced(
  21445                 try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, mod),
  21446                 dest_ty,
  21447             ));
  21448         }
  21449         const elems = try sema.arena.alloc(InternPool.Index, operand_ty.vectorLen(mod));
  21450         for (elems, 0..) |*elem, i| {
  21451             const elem_val = try val.elemValue(mod, i);
  21452             elem.* = try (try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, mod)).intern(dest_scalar_ty, mod);
  21453         }
  21454         return sema.addConstant((try mod.intern(.{ .aggregate = .{
  21455             .ty = dest_ty.toIntern(),
  21456             .storage = .{ .elems = elems },
  21457         } })).toValue());
  21458     }
  21459 
  21460     try sema.requireRuntimeBlock(block, src, operand_src);
  21461     return block.addTyOp(.trunc, dest_ty, operand);
  21462 }
  21463 
  21464 fn zirBitCount(
  21465     sema: *Sema,
  21466     block: *Block,
  21467     inst: Zir.Inst.Index,
  21468     air_tag: Air.Inst.Tag,
  21469     comptime comptimeOp: fn (val: Value, ty: Type, mod: *Module) u64,
  21470 ) CompileError!Air.Inst.Ref {
  21471     const mod = sema.mod;
  21472     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  21473     const src = inst_data.src();
  21474     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21475     const operand = try sema.resolveInst(inst_data.operand);
  21476     const operand_ty = sema.typeOf(operand);
  21477     _ = try sema.checkIntOrVector(block, operand, operand_src);
  21478     const bits = operand_ty.intInfo(mod).bits;
  21479 
  21480     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  21481         return sema.addConstant(val);
  21482     }
  21483 
  21484     const result_scalar_ty = try mod.smallestUnsignedInt(bits);
  21485     switch (operand_ty.zigTypeTag(mod)) {
  21486         .Vector => {
  21487             const vec_len = operand_ty.vectorLen(mod);
  21488             const result_ty = try mod.vectorType(.{
  21489                 .len = vec_len,
  21490                 .child = result_scalar_ty.toIntern(),
  21491             });
  21492             if (try sema.resolveMaybeUndefVal(operand)) |val| {
  21493                 if (val.isUndef(mod)) return sema.addConstUndef(result_ty);
  21494 
  21495                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  21496                 const scalar_ty = operand_ty.scalarType(mod);
  21497                 for (elems, 0..) |*elem, i| {
  21498                     const elem_val = try val.elemValue(mod, i);
  21499                     const count = comptimeOp(elem_val, scalar_ty, mod);
  21500                     elem.* = (try mod.intValue(result_scalar_ty, count)).toIntern();
  21501                 }
  21502                 return sema.addConstant((try mod.intern(.{ .aggregate = .{
  21503                     .ty = result_ty.toIntern(),
  21504                     .storage = .{ .elems = elems },
  21505                 } })).toValue());
  21506             } else {
  21507                 try sema.requireRuntimeBlock(block, src, operand_src);
  21508                 return block.addTyOp(air_tag, result_ty, operand);
  21509             }
  21510         },
  21511         .Int => {
  21512             if (try sema.resolveMaybeUndefLazyVal(operand)) |val| {
  21513                 if (val.isUndef(mod)) return sema.addConstUndef(result_scalar_ty);
  21514                 return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, mod));
  21515             } else {
  21516                 try sema.requireRuntimeBlock(block, src, operand_src);
  21517                 return block.addTyOp(air_tag, result_scalar_ty, operand);
  21518             }
  21519         },
  21520         else => unreachable,
  21521     }
  21522 }
  21523 
  21524 fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21525     const mod = sema.mod;
  21526     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  21527     const src = inst_data.src();
  21528     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21529     const operand = try sema.resolveInst(inst_data.operand);
  21530     const operand_ty = sema.typeOf(operand);
  21531     const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src);
  21532     const bits = scalar_ty.intInfo(mod).bits;
  21533     if (bits % 8 != 0) {
  21534         return sema.fail(
  21535             block,
  21536             operand_src,
  21537             "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits",
  21538             .{ scalar_ty.fmt(mod), bits },
  21539         );
  21540     }
  21541 
  21542     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  21543         return sema.addConstant(val);
  21544     }
  21545 
  21546     switch (operand_ty.zigTypeTag(mod)) {
  21547         .Int => {
  21548             const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| {
  21549                 if (val.isUndef(mod)) return sema.addConstUndef(operand_ty);
  21550                 const result_val = try val.byteSwap(operand_ty, mod, sema.arena);
  21551                 return sema.addConstant(result_val);
  21552             } else operand_src;
  21553 
  21554             try sema.requireRuntimeBlock(block, src, runtime_src);
  21555             return block.addTyOp(.byte_swap, operand_ty, operand);
  21556         },
  21557         .Vector => {
  21558             const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| {
  21559                 if (val.isUndef(mod))
  21560                     return sema.addConstUndef(operand_ty);
  21561 
  21562                 const vec_len = operand_ty.vectorLen(mod);
  21563                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  21564                 for (elems, 0..) |*elem, i| {
  21565                     const elem_val = try val.elemValue(mod, i);
  21566                     elem.* = try (try elem_val.byteSwap(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod);
  21567                 }
  21568                 return sema.addConstant((try mod.intern(.{ .aggregate = .{
  21569                     .ty = operand_ty.toIntern(),
  21570                     .storage = .{ .elems = elems },
  21571                 } })).toValue());
  21572             } else operand_src;
  21573 
  21574             try sema.requireRuntimeBlock(block, src, runtime_src);
  21575             return block.addTyOp(.byte_swap, operand_ty, operand);
  21576         },
  21577         else => unreachable,
  21578     }
  21579 }
  21580 
  21581 fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21582     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  21583     const src = inst_data.src();
  21584     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21585     const operand = try sema.resolveInst(inst_data.operand);
  21586     const operand_ty = sema.typeOf(operand);
  21587     const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src);
  21588 
  21589     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  21590         return sema.addConstant(val);
  21591     }
  21592 
  21593     const mod = sema.mod;
  21594     switch (operand_ty.zigTypeTag(mod)) {
  21595         .Int => {
  21596             const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| {
  21597                 if (val.isUndef(mod)) return sema.addConstUndef(operand_ty);
  21598                 const result_val = try val.bitReverse(operand_ty, mod, sema.arena);
  21599                 return sema.addConstant(result_val);
  21600             } else operand_src;
  21601 
  21602             try sema.requireRuntimeBlock(block, src, runtime_src);
  21603             return block.addTyOp(.bit_reverse, operand_ty, operand);
  21604         },
  21605         .Vector => {
  21606             const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| {
  21607                 if (val.isUndef(mod))
  21608                     return sema.addConstUndef(operand_ty);
  21609 
  21610                 const vec_len = operand_ty.vectorLen(mod);
  21611                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  21612                 for (elems, 0..) |*elem, i| {
  21613                     const elem_val = try val.elemValue(mod, i);
  21614                     elem.* = try (try elem_val.bitReverse(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod);
  21615                 }
  21616                 return sema.addConstant((try mod.intern(.{ .aggregate = .{
  21617                     .ty = operand_ty.toIntern(),
  21618                     .storage = .{ .elems = elems },
  21619                 } })).toValue());
  21620             } else operand_src;
  21621 
  21622             try sema.requireRuntimeBlock(block, src, runtime_src);
  21623             return block.addTyOp(.bit_reverse, operand_ty, operand);
  21624         },
  21625         else => unreachable,
  21626     }
  21627 }
  21628 
  21629 fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21630     const offset = try sema.bitOffsetOf(block, inst);
  21631     return sema.addIntUnsigned(Type.comptime_int, offset);
  21632 }
  21633 
  21634 fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21635     const offset = try sema.bitOffsetOf(block, inst);
  21636     // TODO reminder to make this a compile error for packed structs
  21637     return sema.addIntUnsigned(Type.comptime_int, offset / 8);
  21638 }
  21639 
  21640 fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 {
  21641     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  21642     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  21643     sema.src = src;
  21644     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  21645     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  21646     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21647 
  21648     const ty = try sema.resolveType(block, lhs_src, extra.lhs);
  21649     const field_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, "name of field must be comptime-known");
  21650 
  21651     const mod = sema.mod;
  21652     try sema.resolveTypeLayout(ty);
  21653     switch (ty.zigTypeTag(mod)) {
  21654         .Struct => {},
  21655         else => {
  21656             const msg = msg: {
  21657                 const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(mod)});
  21658                 errdefer msg.destroy(sema.gpa);
  21659                 try sema.addDeclaredHereNote(msg, ty);
  21660                 break :msg msg;
  21661             };
  21662             return sema.failWithOwnedErrorMsg(msg);
  21663         },
  21664     }
  21665 
  21666     const field_index = if (ty.isTuple(mod)) blk: {
  21667         if (mod.intern_pool.stringEqlSlice(field_name, "len")) {
  21668             return sema.fail(block, src, "no offset available for 'len' field of tuple", .{});
  21669         }
  21670         break :blk try sema.tupleFieldIndex(block, ty, field_name, rhs_src);
  21671     } else try sema.structFieldIndex(block, ty, field_name, rhs_src);
  21672 
  21673     if (ty.structFieldIsComptime(field_index, mod)) {
  21674         return sema.fail(block, src, "no offset available for comptime field", .{});
  21675     }
  21676 
  21677     switch (ty.containerLayout(mod)) {
  21678         .Packed => {
  21679             var bit_sum: u64 = 0;
  21680             const fields = ty.structFields(mod);
  21681             for (fields.values(), 0..) |field, i| {
  21682                 if (i == field_index) {
  21683                     return bit_sum;
  21684                 }
  21685                 bit_sum += field.ty.bitSize(mod);
  21686             } else unreachable;
  21687         },
  21688         else => return ty.structFieldOffset(field_index, mod) * 8,
  21689     }
  21690 }
  21691 
  21692 fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
  21693     const mod = sema.mod;
  21694     switch (ty.zigTypeTag(mod)) {
  21695         .Struct, .Enum, .Union, .Opaque => return,
  21696         else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(mod)}),
  21697     }
  21698 }
  21699 
  21700 /// Returns `true` if the type was a comptime_int.
  21701 fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
  21702     const mod = sema.mod;
  21703     switch (try ty.zigTypeTagOrPoison(mod)) {
  21704         .ComptimeInt => return true,
  21705         .Int => return false,
  21706         else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(mod)}),
  21707     }
  21708 }
  21709 
  21710 fn checkInvalidPtrArithmetic(
  21711     sema: *Sema,
  21712     block: *Block,
  21713     src: LazySrcLoc,
  21714     ty: Type,
  21715 ) CompileError!void {
  21716     const mod = sema.mod;
  21717     switch (try ty.zigTypeTagOrPoison(mod)) {
  21718         .Pointer => switch (ty.ptrSize(mod)) {
  21719             .One, .Slice => return,
  21720             .Many, .C => return sema.fail(
  21721                 block,
  21722                 src,
  21723                 "invalid pointer arithmetic operator",
  21724                 .{},
  21725             ),
  21726         },
  21727         else => return,
  21728     }
  21729 }
  21730 
  21731 fn checkArithmeticOp(
  21732     sema: *Sema,
  21733     block: *Block,
  21734     src: LazySrcLoc,
  21735     scalar_tag: std.builtin.TypeId,
  21736     lhs_zig_ty_tag: std.builtin.TypeId,
  21737     rhs_zig_ty_tag: std.builtin.TypeId,
  21738     zir_tag: Zir.Inst.Tag,
  21739 ) CompileError!void {
  21740     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  21741     const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
  21742 
  21743     if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
  21744         return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{
  21745             @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag),
  21746         });
  21747     }
  21748 }
  21749 
  21750 fn checkPtrOperand(
  21751     sema: *Sema,
  21752     block: *Block,
  21753     ty_src: LazySrcLoc,
  21754     ty: Type,
  21755 ) CompileError!void {
  21756     const mod = sema.mod;
  21757     switch (ty.zigTypeTag(mod)) {
  21758         .Pointer => return,
  21759         .Fn => {
  21760             const msg = msg: {
  21761                 const msg = try sema.errMsg(
  21762                     block,
  21763                     ty_src,
  21764                     "expected pointer, found '{}'",
  21765                     .{ty.fmt(mod)},
  21766                 );
  21767                 errdefer msg.destroy(sema.gpa);
  21768 
  21769                 try sema.errNote(block, ty_src, msg, "use '&' to obtain a function pointer", .{});
  21770 
  21771                 break :msg msg;
  21772             };
  21773             return sema.failWithOwnedErrorMsg(msg);
  21774         },
  21775         .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return,
  21776         else => {},
  21777     }
  21778     return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)});
  21779 }
  21780 
  21781 fn checkPtrType(
  21782     sema: *Sema,
  21783     block: *Block,
  21784     ty_src: LazySrcLoc,
  21785     ty: Type,
  21786 ) CompileError!void {
  21787     const mod = sema.mod;
  21788     switch (ty.zigTypeTag(mod)) {
  21789         .Pointer => return,
  21790         .Fn => {
  21791             const msg = msg: {
  21792                 const msg = try sema.errMsg(
  21793                     block,
  21794                     ty_src,
  21795                     "expected pointer type, found '{}'",
  21796                     .{ty.fmt(mod)},
  21797                 );
  21798                 errdefer msg.destroy(sema.gpa);
  21799 
  21800                 try sema.errNote(block, ty_src, msg, "use '*const ' to make a function pointer type", .{});
  21801 
  21802                 break :msg msg;
  21803             };
  21804             return sema.failWithOwnedErrorMsg(msg);
  21805         },
  21806         .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return,
  21807         else => {},
  21808     }
  21809     return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)});
  21810 }
  21811 
  21812 fn checkVectorElemType(
  21813     sema: *Sema,
  21814     block: *Block,
  21815     ty_src: LazySrcLoc,
  21816     ty: Type,
  21817 ) CompileError!void {
  21818     const mod = sema.mod;
  21819     switch (ty.zigTypeTag(mod)) {
  21820         .Int, .Float, .Bool => return,
  21821         else => if (ty.isPtrAtRuntime(mod)) return,
  21822     }
  21823     return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(mod)});
  21824 }
  21825 
  21826 fn checkFloatType(
  21827     sema: *Sema,
  21828     block: *Block,
  21829     ty_src: LazySrcLoc,
  21830     ty: Type,
  21831 ) CompileError!void {
  21832     const mod = sema.mod;
  21833     switch (ty.zigTypeTag(mod)) {
  21834         .ComptimeInt, .ComptimeFloat, .Float => {},
  21835         else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(mod)}),
  21836     }
  21837 }
  21838 
  21839 fn checkNumericType(
  21840     sema: *Sema,
  21841     block: *Block,
  21842     ty_src: LazySrcLoc,
  21843     ty: Type,
  21844 ) CompileError!void {
  21845     const mod = sema.mod;
  21846     switch (ty.zigTypeTag(mod)) {
  21847         .ComptimeFloat, .Float, .ComptimeInt, .Int => {},
  21848         .Vector => switch (ty.childType(mod).zigTypeTag(mod)) {
  21849             .ComptimeFloat, .Float, .ComptimeInt, .Int => {},
  21850             else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}),
  21851         },
  21852         else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(mod)}),
  21853     }
  21854 }
  21855 
  21856 /// Returns the casted pointer.
  21857 fn checkAtomicPtrOperand(
  21858     sema: *Sema,
  21859     block: *Block,
  21860     elem_ty: Type,
  21861     elem_ty_src: LazySrcLoc,
  21862     ptr: Air.Inst.Ref,
  21863     ptr_src: LazySrcLoc,
  21864     ptr_const: bool,
  21865 ) CompileError!Air.Inst.Ref {
  21866     const mod = sema.mod;
  21867     var diag: Module.AtomicPtrAlignmentDiagnostics = .{};
  21868     const alignment = mod.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) {
  21869         error.OutOfMemory => return error.OutOfMemory,
  21870         error.FloatTooBig => return sema.fail(
  21871             block,
  21872             elem_ty_src,
  21873             "expected {d}-bit float type or smaller; found {d}-bit float type",
  21874             .{ diag.max_bits, diag.bits },
  21875         ),
  21876         error.IntTooBig => return sema.fail(
  21877             block,
  21878             elem_ty_src,
  21879             "expected {d}-bit integer type or smaller; found {d}-bit integer type",
  21880             .{ diag.max_bits, diag.bits },
  21881         ),
  21882         error.BadType => return sema.fail(
  21883             block,
  21884             elem_ty_src,
  21885             "expected bool, integer, float, enum, or pointer type; found '{}'",
  21886             .{elem_ty.fmt(mod)},
  21887         ),
  21888     };
  21889 
  21890     var wanted_ptr_data: InternPool.Key.PtrType = .{
  21891         .child = elem_ty.toIntern(),
  21892         .flags = .{
  21893             .alignment = alignment,
  21894             .is_const = ptr_const,
  21895         },
  21896     };
  21897 
  21898     const ptr_ty = sema.typeOf(ptr);
  21899     const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison(mod)) {
  21900         .Pointer => ptr_ty.ptrInfo(mod),
  21901         else => {
  21902             const wanted_ptr_ty = try mod.ptrType(wanted_ptr_data);
  21903             _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
  21904             unreachable;
  21905         },
  21906     };
  21907 
  21908     wanted_ptr_data.flags.address_space = ptr_data.flags.address_space;
  21909     wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero;
  21910     wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile;
  21911 
  21912     const wanted_ptr_ty = try mod.ptrType(wanted_ptr_data);
  21913     const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
  21914 
  21915     return casted_ptr;
  21916 }
  21917 
  21918 fn checkPtrIsNotComptimeMutable(
  21919     sema: *Sema,
  21920     block: *Block,
  21921     ptr_val: Value,
  21922     ptr_src: LazySrcLoc,
  21923     operand_src: LazySrcLoc,
  21924 ) CompileError!void {
  21925     _ = operand_src;
  21926     if (ptr_val.isComptimeMutablePtr(sema.mod)) {
  21927         return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{});
  21928     }
  21929 }
  21930 
  21931 fn checkComptimeVarStore(
  21932     sema: *Sema,
  21933     block: *Block,
  21934     src: LazySrcLoc,
  21935     decl_ref_mut: InternPool.Key.Ptr.Addr.MutDecl,
  21936 ) CompileError!void {
  21937     if (@intFromEnum(decl_ref_mut.runtime_index) < @intFromEnum(block.runtime_index)) {
  21938         if (block.runtime_cond) |cond_src| {
  21939             const msg = msg: {
  21940                 const msg = try sema.errMsg(block, src, "store to comptime variable depends on runtime condition", .{});
  21941                 errdefer msg.destroy(sema.gpa);
  21942                 try sema.errNote(block, cond_src, msg, "runtime condition here", .{});
  21943                 break :msg msg;
  21944             };
  21945             return sema.failWithOwnedErrorMsg(msg);
  21946         }
  21947         if (block.runtime_loop) |loop_src| {
  21948             const msg = msg: {
  21949                 const msg = try sema.errMsg(block, src, "cannot store to comptime variable in non-inline loop", .{});
  21950                 errdefer msg.destroy(sema.gpa);
  21951                 try sema.errNote(block, loop_src, msg, "non-inline loop here", .{});
  21952                 break :msg msg;
  21953             };
  21954             return sema.failWithOwnedErrorMsg(msg);
  21955         }
  21956         unreachable;
  21957     }
  21958 }
  21959 
  21960 fn checkIntOrVector(
  21961     sema: *Sema,
  21962     block: *Block,
  21963     operand: Air.Inst.Ref,
  21964     operand_src: LazySrcLoc,
  21965 ) CompileError!Type {
  21966     const mod = sema.mod;
  21967     const operand_ty = sema.typeOf(operand);
  21968     switch (try operand_ty.zigTypeTagOrPoison(mod)) {
  21969         .Int => return operand_ty,
  21970         .Vector => {
  21971             const elem_ty = operand_ty.childType(mod);
  21972             switch (try elem_ty.zigTypeTagOrPoison(mod)) {
  21973                 .Int => return elem_ty,
  21974                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{
  21975                     elem_ty.fmt(mod),
  21976                 }),
  21977             }
  21978         },
  21979         else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{
  21980             operand_ty.fmt(mod),
  21981         }),
  21982     }
  21983 }
  21984 
  21985 fn checkIntOrVectorAllowComptime(
  21986     sema: *Sema,
  21987     block: *Block,
  21988     operand_ty: Type,
  21989     operand_src: LazySrcLoc,
  21990 ) CompileError!Type {
  21991     const mod = sema.mod;
  21992     switch (try operand_ty.zigTypeTagOrPoison(mod)) {
  21993         .Int, .ComptimeInt => return operand_ty,
  21994         .Vector => {
  21995             const elem_ty = operand_ty.childType(mod);
  21996             switch (try elem_ty.zigTypeTagOrPoison(mod)) {
  21997                 .Int, .ComptimeInt => return elem_ty,
  21998                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{
  21999                     elem_ty.fmt(mod),
  22000                 }),
  22001             }
  22002         },
  22003         else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{
  22004             operand_ty.fmt(mod),
  22005         }),
  22006     }
  22007 }
  22008 
  22009 fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
  22010     const mod = sema.mod;
  22011     switch (ty.zigTypeTag(mod)) {
  22012         .ErrorSet => return,
  22013         else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(mod)}),
  22014     }
  22015 }
  22016 
  22017 const SimdBinOp = struct {
  22018     len: ?usize,
  22019     /// Coerced to `result_ty`.
  22020     lhs: Air.Inst.Ref,
  22021     /// Coerced to `result_ty`.
  22022     rhs: Air.Inst.Ref,
  22023     lhs_val: ?Value,
  22024     rhs_val: ?Value,
  22025     /// Only different than `scalar_ty` when it is a vector operation.
  22026     result_ty: Type,
  22027     scalar_ty: Type,
  22028 };
  22029 
  22030 fn checkSimdBinOp(
  22031     sema: *Sema,
  22032     block: *Block,
  22033     src: LazySrcLoc,
  22034     uncasted_lhs: Air.Inst.Ref,
  22035     uncasted_rhs: Air.Inst.Ref,
  22036     lhs_src: LazySrcLoc,
  22037     rhs_src: LazySrcLoc,
  22038 ) CompileError!SimdBinOp {
  22039     const mod = sema.mod;
  22040     const lhs_ty = sema.typeOf(uncasted_lhs);
  22041     const rhs_ty = sema.typeOf(uncasted_rhs);
  22042 
  22043     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  22044     var vec_len: ?usize = if (lhs_ty.zigTypeTag(mod) == .Vector) lhs_ty.vectorLen(mod) else null;
  22045     const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{
  22046         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  22047     });
  22048     const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src);
  22049     const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src);
  22050 
  22051     return SimdBinOp{
  22052         .len = vec_len,
  22053         .lhs = lhs,
  22054         .rhs = rhs,
  22055         .lhs_val = try sema.resolveMaybeUndefVal(lhs),
  22056         .rhs_val = try sema.resolveMaybeUndefVal(rhs),
  22057         .result_ty = result_ty,
  22058         .scalar_ty = result_ty.scalarType(mod),
  22059     };
  22060 }
  22061 
  22062 fn checkVectorizableBinaryOperands(
  22063     sema: *Sema,
  22064     block: *Block,
  22065     src: LazySrcLoc,
  22066     lhs_ty: Type,
  22067     rhs_ty: Type,
  22068     lhs_src: LazySrcLoc,
  22069     rhs_src: LazySrcLoc,
  22070 ) CompileError!void {
  22071     const mod = sema.mod;
  22072     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  22073     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  22074     if (lhs_zig_ty_tag != .Vector and rhs_zig_ty_tag != .Vector) return;
  22075 
  22076     const lhs_is_vector = switch (lhs_zig_ty_tag) {
  22077         .Vector, .Array => true,
  22078         else => false,
  22079     };
  22080     const rhs_is_vector = switch (rhs_zig_ty_tag) {
  22081         .Vector, .Array => true,
  22082         else => false,
  22083     };
  22084 
  22085     if (lhs_is_vector and rhs_is_vector) {
  22086         const lhs_len = lhs_ty.arrayLen(mod);
  22087         const rhs_len = rhs_ty.arrayLen(mod);
  22088         if (lhs_len != rhs_len) {
  22089             const msg = msg: {
  22090                 const msg = try sema.errMsg(block, src, "vector length mismatch", .{});
  22091                 errdefer msg.destroy(sema.gpa);
  22092                 try sema.errNote(block, lhs_src, msg, "length {d} here", .{lhs_len});
  22093                 try sema.errNote(block, rhs_src, msg, "length {d} here", .{rhs_len});
  22094                 break :msg msg;
  22095             };
  22096             return sema.failWithOwnedErrorMsg(msg);
  22097         }
  22098     } else {
  22099         const msg = msg: {
  22100             const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: '{}' and '{}'", .{
  22101                 lhs_ty.fmt(mod), rhs_ty.fmt(mod),
  22102             });
  22103             errdefer msg.destroy(sema.gpa);
  22104             if (lhs_is_vector) {
  22105                 try sema.errNote(block, lhs_src, msg, "vector here", .{});
  22106                 try sema.errNote(block, rhs_src, msg, "scalar here", .{});
  22107             } else {
  22108                 try sema.errNote(block, lhs_src, msg, "scalar here", .{});
  22109                 try sema.errNote(block, rhs_src, msg, "vector here", .{});
  22110             }
  22111             break :msg msg;
  22112         };
  22113         return sema.failWithOwnedErrorMsg(msg);
  22114     }
  22115 }
  22116 
  22117 fn maybeOptionsSrc(sema: *Sema, block: *Block, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc {
  22118     if (base_src == .unneeded) return .unneeded;
  22119     const mod = sema.mod;
  22120     return mod.optionsSrc(mod.declPtr(block.src_decl), base_src, wanted);
  22121 }
  22122 
  22123 fn resolveExportOptions(
  22124     sema: *Sema,
  22125     block: *Block,
  22126     src: LazySrcLoc,
  22127     zir_ref: Zir.Inst.Ref,
  22128 ) CompileError!Module.Export.Options {
  22129     const mod = sema.mod;
  22130     const gpa = sema.gpa;
  22131     const ip = &mod.intern_pool;
  22132     const export_options_ty = try sema.getBuiltinType("ExportOptions");
  22133     const air_ref = try sema.resolveInst(zir_ref);
  22134     const options = try sema.coerce(block, export_options_ty, air_ref, src);
  22135 
  22136     const name_src = sema.maybeOptionsSrc(block, src, "name");
  22137     const linkage_src = sema.maybeOptionsSrc(block, src, "linkage");
  22138     const section_src = sema.maybeOptionsSrc(block, src, "section");
  22139     const visibility_src = sema.maybeOptionsSrc(block, src, "visibility");
  22140 
  22141     const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src);
  22142     const name_val = try sema.resolveConstValue(block, name_src, name_operand, "name of exported value must be comptime-known");
  22143     const name_ty = Type.slice_const_u8;
  22144     const name = try name_val.toAllocatedBytes(name_ty, sema.arena, mod);
  22145 
  22146     const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src);
  22147     const linkage_val = try sema.resolveConstValue(block, linkage_src, linkage_operand, "linkage of exported value must be comptime-known");
  22148     const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val);
  22149 
  22150     const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "section"), section_src);
  22151     const section_opt_val = try sema.resolveConstValue(block, section_src, section_operand, "linksection of exported value must be comptime-known");
  22152     const section_ty = Type.slice_const_u8;
  22153     const section = if (section_opt_val.optionalValue(mod)) |section_val|
  22154         try section_val.toAllocatedBytes(section_ty, sema.arena, mod)
  22155     else
  22156         null;
  22157 
  22158     const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "visibility"), visibility_src);
  22159     const visibility_val = try sema.resolveConstValue(block, visibility_src, visibility_operand, "visibility of exported value must be comptime-known");
  22160     const visibility = mod.toEnum(std.builtin.SymbolVisibility, visibility_val);
  22161 
  22162     if (name.len < 1) {
  22163         return sema.fail(block, name_src, "exported symbol name cannot be empty", .{});
  22164     }
  22165 
  22166     if (visibility != .default and linkage == .Internal) {
  22167         return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{
  22168             name, @tagName(visibility),
  22169         });
  22170     }
  22171 
  22172     return .{
  22173         .name = try ip.getOrPutString(gpa, name),
  22174         .linkage = linkage,
  22175         .section = try ip.getOrPutStringOpt(gpa, section),
  22176         .visibility = visibility,
  22177     };
  22178 }
  22179 
  22180 fn resolveBuiltinEnum(
  22181     sema: *Sema,
  22182     block: *Block,
  22183     src: LazySrcLoc,
  22184     zir_ref: Zir.Inst.Ref,
  22185     comptime name: []const u8,
  22186     reason: []const u8,
  22187 ) CompileError!@field(std.builtin, name) {
  22188     const mod = sema.mod;
  22189     const ty = try sema.getBuiltinType(name);
  22190     const air_ref = try sema.resolveInst(zir_ref);
  22191     const coerced = try sema.coerce(block, ty, air_ref, src);
  22192     const val = try sema.resolveConstValue(block, src, coerced, reason);
  22193     return mod.toEnum(@field(std.builtin, name), val);
  22194 }
  22195 
  22196 fn resolveAtomicOrder(
  22197     sema: *Sema,
  22198     block: *Block,
  22199     src: LazySrcLoc,
  22200     zir_ref: Zir.Inst.Ref,
  22201     reason: []const u8,
  22202 ) CompileError!std.builtin.AtomicOrder {
  22203     return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicOrder", reason);
  22204 }
  22205 
  22206 fn resolveAtomicRmwOp(
  22207     sema: *Sema,
  22208     block: *Block,
  22209     src: LazySrcLoc,
  22210     zir_ref: Zir.Inst.Ref,
  22211 ) CompileError!std.builtin.AtomicRmwOp {
  22212     return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicRmwOp", "@atomicRmW operation must be comptime-known");
  22213 }
  22214 
  22215 fn zirCmpxchg(
  22216     sema: *Sema,
  22217     block: *Block,
  22218     extended: Zir.Inst.Extended.InstData,
  22219 ) CompileError!Air.Inst.Ref {
  22220     const mod = sema.mod;
  22221     const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data;
  22222     const air_tag: Air.Inst.Tag = switch (extended.small) {
  22223         0 => .cmpxchg_weak,
  22224         1 => .cmpxchg_strong,
  22225         else => unreachable,
  22226     };
  22227     const src = LazySrcLoc.nodeOffset(extra.node);
  22228     // zig fmt: off
  22229     const elem_ty_src      : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  22230     const ptr_src          : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  22231     const expected_src     : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node };
  22232     const new_value_src    : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node };
  22233     const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = extra.node };
  22234     const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = extra.node };
  22235     // zig fmt: on
  22236     const expected_value = try sema.resolveInst(extra.expected_value);
  22237     const elem_ty = sema.typeOf(expected_value);
  22238     if (elem_ty.zigTypeTag(mod) == .Float) {
  22239         return sema.fail(
  22240             block,
  22241             elem_ty_src,
  22242             "expected bool, integer, enum, or pointer type; found '{}'",
  22243             .{elem_ty.fmt(mod)},
  22244         );
  22245     }
  22246     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  22247     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  22248     const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src);
  22249     const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, "atomic order of cmpxchg success must be comptime-known");
  22250     const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, "atomic order of cmpxchg failure must be comptime-known");
  22251 
  22252     if (@intFromEnum(success_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) {
  22253         return sema.fail(block, success_order_src, "success atomic ordering must be Monotonic or stricter", .{});
  22254     }
  22255     if (@intFromEnum(failure_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) {
  22256         return sema.fail(block, failure_order_src, "failure atomic ordering must be Monotonic or stricter", .{});
  22257     }
  22258     if (@intFromEnum(failure_order) > @intFromEnum(success_order)) {
  22259         return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{});
  22260     }
  22261     if (failure_order == .Release or failure_order == .AcqRel) {
  22262         return sema.fail(block, failure_order_src, "failure atomic ordering must not be Release or AcqRel", .{});
  22263     }
  22264 
  22265     const result_ty = try mod.optionalType(elem_ty.toIntern());
  22266 
  22267     // special case zero bit types
  22268     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
  22269         return sema.addConstant((try mod.intern(.{ .opt = .{
  22270             .ty = result_ty.toIntern(),
  22271             .val = .none,
  22272         } })).toValue());
  22273     }
  22274 
  22275     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  22276         if (try sema.resolveMaybeUndefVal(expected_value)) |expected_val| {
  22277             if (try sema.resolveMaybeUndefVal(new_value)) |new_val| {
  22278                 if (expected_val.isUndef(mod) or new_val.isUndef(mod)) {
  22279                     // TODO: this should probably cause the memory stored at the pointer
  22280                     // to become undef as well
  22281                     return sema.addConstUndef(result_ty);
  22282                 }
  22283                 const ptr_ty = sema.typeOf(ptr);
  22284                 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
  22285                 const result_val = try mod.intern(.{ .opt = .{
  22286                     .ty = result_ty.toIntern(),
  22287                     .val = if (stored_val.eql(expected_val, elem_ty, mod)) blk: {
  22288                         try sema.storePtr(block, src, ptr, new_value);
  22289                         break :blk .none;
  22290                     } else stored_val.toIntern(),
  22291                 } });
  22292                 return sema.addConstant(result_val.toValue());
  22293             } else break :rs new_value_src;
  22294         } else break :rs expected_src;
  22295     } else ptr_src;
  22296 
  22297     const flags: u32 = @as(u32, @intFromEnum(success_order)) |
  22298         (@as(u32, @intFromEnum(failure_order)) << 3);
  22299 
  22300     try sema.requireRuntimeBlock(block, src, runtime_src);
  22301     return block.addInst(.{
  22302         .tag = air_tag,
  22303         .data = .{ .ty_pl = .{
  22304             .ty = try sema.addType(result_ty),
  22305             .payload = try sema.addExtra(Air.Cmpxchg{
  22306                 .ptr = ptr,
  22307                 .expected_value = expected_value,
  22308                 .new_value = new_value,
  22309                 .flags = flags,
  22310             }),
  22311         } },
  22312     });
  22313 }
  22314 
  22315 fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22316     const mod = sema.mod;
  22317     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22318     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  22319     const len_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  22320     const scalar_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  22321     const len = @as(u32, @intCast(try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector splat destination length must be comptime-known")));
  22322     const scalar = try sema.resolveInst(extra.rhs);
  22323     const scalar_ty = sema.typeOf(scalar);
  22324     try sema.checkVectorElemType(block, scalar_src, scalar_ty);
  22325     const vector_ty = try mod.vectorType(.{
  22326         .len = len,
  22327         .child = scalar_ty.toIntern(),
  22328     });
  22329     if (try sema.resolveMaybeUndefVal(scalar)) |scalar_val| {
  22330         if (scalar_val.isUndef(mod)) return sema.addConstUndef(vector_ty);
  22331         return sema.addConstant(try sema.splat(vector_ty, scalar_val));
  22332     }
  22333 
  22334     try sema.requireRuntimeBlock(block, inst_data.src(), scalar_src);
  22335     return block.addTyOp(.splat, vector_ty, scalar);
  22336 }
  22337 
  22338 fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22339     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22340     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  22341     const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22342     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22343     const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp", "@reduce operation must be comptime-known");
  22344     const operand = try sema.resolveInst(extra.rhs);
  22345     const operand_ty = sema.typeOf(operand);
  22346     const mod = sema.mod;
  22347 
  22348     if (operand_ty.zigTypeTag(mod) != .Vector) {
  22349         return sema.fail(block, operand_src, "expected vector, found '{}'", .{operand_ty.fmt(mod)});
  22350     }
  22351 
  22352     const scalar_ty = operand_ty.childType(mod);
  22353 
  22354     // Type-check depending on operation.
  22355     switch (operation) {
  22356         .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(mod)) {
  22357             .Int, .Bool => {},
  22358             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{}'", .{
  22359                 @tagName(operation), operand_ty.fmt(mod),
  22360             }),
  22361         },
  22362         .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(mod)) {
  22363             .Int, .Float => {},
  22364             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{}'", .{
  22365                 @tagName(operation), operand_ty.fmt(mod),
  22366             }),
  22367         },
  22368     }
  22369 
  22370     const vec_len = operand_ty.vectorLen(mod);
  22371     if (vec_len == 0) {
  22372         // TODO re-evaluate if we should introduce a "neutral value" for some operations,
  22373         // e.g. zero for add and one for mul.
  22374         return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{});
  22375     }
  22376 
  22377     if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
  22378         if (operand_val.isUndef(mod)) return sema.addConstUndef(scalar_ty);
  22379 
  22380         var accum: Value = try operand_val.elemValue(mod, 0);
  22381         var i: u32 = 1;
  22382         while (i < vec_len) : (i += 1) {
  22383             const elem_val = try operand_val.elemValue(mod, i);
  22384             switch (operation) {
  22385                 .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, mod),
  22386                 .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, mod),
  22387                 .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, mod),
  22388                 .Min => accum = accum.numberMin(elem_val, mod),
  22389                 .Max => accum = accum.numberMax(elem_val, mod),
  22390                 .Add => accum = try sema.numberAddWrapScalar(accum, elem_val, scalar_ty),
  22391                 .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, mod),
  22392             }
  22393         }
  22394         return sema.addConstant(accum);
  22395     }
  22396 
  22397     try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
  22398     return block.addInst(.{
  22399         .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  22400         .data = .{ .reduce = .{
  22401             .operand = operand,
  22402             .operation = operation,
  22403         } },
  22404     });
  22405 }
  22406 
  22407 fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22408     const mod = sema.mod;
  22409     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22410     const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
  22411     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22412     const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  22413 
  22414     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  22415     try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
  22416     var a = try sema.resolveInst(extra.a);
  22417     var b = try sema.resolveInst(extra.b);
  22418     var mask = try sema.resolveInst(extra.mask);
  22419     var mask_ty = sema.typeOf(mask);
  22420 
  22421     const mask_len = switch (sema.typeOf(mask).zigTypeTag(mod)) {
  22422         .Array, .Vector => sema.typeOf(mask).arrayLen(mod),
  22423         else => return sema.fail(block, mask_src, "expected vector or array, found '{}'", .{sema.typeOf(mask).fmt(sema.mod)}),
  22424     };
  22425     mask_ty = try mod.vectorType(.{
  22426         .len = @as(u32, @intCast(mask_len)),
  22427         .child = .i32_type,
  22428     });
  22429     mask = try sema.coerce(block, mask_ty, mask, mask_src);
  22430     const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask, "shuffle mask must be comptime-known");
  22431     return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @as(u32, @intCast(mask_len)));
  22432 }
  22433 
  22434 fn analyzeShuffle(
  22435     sema: *Sema,
  22436     block: *Block,
  22437     src_node: i32,
  22438     elem_ty: Type,
  22439     a_arg: Air.Inst.Ref,
  22440     b_arg: Air.Inst.Ref,
  22441     mask: Value,
  22442     mask_len: u32,
  22443 ) CompileError!Air.Inst.Ref {
  22444     const mod = sema.mod;
  22445     const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node };
  22446     const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node };
  22447     const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node };
  22448     var a = a_arg;
  22449     var b = b_arg;
  22450 
  22451     const res_ty = try mod.vectorType(.{
  22452         .len = mask_len,
  22453         .child = elem_ty.toIntern(),
  22454     });
  22455 
  22456     var maybe_a_len = switch (sema.typeOf(a).zigTypeTag(mod)) {
  22457         .Array, .Vector => sema.typeOf(a).arrayLen(mod),
  22458         .Undefined => null,
  22459         else => return sema.fail(block, a_src, "expected vector or array with element type '{}', found '{}'", .{
  22460             elem_ty.fmt(sema.mod),
  22461             sema.typeOf(a).fmt(sema.mod),
  22462         }),
  22463     };
  22464     var maybe_b_len = switch (sema.typeOf(b).zigTypeTag(mod)) {
  22465         .Array, .Vector => sema.typeOf(b).arrayLen(mod),
  22466         .Undefined => null,
  22467         else => return sema.fail(block, b_src, "expected vector or array with element type '{}', found '{}'", .{
  22468             elem_ty.fmt(sema.mod),
  22469             sema.typeOf(b).fmt(sema.mod),
  22470         }),
  22471     };
  22472     if (maybe_a_len == null and maybe_b_len == null) {
  22473         return sema.addConstUndef(res_ty);
  22474     }
  22475     const a_len = @as(u32, @intCast(maybe_a_len orelse maybe_b_len.?));
  22476     const b_len = @as(u32, @intCast(maybe_b_len orelse a_len));
  22477 
  22478     const a_ty = try mod.vectorType(.{
  22479         .len = a_len,
  22480         .child = elem_ty.toIntern(),
  22481     });
  22482     const b_ty = try mod.vectorType(.{
  22483         .len = b_len,
  22484         .child = elem_ty.toIntern(),
  22485     });
  22486 
  22487     if (maybe_a_len == null) a = try sema.addConstUndef(a_ty) else a = try sema.coerce(block, a_ty, a, a_src);
  22488     if (maybe_b_len == null) b = try sema.addConstUndef(b_ty) else b = try sema.coerce(block, b_ty, b, b_src);
  22489 
  22490     const operand_info = [2]std.meta.Tuple(&.{ u64, LazySrcLoc, Type }){
  22491         .{ a_len, a_src, a_ty },
  22492         .{ b_len, b_src, b_ty },
  22493     };
  22494 
  22495     for (0..@as(usize, @intCast(mask_len))) |i| {
  22496         const elem = try mask.elemValue(sema.mod, i);
  22497         if (elem.isUndef(mod)) continue;
  22498         const int = elem.toSignedInt(mod);
  22499         var unsigned: u32 = undefined;
  22500         var chosen: u32 = undefined;
  22501         if (int >= 0) {
  22502             unsigned = @as(u32, @intCast(int));
  22503             chosen = 0;
  22504         } else {
  22505             unsigned = @as(u32, @intCast(~int));
  22506             chosen = 1;
  22507         }
  22508         if (unsigned >= operand_info[chosen][0]) {
  22509             const msg = msg: {
  22510                 const msg = try sema.errMsg(block, mask_src, "mask index '{d}' has out-of-bounds selection", .{i});
  22511                 errdefer msg.destroy(sema.gpa);
  22512 
  22513                 try sema.errNote(block, operand_info[chosen][1], msg, "selected index '{d}' out of bounds of '{}'", .{
  22514                     unsigned,
  22515                     operand_info[chosen][2].fmt(sema.mod),
  22516                 });
  22517 
  22518                 if (chosen == 0) {
  22519                     try sema.errNote(block, b_src, msg, "selections from the second vector are specified with negative numbers", .{});
  22520                 }
  22521 
  22522                 break :msg msg;
  22523             };
  22524             return sema.failWithOwnedErrorMsg(msg);
  22525         }
  22526     }
  22527 
  22528     if (try sema.resolveMaybeUndefVal(a)) |a_val| {
  22529         if (try sema.resolveMaybeUndefVal(b)) |b_val| {
  22530             const values = try sema.arena.alloc(InternPool.Index, mask_len);
  22531             for (values, 0..) |*value, i| {
  22532                 const mask_elem_val = try mask.elemValue(sema.mod, i);
  22533                 if (mask_elem_val.isUndef(mod)) {
  22534                     value.* = try mod.intern(.{ .undef = elem_ty.toIntern() });
  22535                     continue;
  22536                 }
  22537                 const int = mask_elem_val.toSignedInt(mod);
  22538                 const unsigned = if (int >= 0) @as(u32, @intCast(int)) else @as(u32, @intCast(~int));
  22539                 values[i] = try (try (if (int >= 0) a_val else b_val).elemValue(mod, unsigned)).intern(elem_ty, mod);
  22540             }
  22541             return sema.addConstant((try mod.intern(.{ .aggregate = .{
  22542                 .ty = res_ty.toIntern(),
  22543                 .storage = .{ .elems = values },
  22544             } })).toValue());
  22545         }
  22546     }
  22547 
  22548     // All static analysis passed, and not comptime.
  22549     // For runtime codegen, vectors a and b must be the same length. Here we
  22550     // recursively @shuffle the smaller vector to append undefined elements
  22551     // to it up to the length of the longer vector. This recursion terminates
  22552     // in 1 call because these calls to analyzeShuffle guarantee a_len == b_len.
  22553     if (a_len != b_len) {
  22554         const min_len = @min(a_len, b_len);
  22555         const max_src = if (a_len > b_len) a_src else b_src;
  22556         const max_len = try sema.usizeCast(block, max_src, @max(a_len, b_len));
  22557 
  22558         const expand_mask_values = try sema.arena.alloc(InternPool.Index, max_len);
  22559         for (@as(usize, @intCast(0))..@as(usize, @intCast(min_len))) |i| {
  22560             expand_mask_values[i] = (try mod.intValue(Type.comptime_int, i)).toIntern();
  22561         }
  22562         for (@as(usize, @intCast(min_len))..@as(usize, @intCast(max_len))) |i| {
  22563             expand_mask_values[i] = (try mod.intValue(Type.comptime_int, -1)).toIntern();
  22564         }
  22565         const expand_mask = try mod.intern(.{ .aggregate = .{
  22566             .ty = (try mod.vectorType(.{ .len = @as(u32, @intCast(max_len)), .child = .comptime_int_type })).toIntern(),
  22567             .storage = .{ .elems = expand_mask_values },
  22568         } });
  22569 
  22570         if (a_len < b_len) {
  22571             const undef = try sema.addConstUndef(a_ty);
  22572             a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, expand_mask.toValue(), @as(u32, @intCast(max_len)));
  22573         } else {
  22574             const undef = try sema.addConstUndef(b_ty);
  22575             b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, expand_mask.toValue(), @as(u32, @intCast(max_len)));
  22576         }
  22577     }
  22578 
  22579     return block.addInst(.{
  22580         .tag = .shuffle,
  22581         .data = .{ .ty_pl = .{
  22582             .ty = try sema.addType(res_ty),
  22583             .payload = try block.sema.addExtra(Air.Shuffle{
  22584                 .a = a,
  22585                 .b = b,
  22586                 .mask = mask.toIntern(),
  22587                 .mask_len = mask_len,
  22588             }),
  22589         } },
  22590     });
  22591 }
  22592 
  22593 fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  22594     const mod = sema.mod;
  22595     const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data;
  22596 
  22597     const src = LazySrcLoc.nodeOffset(extra.node);
  22598     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  22599     const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  22600     const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node };
  22601     const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node };
  22602 
  22603     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  22604     try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
  22605     const pred_uncoerced = try sema.resolveInst(extra.pred);
  22606     const pred_ty = sema.typeOf(pred_uncoerced);
  22607 
  22608     const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison(mod)) {
  22609         .Vector, .Array => pred_ty.arrayLen(mod),
  22610         else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(mod)}),
  22611     };
  22612     const vec_len = @as(u32, @intCast(try sema.usizeCast(block, pred_src, vec_len_u64)));
  22613 
  22614     const bool_vec_ty = try mod.vectorType(.{
  22615         .len = vec_len,
  22616         .child = .bool_type,
  22617     });
  22618     const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src);
  22619 
  22620     const vec_ty = try mod.vectorType(.{
  22621         .len = vec_len,
  22622         .child = elem_ty.toIntern(),
  22623     });
  22624     const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src);
  22625     const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src);
  22626 
  22627     const maybe_pred = try sema.resolveMaybeUndefVal(pred);
  22628     const maybe_a = try sema.resolveMaybeUndefVal(a);
  22629     const maybe_b = try sema.resolveMaybeUndefVal(b);
  22630 
  22631     const runtime_src = if (maybe_pred) |pred_val| rs: {
  22632         if (pred_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22633 
  22634         if (maybe_a) |a_val| {
  22635             if (a_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22636 
  22637             if (maybe_b) |b_val| {
  22638                 if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22639 
  22640                 const elems = try sema.gpa.alloc(InternPool.Index, vec_len);
  22641                 for (elems, 0..) |*elem, i| {
  22642                     const pred_elem_val = try pred_val.elemValue(mod, i);
  22643                     const should_choose_a = pred_elem_val.toBool();
  22644                     elem.* = try (try (if (should_choose_a) a_val else b_val).elemValue(mod, i)).intern(elem_ty, mod);
  22645                 }
  22646 
  22647                 return sema.addConstant((try mod.intern(.{ .aggregate = .{
  22648                     .ty = vec_ty.toIntern(),
  22649                     .storage = .{ .elems = elems },
  22650                 } })).toValue());
  22651             } else {
  22652                 break :rs b_src;
  22653             }
  22654         } else {
  22655             if (maybe_b) |b_val| {
  22656                 if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22657             }
  22658             break :rs a_src;
  22659         }
  22660     } else rs: {
  22661         if (maybe_a) |a_val| {
  22662             if (a_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22663         }
  22664         if (maybe_b) |b_val| {
  22665             if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22666         }
  22667         break :rs pred_src;
  22668     };
  22669 
  22670     try sema.requireRuntimeBlock(block, src, runtime_src);
  22671     return block.addInst(.{
  22672         .tag = .select,
  22673         .data = .{ .pl_op = .{
  22674             .operand = pred,
  22675             .payload = try block.sema.addExtra(Air.Bin{
  22676                 .lhs = a,
  22677                 .rhs = b,
  22678             }),
  22679         } },
  22680     });
  22681 }
  22682 
  22683 fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22684     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22685     const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data;
  22686     // zig fmt: off
  22687     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22688     const ptr_src    : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22689     const order_src  : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22690     // zig fmt: on
  22691     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  22692     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  22693     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true);
  22694     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicLoad must be comptime-known");
  22695 
  22696     switch (order) {
  22697         .Release, .AcqRel => {
  22698             return sema.fail(
  22699                 block,
  22700                 order_src,
  22701                 "@atomicLoad atomic ordering must not be Release or AcqRel",
  22702                 .{},
  22703             );
  22704         },
  22705         else => {},
  22706     }
  22707 
  22708     if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
  22709         return sema.addConstant(val);
  22710     }
  22711 
  22712     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
  22713         if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| {
  22714             return sema.addConstant(elem_val);
  22715         }
  22716     }
  22717 
  22718     try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);
  22719     return block.addInst(.{
  22720         .tag = .atomic_load,
  22721         .data = .{ .atomic_load = .{
  22722             .ptr = ptr,
  22723             .order = order,
  22724         } },
  22725     });
  22726 }
  22727 
  22728 fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22729     const mod = sema.mod;
  22730     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22731     const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data;
  22732     const src = inst_data.src();
  22733     // zig fmt: off
  22734     const elem_ty_src   : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22735     const ptr_src       : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22736     const op_src        : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22737     const operand_src   : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  22738     const order_src     : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node };
  22739     // zig fmt: on
  22740     const operand = try sema.resolveInst(extra.operand);
  22741     const elem_ty = sema.typeOf(operand);
  22742     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  22743     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  22744     const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation);
  22745 
  22746     switch (elem_ty.zigTypeTag(mod)) {
  22747         .Enum => if (op != .Xchg) {
  22748             return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{});
  22749         },
  22750         .Bool => if (op != .Xchg) {
  22751             return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{});
  22752         },
  22753         .Float => switch (op) {
  22754             .Xchg, .Add, .Sub, .Max, .Min => {},
  22755             else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}),
  22756         },
  22757         else => {},
  22758     }
  22759     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicRmW must be comptime-known");
  22760 
  22761     if (order == .Unordered) {
  22762         return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be Unordered", .{});
  22763     }
  22764 
  22765     // special case zero bit types
  22766     if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
  22767         return sema.addConstant(val);
  22768     }
  22769 
  22770     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  22771         const maybe_operand_val = try sema.resolveMaybeUndefVal(operand);
  22772         const operand_val = maybe_operand_val orelse {
  22773             try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src);
  22774             break :rs operand_src;
  22775         };
  22776         if (ptr_val.isComptimeMutablePtr(mod)) {
  22777             const ptr_ty = sema.typeOf(ptr);
  22778             const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
  22779             const new_val = switch (op) {
  22780                 // zig fmt: off
  22781                 .Xchg => operand_val,
  22782                 .Add  => try sema.numberAddWrapScalar(stored_val, operand_val, elem_ty),
  22783                 .Sub  => try sema.numberSubWrapScalar(stored_val, operand_val, elem_ty),
  22784                 .And  => try                   stored_val.bitwiseAnd   (operand_val, elem_ty, sema.arena, mod),
  22785                 .Nand => try                   stored_val.bitwiseNand  (operand_val, elem_ty, sema.arena, mod),
  22786                 .Or   => try                   stored_val.bitwiseOr    (operand_val, elem_ty, sema.arena, mod),
  22787                 .Xor  => try                   stored_val.bitwiseXor   (operand_val, elem_ty, sema.arena, mod),
  22788                 .Max  =>                       stored_val.numberMax    (operand_val,                      mod),
  22789                 .Min  =>                       stored_val.numberMin    (operand_val,                      mod),
  22790                 // zig fmt: on
  22791             };
  22792             try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty);
  22793             return sema.addConstant(stored_val);
  22794         } else break :rs ptr_src;
  22795     } else ptr_src;
  22796 
  22797     const flags: u32 = @as(u32, @intFromEnum(order)) | (@as(u32, @intFromEnum(op)) << 3);
  22798 
  22799     try sema.requireRuntimeBlock(block, src, runtime_src);
  22800     return block.addInst(.{
  22801         .tag = .atomic_rmw,
  22802         .data = .{ .pl_op = .{
  22803             .operand = ptr,
  22804             .payload = try sema.addExtra(Air.AtomicRmw{
  22805                 .operand = operand,
  22806                 .flags = flags,
  22807             }),
  22808         } },
  22809     });
  22810 }
  22811 
  22812 fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  22813     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22814     const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
  22815     const src = inst_data.src();
  22816     // zig fmt: off
  22817     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22818     const ptr_src       : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22819     const operand_src   : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22820     const order_src     : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  22821     // zig fmt: on
  22822     const operand = try sema.resolveInst(extra.operand);
  22823     const elem_ty = sema.typeOf(operand);
  22824     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  22825     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  22826     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicStore must be comptime-known");
  22827 
  22828     const air_tag: Air.Inst.Tag = switch (order) {
  22829         .Acquire, .AcqRel => {
  22830             return sema.fail(
  22831                 block,
  22832                 order_src,
  22833                 "@atomicStore atomic ordering must not be Acquire or AcqRel",
  22834                 .{},
  22835             );
  22836         },
  22837         .Unordered => .atomic_store_unordered,
  22838         .Monotonic => .atomic_store_monotonic,
  22839         .Release => .atomic_store_release,
  22840         .SeqCst => .atomic_store_seq_cst,
  22841     };
  22842 
  22843     return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
  22844 }
  22845 
  22846 fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22847     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22848     const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data;
  22849     const src = inst_data.src();
  22850 
  22851     const mulend1_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22852     const mulend2_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22853     const addend_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  22854 
  22855     const addend = try sema.resolveInst(extra.addend);
  22856     const ty = sema.typeOf(addend);
  22857     const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src);
  22858     const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src);
  22859 
  22860     const maybe_mulend1 = try sema.resolveMaybeUndefVal(mulend1);
  22861     const maybe_mulend2 = try sema.resolveMaybeUndefVal(mulend2);
  22862     const maybe_addend = try sema.resolveMaybeUndefVal(addend);
  22863     const mod = sema.mod;
  22864 
  22865     switch (ty.scalarType(mod).zigTypeTag(mod)) {
  22866         .ComptimeFloat, .Float => {},
  22867         else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(sema.mod)}),
  22868     }
  22869 
  22870     const runtime_src = if (maybe_mulend1) |mulend1_val| rs: {
  22871         if (maybe_mulend2) |mulend2_val| {
  22872             if (mulend2_val.isUndef(mod)) return sema.addConstUndef(ty);
  22873 
  22874             if (maybe_addend) |addend_val| {
  22875                 if (addend_val.isUndef(mod)) return sema.addConstUndef(ty);
  22876                 const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, sema.mod);
  22877                 return sema.addConstant(result_val);
  22878             } else {
  22879                 break :rs addend_src;
  22880             }
  22881         } else {
  22882             if (maybe_addend) |addend_val| {
  22883                 if (addend_val.isUndef(mod)) return sema.addConstUndef(ty);
  22884             }
  22885             break :rs mulend2_src;
  22886         }
  22887     } else rs: {
  22888         if (maybe_mulend2) |mulend2_val| {
  22889             if (mulend2_val.isUndef(mod)) return sema.addConstUndef(ty);
  22890         }
  22891         if (maybe_addend) |addend_val| {
  22892             if (addend_val.isUndef(mod)) return sema.addConstUndef(ty);
  22893         }
  22894         break :rs mulend1_src;
  22895     };
  22896 
  22897     try sema.requireRuntimeBlock(block, src, runtime_src);
  22898     return block.addInst(.{
  22899         .tag = .mul_add,
  22900         .data = .{ .pl_op = .{
  22901             .operand = addend,
  22902             .payload = try sema.addExtra(Air.Bin{
  22903                 .lhs = mulend1,
  22904                 .rhs = mulend2,
  22905             }),
  22906         } },
  22907     });
  22908 }
  22909 
  22910 fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22911     const tracy = trace(@src());
  22912     defer tracy.end();
  22913 
  22914     const mod = sema.mod;
  22915     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22916     const modifier_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22917     const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22918     const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22919     const call_src = inst_data.src();
  22920 
  22921     const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;
  22922     var func = try sema.resolveInst(extra.callee);
  22923 
  22924     const modifier_ty = try sema.getBuiltinType("CallModifier");
  22925     const air_ref = try sema.resolveInst(extra.modifier);
  22926     const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src);
  22927     const modifier_val = try sema.resolveConstValue(block, modifier_src, modifier_ref, "call modifier must be comptime-known");
  22928     var modifier = mod.toEnum(std.builtin.CallModifier, modifier_val);
  22929     switch (modifier) {
  22930         // These can be upgraded to comptime or nosuspend calls.
  22931         .auto, .never_tail, .no_async => {
  22932             if (block.is_comptime) {
  22933                 if (modifier == .never_tail) {
  22934                     return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{});
  22935                 }
  22936                 modifier = .compile_time;
  22937             } else if (extra.flags.is_nosuspend) {
  22938                 modifier = .no_async;
  22939             }
  22940         },
  22941         // These can be upgraded to comptime. nosuspend bit can be safely ignored.
  22942         .always_inline, .compile_time => {
  22943             _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse {
  22944                 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)});
  22945             };
  22946 
  22947             if (block.is_comptime) {
  22948                 modifier = .compile_time;
  22949             }
  22950         },
  22951         .always_tail => {
  22952             if (block.is_comptime) {
  22953                 modifier = .compile_time;
  22954             }
  22955         },
  22956         .async_kw => {
  22957             if (extra.flags.is_nosuspend) {
  22958                 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{});
  22959             }
  22960             if (block.is_comptime) {
  22961                 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{});
  22962             }
  22963         },
  22964         .never_inline => {
  22965             if (block.is_comptime) {
  22966                 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{});
  22967             }
  22968         },
  22969     }
  22970 
  22971     const args = try sema.resolveInst(extra.args);
  22972 
  22973     const args_ty = sema.typeOf(args);
  22974     if (!args_ty.isTuple(mod) and args_ty.toIntern() != .empty_struct_type) {
  22975         return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(sema.mod)});
  22976     }
  22977 
  22978     var resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(mod));
  22979     for (resolved_args, 0..) |*resolved, i| {
  22980         resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @as(u32, @intCast(i)), args_ty);
  22981     }
  22982 
  22983     const callee_ty = sema.typeOf(func);
  22984     const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false);
  22985     const ensure_result_used = extra.flags.ensure_result_used;
  22986     return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, null, null);
  22987 }
  22988 
  22989 fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22990     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22991     const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data;
  22992     const src = inst_data.src();
  22993     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22994     const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22995     const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22996 
  22997     const parent_ty = try sema.resolveType(block, ty_src, extra.parent_type);
  22998     const field_name = try sema.resolveConstStringIntern(block, name_src, extra.field_name, "field name must be comptime-known");
  22999     const field_ptr = try sema.resolveInst(extra.field_ptr);
  23000     const field_ptr_ty = sema.typeOf(field_ptr);
  23001     const mod = sema.mod;
  23002     const ip = &mod.intern_pool;
  23003 
  23004     if (parent_ty.zigTypeTag(mod) != .Struct and parent_ty.zigTypeTag(mod) != .Union) {
  23005         return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)});
  23006     }
  23007     try sema.resolveTypeLayout(parent_ty);
  23008 
  23009     const field_index = switch (parent_ty.zigTypeTag(mod)) {
  23010         .Struct => blk: {
  23011             if (parent_ty.isTuple(mod)) {
  23012                 if (ip.stringEqlSlice(field_name, "len")) {
  23013                     return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
  23014                 }
  23015                 break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, name_src);
  23016             } else {
  23017                 break :blk try sema.structFieldIndex(block, parent_ty, field_name, name_src);
  23018             }
  23019         },
  23020         .Union => try sema.unionFieldIndex(block, parent_ty, field_name, name_src),
  23021         else => unreachable,
  23022     };
  23023 
  23024     if (parent_ty.zigTypeTag(mod) == .Struct and parent_ty.structFieldIsComptime(field_index, mod)) {
  23025         return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{});
  23026     }
  23027 
  23028     try sema.checkPtrOperand(block, ptr_src, field_ptr_ty);
  23029     const field_ptr_ty_info = field_ptr_ty.ptrInfo(mod);
  23030 
  23031     var ptr_ty_data: InternPool.Key.PtrType = .{
  23032         .child = parent_ty.structFieldType(field_index, mod).toIntern(),
  23033         .flags = .{
  23034             .address_space = field_ptr_ty_info.flags.address_space,
  23035             .is_const = field_ptr_ty_info.flags.is_const,
  23036         },
  23037     };
  23038 
  23039     if (parent_ty.containerLayout(mod) == .Packed) {
  23040         return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{});
  23041     } else {
  23042         ptr_ty_data.flags.alignment = blk: {
  23043             if (mod.typeToStruct(parent_ty)) |struct_obj| {
  23044                 break :blk struct_obj.fields.values()[field_index].abi_align;
  23045             } else if (mod.typeToUnion(parent_ty)) |union_obj| {
  23046                 break :blk union_obj.fields.values()[field_index].abi_align;
  23047             } else {
  23048                 break :blk .none;
  23049             }
  23050         };
  23051     }
  23052 
  23053     const actual_field_ptr_ty = try mod.ptrType(ptr_ty_data);
  23054     const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src);
  23055 
  23056     ptr_ty_data.child = parent_ty.toIntern();
  23057     const result_ptr = try mod.ptrType(ptr_ty_data);
  23058 
  23059     if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| {
  23060         const field = switch (ip.indexToKey(field_ptr_val.toIntern())) {
  23061             .ptr => |ptr| switch (ptr.addr) {
  23062                 .field => |field| field,
  23063                 else => null,
  23064             },
  23065             else => null,
  23066         } orelse return sema.fail(block, ptr_src, "pointer value not based on parent struct", .{});
  23067 
  23068         if (field.index != field_index) {
  23069             const msg = msg: {
  23070                 const msg = try sema.errMsg(
  23071                     block,
  23072                     src,
  23073                     "field '{}' has index '{d}' but pointer value is index '{d}' of struct '{}'",
  23074                     .{
  23075                         field_name.fmt(ip),
  23076                         field_index,
  23077                         field.index,
  23078                         parent_ty.fmt(sema.mod),
  23079                     },
  23080                 );
  23081                 errdefer msg.destroy(sema.gpa);
  23082                 try sema.addDeclaredHereNote(msg, parent_ty);
  23083                 break :msg msg;
  23084             };
  23085             return sema.failWithOwnedErrorMsg(msg);
  23086         }
  23087         return sema.addConstant(field.base.toValue());
  23088     }
  23089 
  23090     try sema.requireRuntimeBlock(block, src, ptr_src);
  23091     try sema.queueFullTypeResolution(result_ptr);
  23092     return block.addInst(.{
  23093         .tag = .field_parent_ptr,
  23094         .data = .{ .ty_pl = .{
  23095             .ty = try sema.addType(result_ptr),
  23096             .payload = try block.sema.addExtra(Air.FieldParentPtr{
  23097                 .field_ptr = casted_field_ptr,
  23098                 .field_index = @as(u32, @intCast(field_index)),
  23099             }),
  23100         } },
  23101     });
  23102 }
  23103 
  23104 fn zirMinMax(
  23105     sema: *Sema,
  23106     block: *Block,
  23107     inst: Zir.Inst.Index,
  23108     comptime air_tag: Air.Inst.Tag,
  23109 ) CompileError!Air.Inst.Ref {
  23110     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  23111     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23112     const src = inst_data.src();
  23113     const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23114     const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23115     const lhs = try sema.resolveInst(extra.lhs);
  23116     const rhs = try sema.resolveInst(extra.rhs);
  23117     try sema.checkNumericType(block, lhs_src, sema.typeOf(lhs));
  23118     try sema.checkNumericType(block, rhs_src, sema.typeOf(rhs));
  23119     return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src });
  23120 }
  23121 
  23122 fn zirMinMaxMulti(
  23123     sema: *Sema,
  23124     block: *Block,
  23125     extended: Zir.Inst.Extended.InstData,
  23126     comptime air_tag: Air.Inst.Tag,
  23127 ) CompileError!Air.Inst.Ref {
  23128     const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
  23129     const src_node = extra.data.src_node;
  23130     const src = LazySrcLoc.nodeOffset(src_node);
  23131     const operands = sema.code.refSlice(extra.end, extended.small);
  23132 
  23133     const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
  23134     const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len);
  23135 
  23136     for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| {
  23137         op_src.* = switch (i) {
  23138             0 => .{ .node_offset_builtin_call_arg0 = src_node },
  23139             1 => .{ .node_offset_builtin_call_arg1 = src_node },
  23140             2 => .{ .node_offset_builtin_call_arg2 = src_node },
  23141             3 => .{ .node_offset_builtin_call_arg3 = src_node },
  23142             4 => .{ .node_offset_builtin_call_arg4 = src_node },
  23143             5 => .{ .node_offset_builtin_call_arg5 = src_node },
  23144             else => src, // TODO: better source location
  23145         };
  23146         air_ref.* = try sema.resolveInst(zir_ref);
  23147         try sema.checkNumericType(block, op_src.*, sema.typeOf(air_ref.*));
  23148     }
  23149 
  23150     return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs);
  23151 }
  23152 
  23153 fn analyzeMinMax(
  23154     sema: *Sema,
  23155     block: *Block,
  23156     src: LazySrcLoc,
  23157     comptime air_tag: Air.Inst.Tag,
  23158     operands: []const Air.Inst.Ref,
  23159     operand_srcs: []const LazySrcLoc,
  23160 ) CompileError!Air.Inst.Ref {
  23161     assert(operands.len == operand_srcs.len);
  23162     assert(operands.len > 0);
  23163     const mod = sema.mod;
  23164 
  23165     if (operands.len == 1) return operands[0];
  23166 
  23167     const opFunc = switch (air_tag) {
  23168         .min => Value.numberMin,
  23169         .max => Value.numberMax,
  23170         else => @compileError("unreachable"),
  23171     };
  23172 
  23173     // The set of runtime-known operands. Set up in the loop below.
  23174     var runtime_known = try std.DynamicBitSet.initFull(sema.arena, operands.len);
  23175     // The current minmax value - initially this will always be comptime-known, then we'll add
  23176     // runtime values into the mix later.
  23177     var cur_minmax: ?Air.Inst.Ref = null;
  23178     var cur_minmax_src: LazySrcLoc = undefined; // defined if cur_minmax not null
  23179     // The current known scalar bounds of the value.
  23180     var bounds_status: enum {
  23181         unknown, // We've only seen undef comptime_ints so far, so do not know the bounds.
  23182         defined, // We've seen only integers, so the bounds are defined.
  23183         non_integral, // There are floats in the mix, so the bounds aren't defined.
  23184     } = .unknown;
  23185     var cur_min_scalar: Value = undefined;
  23186     var cur_max_scalar: Value = undefined;
  23187 
  23188     // First, find all comptime-known arguments, and get their min/max
  23189 
  23190     for (operands, operand_srcs, 0..) |operand, operand_src, operand_idx| {
  23191         // Resolve the value now to avoid redundant calls to `checkSimdBinOp` - we'll have to call
  23192         // it in the runtime path anyway since the result type may have been refined
  23193         const unresolved_uncoerced_val = try sema.resolveMaybeUndefVal(operand) orelse continue;
  23194         const uncoerced_val = try sema.resolveLazyValue(unresolved_uncoerced_val);
  23195 
  23196         runtime_known.unset(operand_idx);
  23197 
  23198         switch (bounds_status) {
  23199             .unknown, .defined => refine_bounds: {
  23200                 const ty = sema.typeOf(operand);
  23201                 if (!ty.scalarType(mod).isInt(mod) and !ty.scalarType(mod).eql(Type.comptime_int, mod)) {
  23202                     bounds_status = .non_integral;
  23203                     break :refine_bounds;
  23204                 }
  23205                 const scalar_bounds: ?[2]Value = bounds: {
  23206                     if (!ty.isVector(mod)) break :bounds try uncoerced_val.intValueBounds(mod);
  23207                     var cur_bounds: [2]Value = try Value.intValueBounds(try uncoerced_val.elemValue(mod, 0), mod) orelse break :bounds null;
  23208                     const len = try sema.usizeCast(block, src, ty.vectorLen(mod));
  23209                     for (1..len) |i| {
  23210                         const elem = try uncoerced_val.elemValue(mod, i);
  23211                         const elem_bounds = try elem.intValueBounds(mod) orelse break :bounds null;
  23212                         cur_bounds = .{
  23213                             Value.numberMin(elem_bounds[0], cur_bounds[0], mod),
  23214                             Value.numberMax(elem_bounds[1], cur_bounds[1], mod),
  23215                         };
  23216                     }
  23217                     break :bounds cur_bounds;
  23218                 };
  23219                 if (scalar_bounds) |bounds| {
  23220                     if (bounds_status == .unknown) {
  23221                         cur_min_scalar = bounds[0];
  23222                         cur_max_scalar = bounds[1];
  23223                         bounds_status = .defined;
  23224                     } else {
  23225                         cur_min_scalar = opFunc(cur_min_scalar, bounds[0], mod);
  23226                         cur_max_scalar = opFunc(cur_max_scalar, bounds[1], mod);
  23227                     }
  23228                 }
  23229             },
  23230             .non_integral => {},
  23231         }
  23232 
  23233         const cur = cur_minmax orelse {
  23234             cur_minmax = operand;
  23235             cur_minmax_src = operand_src;
  23236             continue;
  23237         };
  23238 
  23239         const simd_op = try sema.checkSimdBinOp(block, src, cur, operand, cur_minmax_src, operand_src);
  23240         const cur_val = try sema.resolveLazyValue(simd_op.lhs_val.?); // cur_minmax is comptime-known
  23241         const operand_val = try sema.resolveLazyValue(simd_op.rhs_val.?); // we checked the operand was resolvable above
  23242 
  23243         const vec_len = simd_op.len orelse {
  23244             const result_val = opFunc(cur_val, operand_val, mod);
  23245             cur_minmax = try sema.addConstant(result_val);
  23246             continue;
  23247         };
  23248         const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  23249         for (elems, 0..) |*elem, i| {
  23250             const lhs_elem_val = try cur_val.elemValue(mod, i);
  23251             const rhs_elem_val = try operand_val.elemValue(mod, i);
  23252             const uncoerced_elem = opFunc(lhs_elem_val, rhs_elem_val, mod);
  23253             elem.* = (try mod.getCoerced(uncoerced_elem, simd_op.scalar_ty)).toIntern();
  23254         }
  23255         cur_minmax = try sema.addConstant((try mod.intern(.{ .aggregate = .{
  23256             .ty = simd_op.result_ty.toIntern(),
  23257             .storage = .{ .elems = elems },
  23258         } })).toValue());
  23259     }
  23260 
  23261     const opt_runtime_idx = runtime_known.findFirstSet();
  23262 
  23263     if (cur_minmax) |ct_minmax_ref| refine: {
  23264         // Refine the comptime-known result type based on the bounds. This isn't strictly necessary
  23265         // in the runtime case, since we'll refine the type again later, but keeping things as small
  23266         // as possible will allow us to emit more optimal AIR (if all the runtime operands have
  23267         // smaller types than the non-refined comptime type).
  23268 
  23269         const val = (try sema.resolveMaybeUndefVal(ct_minmax_ref)).?;
  23270         const orig_ty = sema.typeOf(ct_minmax_ref);
  23271 
  23272         if (opt_runtime_idx == null and orig_ty.scalarType(mod).eql(Type.comptime_int, mod)) {
  23273             // If all arguments were `comptime_int`, and there are no runtime args, we'll preserve that type
  23274             break :refine;
  23275         }
  23276 
  23277         // We can't refine float types
  23278         if (orig_ty.scalarType(mod).isAnyFloat()) break :refine;
  23279 
  23280         assert(bounds_status == .defined); // there was a non-comptime-int integral comptime-known arg
  23281 
  23282         const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar);
  23283         const refined_ty = if (orig_ty.isVector(mod)) try mod.vectorType(.{
  23284             .len = orig_ty.vectorLen(mod),
  23285             .child = refined_scalar_ty.toIntern(),
  23286         }) else refined_scalar_ty;
  23287 
  23288         // Apply the refined type to the current value
  23289         if (std.debug.runtime_safety) {
  23290             assert(try sema.intFitsInType(val, refined_ty, null));
  23291         }
  23292         cur_minmax = try sema.coerceInMemory(val, refined_ty);
  23293     }
  23294 
  23295     const runtime_idx = opt_runtime_idx orelse return cur_minmax.?;
  23296     const runtime_src = operand_srcs[runtime_idx];
  23297     try sema.requireRuntimeBlock(block, src, runtime_src);
  23298 
  23299     // Now, iterate over runtime operands, emitting a min/max instruction for each. We'll refine the
  23300     // type again at the end, based on the comptime-known bound.
  23301 
  23302     // If the comptime-known part is undef we can avoid emitting actual instructions later
  23303     const known_undef = if (cur_minmax) |operand| blk: {
  23304         const val = (try sema.resolveMaybeUndefVal(operand)).?;
  23305         break :blk val.isUndef(mod);
  23306     } else false;
  23307 
  23308     if (cur_minmax == null) {
  23309         // No comptime operands - use the first operand as the starting value
  23310         assert(bounds_status == .unknown);
  23311         assert(runtime_idx == 0);
  23312         cur_minmax = operands[0];
  23313         cur_minmax_src = runtime_src;
  23314         runtime_known.unset(0); // don't look at this operand in the loop below
  23315         const scalar_ty = sema.typeOf(cur_minmax.?).scalarType(mod);
  23316         if (scalar_ty.isInt(mod)) {
  23317             cur_min_scalar = try scalar_ty.minInt(mod, scalar_ty);
  23318             cur_max_scalar = try scalar_ty.maxInt(mod, scalar_ty);
  23319             bounds_status = .defined;
  23320         } else {
  23321             bounds_status = .non_integral;
  23322         }
  23323     }
  23324 
  23325     var it = runtime_known.iterator(.{});
  23326     while (it.next()) |idx| {
  23327         const lhs = cur_minmax.?;
  23328         const lhs_src = cur_minmax_src;
  23329         const rhs = operands[idx];
  23330         const rhs_src = operand_srcs[idx];
  23331         const simd_op = try sema.checkSimdBinOp(block, src, lhs, rhs, lhs_src, rhs_src);
  23332         if (known_undef) {
  23333             cur_minmax = try sema.addConstUndef(simd_op.result_ty);
  23334         } else {
  23335             cur_minmax = try block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs);
  23336         }
  23337         // Compute the bounds of this type
  23338         switch (bounds_status) {
  23339             .unknown, .defined => refine_bounds: {
  23340                 const scalar_ty = sema.typeOf(rhs).scalarType(mod);
  23341                 if (scalar_ty.isAnyFloat()) {
  23342                     bounds_status = .non_integral;
  23343                     break :refine_bounds;
  23344                 }
  23345                 const scalar_min = try scalar_ty.minInt(mod, scalar_ty);
  23346                 const scalar_max = try scalar_ty.maxInt(mod, scalar_ty);
  23347                 if (bounds_status == .unknown) {
  23348                     cur_min_scalar = scalar_min;
  23349                     cur_max_scalar = scalar_max;
  23350                     bounds_status = .defined;
  23351                 } else {
  23352                     cur_min_scalar = opFunc(cur_min_scalar, scalar_min, mod);
  23353                     cur_max_scalar = opFunc(cur_max_scalar, scalar_max, mod);
  23354                 }
  23355             },
  23356             .non_integral => {},
  23357         }
  23358     }
  23359 
  23360     // Finally, refine the type based on the known bounds.
  23361     const unrefined_ty = sema.typeOf(cur_minmax.?);
  23362     if (unrefined_ty.scalarType(mod).isAnyFloat()) {
  23363         // We can't refine floats, so we're done.
  23364         return cur_minmax.?;
  23365     }
  23366     assert(bounds_status == .defined); // there were integral runtime operands
  23367     const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar);
  23368     const refined_ty = if (unrefined_ty.isVector(mod)) try mod.vectorType(.{
  23369         .len = unrefined_ty.vectorLen(mod),
  23370         .child = refined_scalar_ty.toIntern(),
  23371     }) else refined_scalar_ty;
  23372 
  23373     if (!refined_ty.eql(unrefined_ty, mod)) {
  23374         // We've reduced the type - cast the result down
  23375         return block.addTyOp(.intcast, refined_ty, cur_minmax.?);
  23376     }
  23377 
  23378     return cur_minmax.?;
  23379 }
  23380 
  23381 fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref {
  23382     const mod = sema.mod;
  23383     const info = sema.typeOf(ptr).ptrInfo(mod);
  23384     if (info.flags.size == .One) {
  23385         // Already an array pointer.
  23386         return ptr;
  23387     }
  23388     const new_ty = try mod.ptrType(.{
  23389         .child = (try mod.arrayType(.{
  23390             .len = len,
  23391             .sentinel = info.sentinel,
  23392             .child = info.child,
  23393         })).toIntern(),
  23394         .flags = .{
  23395             .alignment = info.flags.alignment,
  23396             .is_const = info.flags.is_const,
  23397             .is_volatile = info.flags.is_volatile,
  23398             .is_allowzero = info.flags.is_allowzero,
  23399             .address_space = info.flags.address_space,
  23400         },
  23401     });
  23402     if (info.flags.size == .Slice) {
  23403         return block.addTyOp(.slice_ptr, new_ty, ptr);
  23404     }
  23405     return block.addBitCast(new_ty, ptr);
  23406 }
  23407 
  23408 fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  23409     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  23410     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23411     const src = inst_data.src();
  23412     const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23413     const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23414     const dest_ptr = try sema.resolveInst(extra.lhs);
  23415     const src_ptr = try sema.resolveInst(extra.rhs);
  23416     const dest_ty = sema.typeOf(dest_ptr);
  23417     const src_ty = sema.typeOf(src_ptr);
  23418     const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr);
  23419     const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr);
  23420     const target = sema.mod.getTarget();
  23421     const mod = sema.mod;
  23422 
  23423     if (dest_ty.isConstPtr(mod)) {
  23424         return sema.fail(block, dest_src, "cannot memcpy to constant pointer", .{});
  23425     }
  23426 
  23427     if (dest_len == .none and src_len == .none) {
  23428         const msg = msg: {
  23429             const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{});
  23430             errdefer msg.destroy(sema.gpa);
  23431             try sema.errNote(block, dest_src, msg, "destination type '{}' provides no length", .{
  23432                 dest_ty.fmt(sema.mod),
  23433             });
  23434             try sema.errNote(block, src_src, msg, "source type '{}' provides no length", .{
  23435                 src_ty.fmt(sema.mod),
  23436             });
  23437             break :msg msg;
  23438         };
  23439         return sema.failWithOwnedErrorMsg(msg);
  23440     }
  23441 
  23442     var len_val: ?Value = null;
  23443 
  23444     if (dest_len != .none and src_len != .none) check: {
  23445         // If we can check at compile-time, no need for runtime safety.
  23446         if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
  23447             len_val = dest_len_val;
  23448             if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  23449                 if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) {
  23450                     const msg = msg: {
  23451                         const msg = try sema.errMsg(block, src, "non-matching @memcpy lengths", .{});
  23452                         errdefer msg.destroy(sema.gpa);
  23453                         try sema.errNote(block, dest_src, msg, "length {} here", .{
  23454                             dest_len_val.fmtValue(Type.usize, sema.mod),
  23455                         });
  23456                         try sema.errNote(block, src_src, msg, "length {} here", .{
  23457                             src_len_val.fmtValue(Type.usize, sema.mod),
  23458                         });
  23459                         break :msg msg;
  23460                     };
  23461                     return sema.failWithOwnedErrorMsg(msg);
  23462                 }
  23463                 break :check;
  23464             }
  23465         } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  23466             len_val = src_len_val;
  23467         }
  23468 
  23469         if (block.wantSafety()) {
  23470             const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);
  23471             try sema.addSafetyCheck(block, ok, .memcpy_len_mismatch);
  23472         }
  23473     } else if (dest_len != .none) {
  23474         if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
  23475             len_val = dest_len_val;
  23476         }
  23477     } else if (src_len != .none) {
  23478         if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  23479             len_val = src_len_val;
  23480         }
  23481     }
  23482 
  23483     const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: {
  23484         if (!dest_ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src;
  23485         if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |_| {
  23486             const len_u64 = (try len_val.?.getUnsignedIntAdvanced(mod, sema)).?;
  23487             const len = try sema.usizeCast(block, dest_src, len_u64);
  23488             for (0..len) |i| {
  23489                 const elem_index = try sema.addIntUnsigned(Type.usize, i);
  23490                 const dest_elem_ptr = try sema.elemPtrOneLayerOnly(
  23491                     block,
  23492                     src,
  23493                     dest_ptr,
  23494                     elem_index,
  23495                     src,
  23496                     true, // init
  23497                     false, // oob_safety
  23498                 );
  23499                 const src_elem_ptr = try sema.elemPtrOneLayerOnly(
  23500                     block,
  23501                     src,
  23502                     src_ptr,
  23503                     elem_index,
  23504                     src,
  23505                     false, // init
  23506                     false, // oob_safety
  23507                 );
  23508                 const uncoerced_elem = try sema.analyzeLoad(block, src, src_elem_ptr, src_src);
  23509                 try sema.storePtr2(
  23510                     block,
  23511                     src,
  23512                     dest_elem_ptr,
  23513                     dest_src,
  23514                     uncoerced_elem,
  23515                     src_src,
  23516                     .store,
  23517                 );
  23518             }
  23519             return;
  23520         } else break :rs src_src;
  23521     } else dest_src;
  23522 
  23523     // If in-memory coercion is not allowed, explode this memcpy call into a
  23524     // for loop that copies element-wise.
  23525     // Likewise if this is an iterable rather than a pointer, do the same
  23526     // lowering. The AIR instruction requires pointers with element types of
  23527     // equal ABI size.
  23528 
  23529     if (dest_ty.zigTypeTag(mod) != .Pointer or src_ty.zigTypeTag(mod) != .Pointer) {
  23530         return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{});
  23531     }
  23532 
  23533     const dest_elem_ty = dest_ty.elemType2(mod);
  23534     const src_elem_ty = src_ty.elemType2(mod);
  23535     if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) {
  23536         return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{});
  23537     }
  23538 
  23539     // If the length is comptime-known, then upgrade src and destination types
  23540     // into pointer-to-array. At this point we know they are both pointers
  23541     // already.
  23542     var new_dest_ptr = dest_ptr;
  23543     var new_src_ptr = src_ptr;
  23544     if (len_val) |val| {
  23545         const len = val.toUnsignedInt(mod);
  23546         if (len == 0) {
  23547             // This AIR instruction guarantees length > 0 if it is comptime-known.
  23548             return;
  23549         }
  23550         new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len);
  23551         new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);
  23552     }
  23553 
  23554     if (dest_len != .none) {
  23555         // Change the src from slice to a many pointer, to avoid multiple ptr
  23556         // slice extractions in AIR instructions.
  23557         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  23558         if (new_src_ptr_ty.isSlice(mod)) {
  23559             new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
  23560         }
  23561     } else if (dest_len == .none and len_val == null) {
  23562         // Change the dest to a slice, since its type must have the length.
  23563         const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr);
  23564         new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, .unneeded, dest_src, dest_src, dest_src, false);
  23565         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  23566         if (new_src_ptr_ty.isSlice(mod)) {
  23567             new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
  23568         }
  23569     }
  23570 
  23571     try sema.requireRuntimeBlock(block, src, runtime_src);
  23572 
  23573     // Aliasing safety check.
  23574     if (block.wantSafety()) {
  23575         const len = if (len_val) |v|
  23576             try sema.addConstant(v)
  23577         else if (dest_len != .none)
  23578             dest_len
  23579         else
  23580             src_len;
  23581 
  23582         // Extract raw pointer from dest slice. The AIR instructions could support them, but
  23583         // it would cause redundant machine code instructions.
  23584         const new_dest_ptr_ty = sema.typeOf(new_dest_ptr);
  23585         const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(mod))
  23586             try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty)
  23587         else if (new_dest_ptr_ty.ptrSize(mod) == .One) ptr: {
  23588             var dest_manyptr_ty_key = mod.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type;
  23589             assert(dest_manyptr_ty_key.flags.size == .One);
  23590             dest_manyptr_ty_key.child = dest_elem_ty.toIntern();
  23591             dest_manyptr_ty_key.flags.size = .Many;
  23592             break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(dest_manyptr_ty_key), new_dest_ptr, dest_src);
  23593         } else new_dest_ptr;
  23594 
  23595         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  23596         const raw_src_ptr = if (new_src_ptr_ty.isSlice(mod))
  23597             try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty)
  23598         else if (new_src_ptr_ty.ptrSize(mod) == .One) ptr: {
  23599             var src_manyptr_ty_key = mod.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type;
  23600             assert(src_manyptr_ty_key.flags.size == .One);
  23601             src_manyptr_ty_key.child = src_elem_ty.toIntern();
  23602             src_manyptr_ty_key.flags.size = .Many;
  23603             break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(src_manyptr_ty_key), new_src_ptr, src_src);
  23604         } else new_src_ptr;
  23605 
  23606         // ok1: dest >= src + len
  23607         // ok2: src >= dest + len
  23608         const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src);
  23609         const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src);
  23610         const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
  23611         const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
  23612         const ok = try block.addBinOp(.bit_or, ok1, ok2);
  23613         try sema.addSafetyCheck(block, ok, .memcpy_alias);
  23614     }
  23615 
  23616     _ = try block.addInst(.{
  23617         .tag = .memcpy,
  23618         .data = .{ .bin_op = .{
  23619             .lhs = new_dest_ptr,
  23620             .rhs = new_src_ptr,
  23621         } },
  23622     });
  23623 }
  23624 
  23625 fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  23626     const mod = sema.mod;
  23627     const gpa = sema.gpa;
  23628     const ip = &mod.intern_pool;
  23629     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  23630     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23631     const src = inst_data.src();
  23632     const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23633     const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23634     const dest_ptr = try sema.resolveInst(extra.lhs);
  23635     const uncoerced_elem = try sema.resolveInst(extra.rhs);
  23636     const dest_ptr_ty = sema.typeOf(dest_ptr);
  23637     try checkMemOperand(sema, block, dest_src, dest_ptr_ty);
  23638 
  23639     if (dest_ptr_ty.isConstPtr(mod)) {
  23640         return sema.fail(block, dest_src, "cannot memset constant pointer", .{});
  23641     }
  23642 
  23643     const dest_elem_ty = dest_ptr_ty.elemType2(mod);
  23644 
  23645     const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: {
  23646         const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, "len"), dest_src);
  23647         const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse
  23648             break :rs dest_src;
  23649         const len_u64 = (try len_val.getUnsignedIntAdvanced(mod, sema)).?;
  23650         const len = try sema.usizeCast(block, dest_src, len_u64);
  23651         if (len == 0) {
  23652             // This AIR instruction guarantees length > 0 if it is comptime-known.
  23653             return;
  23654         }
  23655 
  23656         if (!ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src;
  23657         if (try sema.resolveMaybeUndefVal(uncoerced_elem)) |_| {
  23658             for (0..len) |i| {
  23659                 const elem_index = try sema.addIntUnsigned(Type.usize, i);
  23660                 const elem_ptr = try sema.elemPtrOneLayerOnly(
  23661                     block,
  23662                     src,
  23663                     dest_ptr,
  23664                     elem_index,
  23665                     src,
  23666                     true, // init
  23667                     false, // oob_safety
  23668                 );
  23669                 try sema.storePtr2(
  23670                     block,
  23671                     src,
  23672                     elem_ptr,
  23673                     dest_src,
  23674                     uncoerced_elem,
  23675                     value_src,
  23676                     .store,
  23677                 );
  23678             }
  23679             return;
  23680         } else break :rs value_src;
  23681     } else dest_src;
  23682 
  23683     const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src);
  23684 
  23685     try sema.requireRuntimeBlock(block, src, runtime_src);
  23686     _ = try block.addInst(.{
  23687         .tag = if (block.wantSafety()) .memset_safe else .memset,
  23688         .data = .{ .bin_op = .{
  23689             .lhs = dest_ptr,
  23690             .rhs = elem,
  23691         } },
  23692     });
  23693 }
  23694 
  23695 fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  23696     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  23697     const src = LazySrcLoc.nodeOffset(extra.node);
  23698     return sema.failWithUseOfAsync(block, src);
  23699 }
  23700 
  23701 fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23702     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  23703     const src = inst_data.src();
  23704     return sema.failWithUseOfAsync(block, src);
  23705 }
  23706 
  23707 fn zirAwait(
  23708     sema: *Sema,
  23709     block: *Block,
  23710     inst: Zir.Inst.Index,
  23711 ) CompileError!Air.Inst.Ref {
  23712     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  23713     const src = inst_data.src();
  23714 
  23715     return sema.failWithUseOfAsync(block, src);
  23716 }
  23717 
  23718 fn zirAwaitNosuspend(
  23719     sema: *Sema,
  23720     block: *Block,
  23721     extended: Zir.Inst.Extended.InstData,
  23722 ) CompileError!Air.Inst.Ref {
  23723     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  23724     const src = LazySrcLoc.nodeOffset(extra.node);
  23725 
  23726     return sema.failWithUseOfAsync(block, src);
  23727 }
  23728 
  23729 fn zirVarExtended(
  23730     sema: *Sema,
  23731     block: *Block,
  23732     extended: Zir.Inst.Extended.InstData,
  23733 ) CompileError!Air.Inst.Ref {
  23734     const mod = sema.mod;
  23735     const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
  23736     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 };
  23737     const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 };
  23738     const small = @as(Zir.Inst.ExtendedVar.Small, @bitCast(extended.small));
  23739 
  23740     var extra_index: usize = extra.end;
  23741 
  23742     const lib_name: ?[]const u8 = if (small.has_lib_name) blk: {
  23743         const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
  23744         extra_index += 1;
  23745         break :blk lib_name;
  23746     } else null;
  23747 
  23748     // ZIR supports encoding this information but it is not used; the information
  23749     // is encoded via the Decl entry.
  23750     assert(!small.has_align);
  23751 
  23752     const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: {
  23753         const init_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23754         extra_index += 1;
  23755         break :blk try sema.resolveInst(init_ref);
  23756     } else .none;
  23757 
  23758     const have_ty = extra.data.var_type != .none;
  23759     const var_ty = if (have_ty)
  23760         try sema.resolveType(block, ty_src, extra.data.var_type)
  23761     else
  23762         sema.typeOf(uncasted_init);
  23763 
  23764     const init_val = if (uncasted_init != .none) blk: {
  23765         const init = if (have_ty)
  23766             try sema.coerce(block, var_ty, uncasted_init, init_src)
  23767         else
  23768             uncasted_init;
  23769 
  23770         break :blk ((try sema.resolveMaybeUndefVal(init)) orelse
  23771             return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime-known")).toIntern();
  23772     } else .none;
  23773 
  23774     try sema.validateVarType(block, ty_src, var_ty, small.is_extern);
  23775 
  23776     return sema.addConstant((try mod.intern(.{ .variable = .{
  23777         .ty = var_ty.toIntern(),
  23778         .init = init_val,
  23779         .decl = sema.owner_decl_index,
  23780         .lib_name = if (lib_name) |lname| (try mod.intern_pool.getOrPutString(
  23781             sema.gpa,
  23782             try sema.handleExternLibName(block, ty_src, lname),
  23783         )).toOptional() else .none,
  23784         .is_extern = small.is_extern,
  23785         .is_threadlocal = small.is_threadlocal,
  23786     } })).toValue());
  23787 }
  23788 
  23789 fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23790     const tracy = trace(@src());
  23791     defer tracy.end();
  23792 
  23793     const mod = sema.mod;
  23794     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  23795     const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
  23796     const target = mod.getTarget();
  23797 
  23798     const align_src: LazySrcLoc = .{ .node_offset_fn_type_align = inst_data.src_node };
  23799     const addrspace_src: LazySrcLoc = .{ .node_offset_fn_type_addrspace = inst_data.src_node };
  23800     const section_src: LazySrcLoc = .{ .node_offset_fn_type_section = inst_data.src_node };
  23801     const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node };
  23802     const ret_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node };
  23803     const has_body = extra.data.body_len != 0;
  23804 
  23805     var extra_index: usize = extra.end;
  23806 
  23807     const lib_name: ?[]const u8 = if (extra.data.bits.has_lib_name) blk: {
  23808         const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
  23809         extra_index += 1;
  23810         break :blk lib_name;
  23811     } else null;
  23812 
  23813     if (has_body and
  23814         (extra.data.bits.has_align_body or extra.data.bits.has_align_ref) and
  23815         !target_util.supportsFunctionAlignment(target))
  23816     {
  23817         return sema.fail(block, align_src, "target does not support function alignment", .{});
  23818     }
  23819 
  23820     const @"align": ?Alignment = if (extra.data.bits.has_align_body) blk: {
  23821         const body_len = sema.code.extra[extra_index];
  23822         extra_index += 1;
  23823         const body = sema.code.extra[extra_index..][0..body_len];
  23824         extra_index += body.len;
  23825 
  23826         const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29, "alignment must be comptime-known");
  23827         if (val.isGenericPoison()) {
  23828             break :blk null;
  23829         }
  23830         const alignment = @as(u32, @intCast(val.toUnsignedInt(mod)));
  23831         try sema.validateAlign(block, align_src, alignment);
  23832         if (alignment == target_util.defaultFunctionAlignment(target)) {
  23833             break :blk .none;
  23834         } else {
  23835             break :blk Alignment.fromNonzeroByteUnits(alignment);
  23836         }
  23837     } else if (extra.data.bits.has_align_ref) blk: {
  23838         const align_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23839         extra_index += 1;
  23840         const align_tv = sema.resolveInstConst(block, align_src, align_ref, "alignment must be comptime-known") catch |err| switch (err) {
  23841             error.GenericPoison => {
  23842                 break :blk null;
  23843             },
  23844             else => |e| return e,
  23845         };
  23846         const alignment = @as(u32, @intCast(align_tv.val.toUnsignedInt(mod)));
  23847         try sema.validateAlign(block, align_src, alignment);
  23848         if (alignment == target_util.defaultFunctionAlignment(target)) {
  23849             break :blk .none;
  23850         } else {
  23851             break :blk Alignment.fromNonzeroByteUnits(alignment);
  23852         }
  23853     } else .none;
  23854 
  23855     const @"addrspace": ?std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: {
  23856         const body_len = sema.code.extra[extra_index];
  23857         extra_index += 1;
  23858         const body = sema.code.extra[extra_index..][0..body_len];
  23859         extra_index += body.len;
  23860 
  23861         const addrspace_ty = try sema.getBuiltinType("AddressSpace");
  23862         const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty, "addrespace must be comptime-known");
  23863         if (val.isGenericPoison()) {
  23864             break :blk null;
  23865         }
  23866         break :blk mod.toEnum(std.builtin.AddressSpace, val);
  23867     } else if (extra.data.bits.has_addrspace_ref) blk: {
  23868         const addrspace_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23869         extra_index += 1;
  23870         const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref, "addrespace must be comptime-known") catch |err| switch (err) {
  23871             error.GenericPoison => {
  23872                 break :blk null;
  23873             },
  23874             else => |e| return e,
  23875         };
  23876         break :blk mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val);
  23877     } else target_util.defaultAddressSpace(target, .function);
  23878 
  23879     const @"linksection": FuncLinkSection = if (extra.data.bits.has_section_body) blk: {
  23880         const body_len = sema.code.extra[extra_index];
  23881         extra_index += 1;
  23882         const body = sema.code.extra[extra_index..][0..body_len];
  23883         extra_index += body.len;
  23884 
  23885         const ty = Type.slice_const_u8;
  23886         const val = try sema.resolveGenericBody(block, section_src, body, inst, ty, "linksection must be comptime-known");
  23887         if (val.isGenericPoison()) {
  23888             break :blk FuncLinkSection{ .generic = {} };
  23889         }
  23890         break :blk FuncLinkSection{ .explicit = try val.toIpString(ty, mod) };
  23891     } else if (extra.data.bits.has_section_ref) blk: {
  23892         const section_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23893         extra_index += 1;
  23894         const section_name = sema.resolveConstStringIntern(block, section_src, section_ref, "linksection must be comptime-known") catch |err| switch (err) {
  23895             error.GenericPoison => {
  23896                 break :blk FuncLinkSection{ .generic = {} };
  23897             },
  23898             else => |e| return e,
  23899         };
  23900         break :blk FuncLinkSection{ .explicit = section_name };
  23901     } else FuncLinkSection{ .default = {} };
  23902 
  23903     const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
  23904         const body_len = sema.code.extra[extra_index];
  23905         extra_index += 1;
  23906         const body = sema.code.extra[extra_index..][0..body_len];
  23907         extra_index += body.len;
  23908 
  23909         const cc_ty = try sema.getBuiltinType("CallingConvention");
  23910         const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, "calling convention must be comptime-known");
  23911         if (val.isGenericPoison()) {
  23912             break :blk null;
  23913         }
  23914         break :blk mod.toEnum(std.builtin.CallingConvention, val);
  23915     } else if (extra.data.bits.has_cc_ref) blk: {
  23916         const cc_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23917         extra_index += 1;
  23918         const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref, "calling convention must be comptime-known") catch |err| switch (err) {
  23919             error.GenericPoison => {
  23920                 break :blk null;
  23921             },
  23922             else => |e| return e,
  23923         };
  23924         break :blk mod.toEnum(std.builtin.CallingConvention, cc_tv.val);
  23925     } else if (sema.owner_decl.is_exported and has_body)
  23926         .C
  23927     else
  23928         .Unspecified;
  23929 
  23930     const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
  23931         const body_len = sema.code.extra[extra_index];
  23932         extra_index += 1;
  23933         const body = sema.code.extra[extra_index..][0..body_len];
  23934         extra_index += body.len;
  23935 
  23936         const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type, "return type must be comptime-known");
  23937         const ty = val.toType();
  23938         break :blk ty;
  23939     } else if (extra.data.bits.has_ret_ty_ref) blk: {
  23940         const ret_ty_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23941         extra_index += 1;
  23942         const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref, "return type must be comptime-known") catch |err| switch (err) {
  23943             error.GenericPoison => {
  23944                 break :blk Type.generic_poison;
  23945             },
  23946             else => |e| return e,
  23947         };
  23948         const ty = ret_ty_tv.val.toType();
  23949         break :blk ty;
  23950     } else Type.void;
  23951 
  23952     const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: {
  23953         const x = sema.code.extra[extra_index];
  23954         extra_index += 1;
  23955         break :blk x;
  23956     } else 0;
  23957 
  23958     var src_locs: Zir.Inst.Func.SrcLocs = undefined;
  23959     if (has_body) {
  23960         extra_index += extra.data.body_len;
  23961         src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
  23962     }
  23963 
  23964     const is_var_args = extra.data.bits.is_var_args;
  23965     const is_inferred_error = extra.data.bits.is_inferred_error;
  23966     const is_extern = extra.data.bits.is_extern;
  23967     const is_noinline = extra.data.bits.is_noinline;
  23968 
  23969     return sema.funcCommon(
  23970         block,
  23971         inst_data.src_node,
  23972         inst,
  23973         @"align",
  23974         @"addrspace",
  23975         @"linksection",
  23976         cc,
  23977         ret_ty,
  23978         is_var_args,
  23979         is_inferred_error,
  23980         is_extern,
  23981         has_body,
  23982         src_locs,
  23983         lib_name,
  23984         noalias_bits,
  23985         is_noinline,
  23986     );
  23987 }
  23988 
  23989 fn zirCUndef(
  23990     sema: *Sema,
  23991     block: *Block,
  23992     extended: Zir.Inst.Extended.InstData,
  23993 ) CompileError!Air.Inst.Ref {
  23994     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  23995     const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  23996 
  23997     const name = try sema.resolveConstString(block, src, extra.operand, "name of macro being undefined must be comptime-known");
  23998     try block.c_import_buf.?.writer().print("#undef {s}\n", .{name});
  23999     return Air.Inst.Ref.void_value;
  24000 }
  24001 
  24002 fn zirCInclude(
  24003     sema: *Sema,
  24004     block: *Block,
  24005     extended: Zir.Inst.Extended.InstData,
  24006 ) CompileError!Air.Inst.Ref {
  24007     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  24008     const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24009 
  24010     const name = try sema.resolveConstString(block, src, extra.operand, "path being included must be comptime-known");
  24011     try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name});
  24012     return Air.Inst.Ref.void_value;
  24013 }
  24014 
  24015 fn zirCDefine(
  24016     sema: *Sema,
  24017     block: *Block,
  24018     extended: Zir.Inst.Extended.InstData,
  24019 ) CompileError!Air.Inst.Ref {
  24020     const mod = sema.mod;
  24021     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  24022     const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24023     const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  24024 
  24025     const name = try sema.resolveConstString(block, name_src, extra.lhs, "name of macro being undefined must be comptime-known");
  24026     const rhs = try sema.resolveInst(extra.rhs);
  24027     if (sema.typeOf(rhs).zigTypeTag(mod) != .Void) {
  24028         const value = try sema.resolveConstString(block, val_src, extra.rhs, "value of macro being undefined must be comptime-known");
  24029         try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value });
  24030     } else {
  24031         try block.c_import_buf.?.writer().print("#define {s}\n", .{name});
  24032     }
  24033     return Air.Inst.Ref.void_value;
  24034 }
  24035 
  24036 fn zirWasmMemorySize(
  24037     sema: *Sema,
  24038     block: *Block,
  24039     extended: Zir.Inst.Extended.InstData,
  24040 ) CompileError!Air.Inst.Ref {
  24041     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  24042     const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24043     const builtin_src = LazySrcLoc.nodeOffset(extra.node);
  24044     const target = sema.mod.getTarget();
  24045     if (!target.isWasm()) {
  24046         return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
  24047     }
  24048 
  24049     const index = @as(u32, @intCast(try sema.resolveInt(block, index_src, extra.operand, Type.u32, "wasm memory size index must be comptime-known")));
  24050     try sema.requireRuntimeBlock(block, builtin_src, null);
  24051     return block.addInst(.{
  24052         .tag = .wasm_memory_size,
  24053         .data = .{ .pl_op = .{
  24054             .operand = .none,
  24055             .payload = index,
  24056         } },
  24057     });
  24058 }
  24059 
  24060 fn zirWasmMemoryGrow(
  24061     sema: *Sema,
  24062     block: *Block,
  24063     extended: Zir.Inst.Extended.InstData,
  24064 ) CompileError!Air.Inst.Ref {
  24065     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  24066     const builtin_src = LazySrcLoc.nodeOffset(extra.node);
  24067     const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24068     const delta_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  24069     const target = sema.mod.getTarget();
  24070     if (!target.isWasm()) {
  24071         return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
  24072     }
  24073 
  24074     const index = @as(u32, @intCast(try sema.resolveInt(block, index_src, extra.lhs, Type.u32, "wasm memory size index must be comptime-known")));
  24075     const delta = try sema.coerce(block, Type.u32, try sema.resolveInst(extra.rhs), delta_src);
  24076 
  24077     try sema.requireRuntimeBlock(block, builtin_src, null);
  24078     return block.addInst(.{
  24079         .tag = .wasm_memory_grow,
  24080         .data = .{ .pl_op = .{
  24081             .operand = delta,
  24082             .payload = index,
  24083         } },
  24084     });
  24085 }
  24086 
  24087 fn resolvePrefetchOptions(
  24088     sema: *Sema,
  24089     block: *Block,
  24090     src: LazySrcLoc,
  24091     zir_ref: Zir.Inst.Ref,
  24092 ) CompileError!std.builtin.PrefetchOptions {
  24093     const mod = sema.mod;
  24094     const gpa = sema.gpa;
  24095     const ip = &mod.intern_pool;
  24096     const options_ty = try sema.getBuiltinType("PrefetchOptions");
  24097     const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src);
  24098 
  24099     const rw_src = sema.maybeOptionsSrc(block, src, "rw");
  24100     const locality_src = sema.maybeOptionsSrc(block, src, "locality");
  24101     const cache_src = sema.maybeOptionsSrc(block, src, "cache");
  24102 
  24103     const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "rw"), rw_src);
  24104     const rw_val = try sema.resolveConstValue(block, rw_src, rw, "prefetch read/write must be comptime-known");
  24105 
  24106     const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "locality"), locality_src);
  24107     const locality_val = try sema.resolveConstValue(block, locality_src, locality, "prefetch locality must be comptime-known");
  24108 
  24109     const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "cache"), cache_src);
  24110     const cache_val = try sema.resolveConstValue(block, cache_src, cache, "prefetch cache must be comptime-known");
  24111 
  24112     return std.builtin.PrefetchOptions{
  24113         .rw = mod.toEnum(std.builtin.PrefetchOptions.Rw, rw_val),
  24114         .locality = @as(u2, @intCast(locality_val.toUnsignedInt(mod))),
  24115         .cache = mod.toEnum(std.builtin.PrefetchOptions.Cache, cache_val),
  24116     };
  24117 }
  24118 
  24119 fn zirPrefetch(
  24120     sema: *Sema,
  24121     block: *Block,
  24122     extended: Zir.Inst.Extended.InstData,
  24123 ) CompileError!Air.Inst.Ref {
  24124     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  24125     const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24126     const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  24127     const ptr = try sema.resolveInst(extra.lhs);
  24128     try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr));
  24129 
  24130     const options = sema.resolvePrefetchOptions(block, .unneeded, extra.rhs) catch |err| switch (err) {
  24131         error.NeededSourceLocation => {
  24132             _ = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs);
  24133             unreachable;
  24134         },
  24135         else => |e| return e,
  24136     };
  24137 
  24138     if (!block.is_comptime) {
  24139         _ = try block.addInst(.{
  24140             .tag = .prefetch,
  24141             .data = .{ .prefetch = .{
  24142                 .ptr = ptr,
  24143                 .rw = options.rw,
  24144                 .locality = options.locality,
  24145                 .cache = options.cache,
  24146             } },
  24147         });
  24148     }
  24149 
  24150     return Air.Inst.Ref.void_value;
  24151 }
  24152 
  24153 fn resolveExternOptions(
  24154     sema: *Sema,
  24155     block: *Block,
  24156     src: LazySrcLoc,
  24157     zir_ref: Zir.Inst.Ref,
  24158 ) CompileError!struct {
  24159     name: InternPool.NullTerminatedString,
  24160     library_name: InternPool.OptionalNullTerminatedString = .none,
  24161     linkage: std.builtin.GlobalLinkage = .Strong,
  24162     is_thread_local: bool = false,
  24163 } {
  24164     const mod = sema.mod;
  24165     const gpa = sema.gpa;
  24166     const ip = &mod.intern_pool;
  24167     const options_inst = try sema.resolveInst(zir_ref);
  24168     const extern_options_ty = try sema.getBuiltinType("ExternOptions");
  24169     const options = try sema.coerce(block, extern_options_ty, options_inst, src);
  24170 
  24171     const name_src = sema.maybeOptionsSrc(block, src, "name");
  24172     const library_src = sema.maybeOptionsSrc(block, src, "library");
  24173     const linkage_src = sema.maybeOptionsSrc(block, src, "linkage");
  24174     const thread_local_src = sema.maybeOptionsSrc(block, src, "thread_local");
  24175 
  24176     const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src);
  24177     const name_val = try sema.resolveConstValue(block, name_src, name_ref, "name of the extern symbol must be comptime-known");
  24178     const name = try name_val.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod);
  24179 
  24180     const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "library_name"), library_src);
  24181     const library_name_val = try sema.resolveConstValue(block, library_src, library_name_inst, "library in which extern symbol is must be comptime-known");
  24182 
  24183     const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src);
  24184     const linkage_val = try sema.resolveConstValue(block, linkage_src, linkage_ref, "linkage of the extern symbol must be comptime-known");
  24185     const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val);
  24186 
  24187     const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "is_thread_local"), thread_local_src);
  24188     const is_thread_local_val = try sema.resolveConstValue(block, thread_local_src, is_thread_local, "threadlocality of the extern symbol must be comptime-known");
  24189 
  24190     const library_name = if (library_name_val.optionalValue(mod)) |payload| blk: {
  24191         const library_name = try payload.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod);
  24192         if (library_name.len == 0) {
  24193             return sema.fail(block, library_src, "library name cannot be empty", .{});
  24194         }
  24195         break :blk try sema.handleExternLibName(block, library_src, library_name);
  24196     } else null;
  24197 
  24198     if (name.len == 0) {
  24199         return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
  24200     }
  24201 
  24202     if (linkage != .Weak and linkage != .Strong) {
  24203         return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{});
  24204     }
  24205 
  24206     return .{
  24207         .name = try ip.getOrPutString(gpa, name),
  24208         .library_name = try ip.getOrPutStringOpt(gpa, library_name),
  24209         .linkage = linkage,
  24210         .is_thread_local = is_thread_local_val.toBool(),
  24211     };
  24212 }
  24213 
  24214 fn zirBuiltinExtern(
  24215     sema: *Sema,
  24216     block: *Block,
  24217     extended: Zir.Inst.Extended.InstData,
  24218 ) CompileError!Air.Inst.Ref {
  24219     const mod = sema.mod;
  24220     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  24221     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24222     const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  24223 
  24224     var ty = try sema.resolveType(block, ty_src, extra.lhs);
  24225     if (!ty.isPtrAtRuntime(mod)) {
  24226         return sema.fail(block, ty_src, "expected (optional) pointer", .{});
  24227     }
  24228     if (!try sema.validateExternType(ty.childType(mod), .other)) {
  24229         const msg = msg: {
  24230             const msg = try sema.errMsg(block, ty_src, "extern symbol cannot have type '{}'", .{ty.fmt(mod)});
  24231             errdefer msg.destroy(sema.gpa);
  24232             const src_decl = sema.mod.declPtr(block.src_decl);
  24233             try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), ty, .other);
  24234             break :msg msg;
  24235         };
  24236         return sema.failWithOwnedErrorMsg(msg);
  24237     }
  24238 
  24239     const options = sema.resolveExternOptions(block, .unneeded, extra.rhs) catch |err| switch (err) {
  24240         error.NeededSourceLocation => {
  24241             _ = try sema.resolveExternOptions(block, options_src, extra.rhs);
  24242             unreachable;
  24243         },
  24244         else => |e| return e,
  24245     };
  24246 
  24247     if (options.linkage == .Weak and !ty.ptrAllowsZero(mod)) {
  24248         ty = try mod.optionalType(ty.toIntern());
  24249     }
  24250 
  24251     // TODO check duplicate extern
  24252 
  24253     const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null);
  24254     errdefer mod.destroyDecl(new_decl_index);
  24255     const new_decl = mod.declPtr(new_decl_index);
  24256     new_decl.name = options.name;
  24257 
  24258     {
  24259         const new_var = try mod.intern(.{ .variable = .{
  24260             .ty = ty.toIntern(),
  24261             .init = .none,
  24262             .decl = sema.owner_decl_index,
  24263             .is_extern = true,
  24264             .is_const = true,
  24265             .is_threadlocal = options.is_thread_local,
  24266             .is_weak_linkage = options.linkage == .Weak,
  24267         } });
  24268 
  24269         new_decl.src_line = sema.owner_decl.src_line;
  24270         // We only access this decl through the decl_ref with the correct type created
  24271         // below, so this type doesn't matter
  24272         new_decl.ty = ty;
  24273         new_decl.val = new_var.toValue();
  24274         new_decl.alignment = .none;
  24275         new_decl.@"linksection" = .none;
  24276         new_decl.has_tv = true;
  24277         new_decl.analysis = .complete;
  24278         new_decl.generation = mod.generation;
  24279     }
  24280 
  24281     try mod.declareDeclDependency(sema.owner_decl_index, new_decl_index);
  24282     try sema.ensureDeclAnalyzed(new_decl_index);
  24283 
  24284     return sema.addConstant(try mod.getCoerced((try mod.intern(.{ .ptr = .{
  24285         .ty = switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  24286             .ptr_type => ty.toIntern(),
  24287             .opt_type => |child_type| child_type,
  24288             else => unreachable,
  24289         },
  24290         .addr = .{ .decl = new_decl_index },
  24291     } })).toValue(), ty));
  24292 }
  24293 
  24294 fn zirWorkItem(
  24295     sema: *Sema,
  24296     block: *Block,
  24297     extended: Zir.Inst.Extended.InstData,
  24298     zir_tag: Zir.Inst.Extended,
  24299 ) CompileError!Air.Inst.Ref {
  24300     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  24301     const dimension_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24302     const builtin_src = LazySrcLoc.nodeOffset(extra.node);
  24303     const target = sema.mod.getTarget();
  24304 
  24305     switch (target.cpu.arch) {
  24306         // TODO: Allow for other GPU targets.
  24307         .amdgcn => {},
  24308         else => {
  24309             return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)});
  24310         },
  24311     }
  24312 
  24313     const dimension = @as(u32, @intCast(try sema.resolveInt(block, dimension_src, extra.operand, Type.u32, "dimension must be comptime-known")));
  24314     try sema.requireRuntimeBlock(block, builtin_src, null);
  24315 
  24316     return block.addInst(.{
  24317         .tag = switch (zir_tag) {
  24318             .work_item_id => .work_item_id,
  24319             .work_group_size => .work_group_size,
  24320             .work_group_id => .work_group_id,
  24321             else => unreachable,
  24322         },
  24323         .data = .{ .pl_op = .{
  24324             .operand = .none,
  24325             .payload = dimension,
  24326         } },
  24327     });
  24328 }
  24329 
  24330 fn zirInComptime(
  24331     sema: *Sema,
  24332     block: *Block,
  24333 ) CompileError!Air.Inst.Ref {
  24334     _ = sema;
  24335     if (block.is_comptime) {
  24336         return Air.Inst.Ref.bool_true;
  24337     } else {
  24338         return Air.Inst.Ref.bool_false;
  24339     }
  24340 }
  24341 
  24342 fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
  24343     if (block.is_comptime) {
  24344         const msg = msg: {
  24345             const msg = try sema.errMsg(block, src, "unable to evaluate comptime expression", .{});
  24346             errdefer msg.destroy(sema.gpa);
  24347 
  24348             if (runtime_src) |some| {
  24349                 try sema.errNote(block, some, msg, "operation is runtime due to this operand", .{});
  24350             }
  24351             if (block.comptime_reason) |some| {
  24352                 try some.explain(sema, msg);
  24353             }
  24354             break :msg msg;
  24355         };
  24356         return sema.failWithOwnedErrorMsg(msg);
  24357     }
  24358 }
  24359 
  24360 /// Emit a compile error if type cannot be used for a runtime variable.
  24361 fn validateVarType(
  24362     sema: *Sema,
  24363     block: *Block,
  24364     src: LazySrcLoc,
  24365     var_ty: Type,
  24366     is_extern: bool,
  24367 ) CompileError!void {
  24368     const mod = sema.mod;
  24369     if (is_extern and !try sema.validateExternType(var_ty, .other)) {
  24370         const msg = msg: {
  24371             const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)});
  24372             errdefer msg.destroy(sema.gpa);
  24373             const src_decl = mod.declPtr(block.src_decl);
  24374             try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), var_ty, .other);
  24375             break :msg msg;
  24376         };
  24377         return sema.failWithOwnedErrorMsg(msg);
  24378     }
  24379 
  24380     if (try sema.validateRunTimeType(var_ty, is_extern)) return;
  24381 
  24382     const msg = msg: {
  24383         const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(mod)});
  24384         errdefer msg.destroy(sema.gpa);
  24385 
  24386         const src_decl = mod.declPtr(block.src_decl);
  24387         try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), var_ty);
  24388         if (var_ty.zigTypeTag(mod) == .ComptimeInt or var_ty.zigTypeTag(mod) == .ComptimeFloat) {
  24389             try sema.errNote(block, src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{});
  24390         }
  24391 
  24392         break :msg msg;
  24393     };
  24394     return sema.failWithOwnedErrorMsg(msg);
  24395 }
  24396 
  24397 fn validateRunTimeType(
  24398     sema: *Sema,
  24399     var_ty: Type,
  24400     is_extern: bool,
  24401 ) CompileError!bool {
  24402     const mod = sema.mod;
  24403     var ty = var_ty;
  24404     while (true) switch (ty.zigTypeTag(mod)) {
  24405         .Bool,
  24406         .Int,
  24407         .Float,
  24408         .ErrorSet,
  24409         .Frame,
  24410         .AnyFrame,
  24411         .Void,
  24412         => return true,
  24413 
  24414         .Enum => return !(try sema.typeRequiresComptime(ty)),
  24415 
  24416         .ComptimeFloat,
  24417         .ComptimeInt,
  24418         .EnumLiteral,
  24419         .NoReturn,
  24420         .Type,
  24421         .Undefined,
  24422         .Null,
  24423         .Fn,
  24424         => return false,
  24425 
  24426         .Pointer => {
  24427             const elem_ty = ty.childType(mod);
  24428             switch (elem_ty.zigTypeTag(mod)) {
  24429                 .Opaque => return true,
  24430                 .Fn => return elem_ty.isFnOrHasRuntimeBits(mod),
  24431                 else => ty = elem_ty,
  24432             }
  24433         },
  24434         .Opaque => return is_extern,
  24435 
  24436         .Optional => {
  24437             const child_ty = ty.optionalChild(mod);
  24438             return sema.validateRunTimeType(child_ty, is_extern);
  24439         },
  24440         .Array, .Vector => ty = ty.childType(mod),
  24441 
  24442         .ErrorUnion => ty = ty.errorUnionPayload(mod),
  24443 
  24444         .Struct, .Union => {
  24445             const resolved_ty = try sema.resolveTypeFields(ty);
  24446             const needs_comptime = try sema.typeRequiresComptime(resolved_ty);
  24447             return !needs_comptime;
  24448         },
  24449     };
  24450 }
  24451 
  24452 const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void);
  24453 
  24454 fn explainWhyTypeIsComptime(
  24455     sema: *Sema,
  24456     msg: *Module.ErrorMsg,
  24457     src_loc: Module.SrcLoc,
  24458     ty: Type,
  24459 ) CompileError!void {
  24460     var type_set = TypeSet{};
  24461     defer type_set.deinit(sema.gpa);
  24462 
  24463     try sema.resolveTypeFully(ty);
  24464     return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set);
  24465 }
  24466 
  24467 fn explainWhyTypeIsComptimeInner(
  24468     sema: *Sema,
  24469     msg: *Module.ErrorMsg,
  24470     src_loc: Module.SrcLoc,
  24471     ty: Type,
  24472     type_set: *TypeSet,
  24473 ) CompileError!void {
  24474     const mod = sema.mod;
  24475     switch (ty.zigTypeTag(mod)) {
  24476         .Bool,
  24477         .Int,
  24478         .Float,
  24479         .ErrorSet,
  24480         .Enum,
  24481         .Frame,
  24482         .AnyFrame,
  24483         .Void,
  24484         => return,
  24485 
  24486         .Fn => {
  24487             try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{
  24488                 ty.fmt(sema.mod),
  24489             });
  24490         },
  24491 
  24492         .Type => {
  24493             try mod.errNoteNonLazy(src_loc, msg, "types are not available at runtime", .{});
  24494         },
  24495 
  24496         .ComptimeFloat,
  24497         .ComptimeInt,
  24498         .EnumLiteral,
  24499         .NoReturn,
  24500         .Undefined,
  24501         .Null,
  24502         => return,
  24503 
  24504         .Opaque => {
  24505             try mod.errNoteNonLazy(src_loc, msg, "opaque type '{}' has undefined size", .{ty.fmt(sema.mod)});
  24506         },
  24507 
  24508         .Array, .Vector => {
  24509             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set);
  24510         },
  24511         .Pointer => {
  24512             const elem_ty = ty.elemType2(mod);
  24513             if (elem_ty.zigTypeTag(mod) == .Fn) {
  24514                 const fn_info = mod.typeToFunc(elem_ty).?;
  24515                 if (fn_info.is_generic) {
  24516                     try mod.errNoteNonLazy(src_loc, msg, "function is generic", .{});
  24517                 }
  24518                 switch (fn_info.cc) {
  24519                     .Inline => try mod.errNoteNonLazy(src_loc, msg, "function has inline calling convention", .{}),
  24520                     else => {},
  24521                 }
  24522                 if (fn_info.return_type.toType().comptimeOnly(mod)) {
  24523                     try mod.errNoteNonLazy(src_loc, msg, "function has a comptime-only return type", .{});
  24524                 }
  24525                 return;
  24526             }
  24527             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set);
  24528         },
  24529 
  24530         .Optional => {
  24531             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(mod), type_set);
  24532         },
  24533         .ErrorUnion => {
  24534             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(mod), type_set);
  24535         },
  24536 
  24537         .Struct => {
  24538             if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
  24539 
  24540             if (mod.typeToStruct(ty)) |struct_obj| {
  24541                 for (struct_obj.fields.values(), 0..) |field, i| {
  24542                     const field_src_loc = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  24543                         .index = i,
  24544                         .range = .type,
  24545                     });
  24546 
  24547                     if (try sema.typeRequiresComptime(field.ty)) {
  24548                         try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{});
  24549                         try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field.ty, type_set);
  24550                     }
  24551                 }
  24552             }
  24553             // TODO tuples
  24554         },
  24555 
  24556         .Union => {
  24557             if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
  24558 
  24559             if (mod.typeToUnion(ty)) |union_obj| {
  24560                 for (union_obj.fields.values(), 0..) |field, i| {
  24561                     const field_src_loc = mod.fieldSrcLoc(union_obj.owner_decl, .{
  24562                         .index = i,
  24563                         .range = .type,
  24564                     });
  24565 
  24566                     if (try sema.typeRequiresComptime(field.ty)) {
  24567                         try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{});
  24568                         try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field.ty, type_set);
  24569                     }
  24570                 }
  24571             }
  24572         },
  24573     }
  24574 }
  24575 
  24576 const ExternPosition = enum {
  24577     ret_ty,
  24578     param_ty,
  24579     union_field,
  24580     struct_field,
  24581     element,
  24582     other,
  24583 };
  24584 
  24585 /// Returns true if `ty` is allowed in extern types.
  24586 /// Does *NOT* require `ty` to be resolved in any way.
  24587 /// Calls `resolveTypeLayout` for packed containers.
  24588 fn validateExternType(
  24589     sema: *Sema,
  24590     ty: Type,
  24591     position: ExternPosition,
  24592 ) !bool {
  24593     const mod = sema.mod;
  24594     switch (ty.zigTypeTag(mod)) {
  24595         .Type,
  24596         .ComptimeFloat,
  24597         .ComptimeInt,
  24598         .EnumLiteral,
  24599         .Undefined,
  24600         .Null,
  24601         .ErrorUnion,
  24602         .ErrorSet,
  24603         .Frame,
  24604         => return false,
  24605         .Void => return position == .union_field or position == .ret_ty,
  24606         .NoReturn => return position == .ret_ty,
  24607         .Opaque,
  24608         .Bool,
  24609         .Float,
  24610         .AnyFrame,
  24611         => return true,
  24612         .Pointer => return !(ty.isSlice(mod) or try sema.typeRequiresComptime(ty)),
  24613         .Int => switch (ty.intInfo(mod).bits) {
  24614             8, 16, 32, 64, 128 => return true,
  24615             else => return false,
  24616         },
  24617         .Fn => {
  24618             if (position != .other) return false;
  24619             const target = sema.mod.getTarget();
  24620             // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI.
  24621             // The goal is to experiment with more integrated CPU/GPU code.
  24622             if (ty.fnCallingConvention(mod) == .Kernel and (target.cpu.arch == .nvptx or target.cpu.arch == .nvptx64)) {
  24623                 return true;
  24624             }
  24625             return !target_util.fnCallConvAllowsZigTypes(target, ty.fnCallingConvention(mod));
  24626         },
  24627         .Enum => {
  24628             return sema.validateExternType(ty.intTagType(mod), position);
  24629         },
  24630         .Struct, .Union => switch (ty.containerLayout(mod)) {
  24631             .Extern => return true,
  24632             .Packed => {
  24633                 const bit_size = try ty.bitSizeAdvanced(mod, sema);
  24634                 switch (bit_size) {
  24635                     8, 16, 32, 64, 128 => return true,
  24636                     else => return false,
  24637                 }
  24638             },
  24639             .Auto => return false,
  24640         },
  24641         .Array => {
  24642             if (position == .ret_ty or position == .param_ty) return false;
  24643             return sema.validateExternType(ty.elemType2(mod), .element);
  24644         },
  24645         .Vector => return sema.validateExternType(ty.elemType2(mod), .element),
  24646         .Optional => return ty.isPtrLikeOptional(mod),
  24647     }
  24648 }
  24649 
  24650 fn explainWhyTypeIsNotExtern(
  24651     sema: *Sema,
  24652     msg: *Module.ErrorMsg,
  24653     src_loc: Module.SrcLoc,
  24654     ty: Type,
  24655     position: ExternPosition,
  24656 ) CompileError!void {
  24657     const mod = sema.mod;
  24658     switch (ty.zigTypeTag(mod)) {
  24659         .Opaque,
  24660         .Bool,
  24661         .Float,
  24662         .AnyFrame,
  24663         => return,
  24664 
  24665         .Type,
  24666         .ComptimeFloat,
  24667         .ComptimeInt,
  24668         .EnumLiteral,
  24669         .Undefined,
  24670         .Null,
  24671         .ErrorUnion,
  24672         .ErrorSet,
  24673         .Frame,
  24674         => return,
  24675 
  24676         .Pointer => {
  24677             if (ty.isSlice(mod)) {
  24678                 try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{});
  24679             } else {
  24680                 const pointee_ty = ty.childType(mod);
  24681                 try mod.errNoteNonLazy(src_loc, msg, "pointer to comptime-only type '{}'", .{pointee_ty.fmt(sema.mod)});
  24682                 try sema.explainWhyTypeIsComptime(msg, src_loc, pointee_ty);
  24683             }
  24684         },
  24685         .Void => try mod.errNoteNonLazy(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}),
  24686         .NoReturn => try mod.errNoteNonLazy(src_loc, msg, "'noreturn' is only allowed as a return type", .{}),
  24687         .Int => if (!std.math.isPowerOfTwo(ty.intInfo(mod).bits)) {
  24688             try mod.errNoteNonLazy(src_loc, msg, "only integers with power of two bits are extern compatible", .{});
  24689         } else {
  24690             try mod.errNoteNonLazy(src_loc, msg, "only integers with 8, 16, 32, 64 and 128 bits are extern compatible", .{});
  24691         },
  24692         .Fn => {
  24693             if (position != .other) {
  24694                 try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{});
  24695                 try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{});
  24696                 return;
  24697             }
  24698             switch (ty.fnCallingConvention(mod)) {
  24699                 .Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}),
  24700                 .Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}),
  24701                 .Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}),
  24702                 else => return,
  24703             }
  24704         },
  24705         .Enum => {
  24706             const tag_ty = ty.intTagType(mod);
  24707             try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)});
  24708             try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position);
  24709         },
  24710         .Struct => try mod.errNoteNonLazy(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}),
  24711         .Union => try mod.errNoteNonLazy(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}),
  24712         .Array => {
  24713             if (position == .ret_ty) {
  24714                 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{});
  24715             } else if (position == .param_ty) {
  24716                 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{});
  24717             }
  24718             try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element);
  24719         },
  24720         .Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element),
  24721         .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
  24722     }
  24723 }
  24724 
  24725 /// Returns true if `ty` is allowed in packed types.
  24726 /// Does *NOT* require `ty` to be resolved in any way.
  24727 fn validatePackedType(ty: Type, mod: *Module) bool {
  24728     switch (ty.zigTypeTag(mod)) {
  24729         .Type,
  24730         .ComptimeFloat,
  24731         .ComptimeInt,
  24732         .EnumLiteral,
  24733         .Undefined,
  24734         .Null,
  24735         .ErrorUnion,
  24736         .ErrorSet,
  24737         .Frame,
  24738         .NoReturn,
  24739         .Opaque,
  24740         .AnyFrame,
  24741         .Fn,
  24742         .Array,
  24743         => return false,
  24744         .Optional => return ty.isPtrLikeOptional(mod),
  24745         .Void,
  24746         .Bool,
  24747         .Float,
  24748         .Int,
  24749         .Vector,
  24750         .Enum,
  24751         => return true,
  24752         .Pointer => return !ty.isSlice(mod),
  24753         .Struct, .Union => return ty.containerLayout(mod) == .Packed,
  24754     }
  24755 }
  24756 
  24757 fn explainWhyTypeIsNotPacked(
  24758     sema: *Sema,
  24759     msg: *Module.ErrorMsg,
  24760     src_loc: Module.SrcLoc,
  24761     ty: Type,
  24762 ) CompileError!void {
  24763     const mod = sema.mod;
  24764     switch (ty.zigTypeTag(mod)) {
  24765         .Void,
  24766         .Bool,
  24767         .Float,
  24768         .Int,
  24769         .Vector,
  24770         .Enum,
  24771         => return,
  24772         .Type,
  24773         .ComptimeFloat,
  24774         .ComptimeInt,
  24775         .EnumLiteral,
  24776         .Undefined,
  24777         .Null,
  24778         .Frame,
  24779         .NoReturn,
  24780         .Opaque,
  24781         .ErrorUnion,
  24782         .ErrorSet,
  24783         .AnyFrame,
  24784         .Optional,
  24785         .Array,
  24786         => try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}),
  24787         .Pointer => try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{}),
  24788         .Fn => {
  24789             try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{});
  24790             try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{});
  24791         },
  24792         .Struct => try mod.errNoteNonLazy(src_loc, msg, "only packed structs layout are allowed in packed types", .{}),
  24793         .Union => try mod.errNoteNonLazy(src_loc, msg, "only packed unions layout are allowed in packed types", .{}),
  24794     }
  24795 }
  24796 
  24797 fn prepareSimplePanic(sema: *Sema, block: *Block) !void {
  24798     const mod = sema.mod;
  24799 
  24800     if (mod.panic_func_index == .none) {
  24801         const decl_index = (try sema.getBuiltinDecl(block, "panic"));
  24802         // decl_index may be an alias; we must find the decl that actually
  24803         // owns the function.
  24804         try sema.ensureDeclAnalyzed(decl_index);
  24805         const tv = try mod.declPtr(decl_index).typedValue();
  24806         assert(tv.ty.zigTypeTag(mod) == .Fn);
  24807         assert(try sema.fnHasRuntimeBits(tv.ty));
  24808         const func_index = mod.intern_pool.indexToFunc(tv.val.toIntern()).unwrap().?;
  24809         try mod.ensureFuncBodyAnalysisQueued(func_index);
  24810         mod.panic_func_index = func_index.toOptional();
  24811     }
  24812 
  24813     if (mod.null_stack_trace == .none) {
  24814         const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
  24815         const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
  24816         const target = mod.getTarget();
  24817         const ptr_stack_trace_ty = try mod.ptrType(.{
  24818             .child = stack_trace_ty.toIntern(),
  24819             .flags = .{
  24820                 .address_space = target_util.defaultAddressSpace(target, .global_constant),
  24821             },
  24822         });
  24823         const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern());
  24824         mod.null_stack_trace = try mod.intern(.{ .opt = .{
  24825             .ty = opt_ptr_stack_trace_ty.toIntern(),
  24826             .val = .none,
  24827         } });
  24828     }
  24829 }
  24830 
  24831 /// Backends depend on panic decls being available when lowering safety-checked
  24832 /// instructions. This function ensures the panic function will be available to
  24833 /// be called during that time.
  24834 fn preparePanicId(sema: *Sema, block: *Block, panic_id: Module.PanicId) !Module.Decl.Index {
  24835     const mod = sema.mod;
  24836     const gpa = sema.gpa;
  24837     if (mod.panic_messages[@intFromEnum(panic_id)].unwrap()) |x| return x;
  24838 
  24839     try sema.prepareSimplePanic(block);
  24840 
  24841     const panic_messages_ty = try sema.getBuiltinType("panic_messages");
  24842     const msg_decl_index = (try sema.namespaceLookup(
  24843         block,
  24844         sema.src,
  24845         panic_messages_ty.getNamespaceIndex(mod).unwrap().?,
  24846         try mod.intern_pool.getOrPutString(gpa, @tagName(panic_id)),
  24847     )).?;
  24848     try sema.ensureDeclAnalyzed(msg_decl_index);
  24849     mod.panic_messages[@intFromEnum(panic_id)] = msg_decl_index.toOptional();
  24850     return msg_decl_index;
  24851 }
  24852 
  24853 fn addSafetyCheck(
  24854     sema: *Sema,
  24855     parent_block: *Block,
  24856     ok: Air.Inst.Ref,
  24857     panic_id: Module.PanicId,
  24858 ) !void {
  24859     const gpa = sema.gpa;
  24860     assert(!parent_block.is_comptime);
  24861 
  24862     var fail_block: Block = .{
  24863         .parent = parent_block,
  24864         .sema = sema,
  24865         .src_decl = parent_block.src_decl,
  24866         .namespace = parent_block.namespace,
  24867         .wip_capture_scope = parent_block.wip_capture_scope,
  24868         .instructions = .{},
  24869         .inlining = parent_block.inlining,
  24870         .is_comptime = false,
  24871     };
  24872 
  24873     defer fail_block.instructions.deinit(gpa);
  24874 
  24875     try sema.safetyPanic(&fail_block, panic_id);
  24876     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  24877 }
  24878 
  24879 fn addSafetyCheckExtra(
  24880     sema: *Sema,
  24881     parent_block: *Block,
  24882     ok: Air.Inst.Ref,
  24883     fail_block: *Block,
  24884 ) !void {
  24885     const gpa = sema.gpa;
  24886 
  24887     try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
  24888 
  24889     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
  24890         1 + // The main block only needs space for the cond_br.
  24891         @typeInfo(Air.CondBr).Struct.fields.len +
  24892         1 + // The ok branch of the cond_br only needs space for the br.
  24893         fail_block.instructions.items.len);
  24894 
  24895     try sema.air_instructions.ensureUnusedCapacity(gpa, 3);
  24896     const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
  24897     const cond_br_inst = block_inst + 1;
  24898     const br_inst = cond_br_inst + 1;
  24899     sema.air_instructions.appendAssumeCapacity(.{
  24900         .tag = .block,
  24901         .data = .{ .ty_pl = .{
  24902             .ty = .void_type,
  24903             .payload = sema.addExtraAssumeCapacity(Air.Block{
  24904                 .body_len = 1,
  24905             }),
  24906         } },
  24907     });
  24908     sema.air_extra.appendAssumeCapacity(cond_br_inst);
  24909 
  24910     sema.air_instructions.appendAssumeCapacity(.{
  24911         .tag = .cond_br,
  24912         .data = .{ .pl_op = .{
  24913             .operand = ok,
  24914             .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  24915                 .then_body_len = 1,
  24916                 .else_body_len = @as(u32, @intCast(fail_block.instructions.items.len)),
  24917             }),
  24918         } },
  24919     });
  24920     sema.air_extra.appendAssumeCapacity(br_inst);
  24921     sema.air_extra.appendSliceAssumeCapacity(fail_block.instructions.items);
  24922 
  24923     sema.air_instructions.appendAssumeCapacity(.{
  24924         .tag = .br,
  24925         .data = .{ .br = .{
  24926             .block_inst = block_inst,
  24927             .operand = .void_value,
  24928         } },
  24929     });
  24930 
  24931     parent_block.instructions.appendAssumeCapacity(block_inst);
  24932 }
  24933 
  24934 fn panicWithMsg(sema: *Sema, block: *Block, msg_inst: Air.Inst.Ref) !void {
  24935     const mod = sema.mod;
  24936 
  24937     if (!mod.backendSupportsFeature(.panic_fn)) {
  24938         _ = try block.addNoOp(.trap);
  24939         return;
  24940     }
  24941 
  24942     try sema.prepareSimplePanic(block);
  24943 
  24944     const panic_func = mod.funcPtrUnwrap(mod.panic_func_index).?;
  24945     const panic_fn = try sema.analyzeDeclVal(block, .unneeded, panic_func.owner_decl);
  24946     const null_stack_trace = try sema.addConstant(mod.null_stack_trace.toValue());
  24947 
  24948     const opt_usize_ty = try mod.optionalType(.usize_type);
  24949     const null_ret_addr = try sema.addConstant((try mod.intern(.{ .opt = .{
  24950         .ty = opt_usize_ty.toIntern(),
  24951         .val = .none,
  24952     } })).toValue());
  24953     try sema.callBuiltin(block, panic_fn, .auto, &.{ msg_inst, null_stack_trace, null_ret_addr });
  24954 }
  24955 
  24956 fn panicUnwrapError(
  24957     sema: *Sema,
  24958     parent_block: *Block,
  24959     operand: Air.Inst.Ref,
  24960     unwrap_err_tag: Air.Inst.Tag,
  24961     is_non_err_tag: Air.Inst.Tag,
  24962 ) !void {
  24963     assert(!parent_block.is_comptime);
  24964     const ok = try parent_block.addUnOp(is_non_err_tag, operand);
  24965     if (!sema.mod.comp.formatted_panics) {
  24966         return sema.addSafetyCheck(parent_block, ok, .unwrap_error);
  24967     }
  24968     const gpa = sema.gpa;
  24969 
  24970     var fail_block: Block = .{
  24971         .parent = parent_block,
  24972         .sema = sema,
  24973         .src_decl = parent_block.src_decl,
  24974         .namespace = parent_block.namespace,
  24975         .wip_capture_scope = parent_block.wip_capture_scope,
  24976         .instructions = .{},
  24977         .inlining = parent_block.inlining,
  24978         .is_comptime = false,
  24979     };
  24980 
  24981     defer fail_block.instructions.deinit(gpa);
  24982 
  24983     {
  24984         if (!sema.mod.backendSupportsFeature(.panic_unwrap_error)) {
  24985             _ = try fail_block.addNoOp(.trap);
  24986         } else {
  24987             const panic_fn = try sema.getBuiltin("panicUnwrapError");
  24988             const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
  24989             const err_return_trace = try sema.getErrorReturnTrace(&fail_block);
  24990             const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
  24991             try sema.callBuiltin(&fail_block, panic_fn, .auto, &args);
  24992         }
  24993     }
  24994     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  24995 }
  24996 
  24997 fn panicIndexOutOfBounds(
  24998     sema: *Sema,
  24999     parent_block: *Block,
  25000     index: Air.Inst.Ref,
  25001     len: Air.Inst.Ref,
  25002     cmp_op: Air.Inst.Tag,
  25003 ) !void {
  25004     assert(!parent_block.is_comptime);
  25005     const ok = try parent_block.addBinOp(cmp_op, index, len);
  25006     if (!sema.mod.comp.formatted_panics) {
  25007         return sema.addSafetyCheck(parent_block, ok, .index_out_of_bounds);
  25008     }
  25009     try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });
  25010 }
  25011 
  25012 fn panicInactiveUnionField(
  25013     sema: *Sema,
  25014     parent_block: *Block,
  25015     active_tag: Air.Inst.Ref,
  25016     wanted_tag: Air.Inst.Ref,
  25017 ) !void {
  25018     assert(!parent_block.is_comptime);
  25019     const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
  25020     if (!sema.mod.comp.formatted_panics) {
  25021         return sema.addSafetyCheck(parent_block, ok, .inactive_union_field);
  25022     }
  25023     try sema.safetyCheckFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
  25024 }
  25025 
  25026 fn panicSentinelMismatch(
  25027     sema: *Sema,
  25028     parent_block: *Block,
  25029     maybe_sentinel: ?Value,
  25030     sentinel_ty: Type,
  25031     ptr: Air.Inst.Ref,
  25032     sentinel_index: Air.Inst.Ref,
  25033 ) !void {
  25034     assert(!parent_block.is_comptime);
  25035     const mod = sema.mod;
  25036     const expected_sentinel_val = maybe_sentinel orelse return;
  25037     const expected_sentinel = try sema.addConstant(expected_sentinel_val);
  25038 
  25039     const ptr_ty = sema.typeOf(ptr);
  25040     const actual_sentinel = if (ptr_ty.isSlice(mod))
  25041         try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index)
  25042     else blk: {
  25043         const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null);
  25044         const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty);
  25045         break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr);
  25046     };
  25047 
  25048     const ok = if (sentinel_ty.zigTypeTag(mod) == .Vector) ok: {
  25049         const eql =
  25050             try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq);
  25051         break :ok try parent_block.addInst(.{
  25052             .tag = .reduce,
  25053             .data = .{ .reduce = .{
  25054                 .operand = eql,
  25055                 .operation = .And,
  25056             } },
  25057         });
  25058     } else if (sentinel_ty.isSelfComparable(mod, true))
  25059         try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel)
  25060     else {
  25061         const panic_fn = try sema.getBuiltin("checkNonScalarSentinel");
  25062         const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
  25063         try sema.callBuiltin(parent_block, panic_fn, .auto, &args);
  25064         return;
  25065     };
  25066 
  25067     if (!sema.mod.comp.formatted_panics) {
  25068         return sema.addSafetyCheck(parent_block, ok, .sentinel_mismatch);
  25069     }
  25070     try sema.safetyCheckFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel });
  25071 }
  25072 
  25073 fn safetyCheckFormatted(
  25074     sema: *Sema,
  25075     parent_block: *Block,
  25076     ok: Air.Inst.Ref,
  25077     func: []const u8,
  25078     args: []const Air.Inst.Ref,
  25079 ) CompileError!void {
  25080     assert(sema.mod.comp.formatted_panics);
  25081     const gpa = sema.gpa;
  25082 
  25083     var fail_block: Block = .{
  25084         .parent = parent_block,
  25085         .sema = sema,
  25086         .src_decl = parent_block.src_decl,
  25087         .namespace = parent_block.namespace,
  25088         .wip_capture_scope = parent_block.wip_capture_scope,
  25089         .instructions = .{},
  25090         .inlining = parent_block.inlining,
  25091         .is_comptime = false,
  25092     };
  25093 
  25094     defer fail_block.instructions.deinit(gpa);
  25095 
  25096     if (!sema.mod.backendSupportsFeature(.safety_check_formatted)) {
  25097         _ = try fail_block.addNoOp(.trap);
  25098     } else {
  25099         const panic_fn = try sema.getBuiltin(func);
  25100         try sema.callBuiltin(&fail_block, panic_fn, .auto, args);
  25101     }
  25102     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  25103 }
  25104 
  25105 fn safetyPanic(sema: *Sema, block: *Block, panic_id: Module.PanicId) CompileError!void {
  25106     const msg_decl_index = try sema.preparePanicId(block, panic_id);
  25107     const msg_inst = try sema.analyzeDeclVal(block, sema.src, msg_decl_index);
  25108     try sema.panicWithMsg(block, msg_inst);
  25109 }
  25110 
  25111 fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
  25112     sema.branch_count += 1;
  25113     if (sema.branch_count > sema.branch_quota) {
  25114         const msg = try sema.errMsg(
  25115             block,
  25116             src,
  25117             "evaluation exceeded {d} backwards branches",
  25118             .{sema.branch_quota},
  25119         );
  25120         try sema.errNote(
  25121             block,
  25122             src,
  25123             msg,
  25124             "use @setEvalBranchQuota() to raise the branch limit from {d}",
  25125             .{sema.branch_quota},
  25126         );
  25127         return sema.failWithOwnedErrorMsg(msg);
  25128     }
  25129 }
  25130 
  25131 fn fieldVal(
  25132     sema: *Sema,
  25133     block: *Block,
  25134     src: LazySrcLoc,
  25135     object: Air.Inst.Ref,
  25136     field_name: InternPool.NullTerminatedString,
  25137     field_name_src: LazySrcLoc,
  25138 ) CompileError!Air.Inst.Ref {
  25139     // When editing this function, note that there is corresponding logic to be edited
  25140     // in `fieldPtr`. This function takes a value and returns a value.
  25141 
  25142     const mod = sema.mod;
  25143     const ip = &mod.intern_pool;
  25144     const object_src = src; // TODO better source location
  25145     const object_ty = sema.typeOf(object);
  25146 
  25147     // Zig allows dereferencing a single pointer during field lookup. Note that
  25148     // we don't actually need to generate the dereference some field lookups, like the
  25149     // length of arrays and other comptime operations.
  25150     const is_pointer_to = object_ty.isSinglePointer(mod);
  25151 
  25152     const inner_ty = if (is_pointer_to)
  25153         object_ty.childType(mod)
  25154     else
  25155         object_ty;
  25156 
  25157     switch (inner_ty.zigTypeTag(mod)) {
  25158         .Array => {
  25159             if (ip.stringEqlSlice(field_name, "len")) {
  25160                 return sema.addConstant(
  25161                     try mod.intValue(Type.usize, inner_ty.arrayLen(mod)),
  25162                 );
  25163             } else if (ip.stringEqlSlice(field_name, "ptr") and is_pointer_to) {
  25164                 const ptr_info = object_ty.ptrInfo(mod);
  25165                 const result_ty = try mod.ptrType(.{
  25166                     .child = ptr_info.child.toType().childType(mod).toIntern(),
  25167                     .sentinel = ptr_info.sentinel,
  25168                     .flags = .{
  25169                         .size = .Many,
  25170                         .alignment = ptr_info.flags.alignment,
  25171                         .is_const = ptr_info.flags.is_const,
  25172                         .is_volatile = ptr_info.flags.is_volatile,
  25173                         .is_allowzero = ptr_info.flags.is_allowzero,
  25174                         .address_space = ptr_info.flags.address_space,
  25175                         .vector_index = ptr_info.flags.vector_index,
  25176                     },
  25177                     .packed_offset = ptr_info.packed_offset,
  25178                 });
  25179                 return sema.coerce(block, result_ty, object, src);
  25180             } else {
  25181                 return sema.fail(
  25182                     block,
  25183                     field_name_src,
  25184                     "no member named '{}' in '{}'",
  25185                     .{ field_name.fmt(ip), object_ty.fmt(mod) },
  25186                 );
  25187             }
  25188         },
  25189         .Pointer => {
  25190             const ptr_info = inner_ty.ptrInfo(mod);
  25191             if (ptr_info.flags.size == .Slice) {
  25192                 if (ip.stringEqlSlice(field_name, "ptr")) {
  25193                     const slice = if (is_pointer_to)
  25194                         try sema.analyzeLoad(block, src, object, object_src)
  25195                     else
  25196                         object;
  25197                     return sema.analyzeSlicePtr(block, object_src, slice, inner_ty);
  25198                 } else if (ip.stringEqlSlice(field_name, "len")) {
  25199                     const slice = if (is_pointer_to)
  25200                         try sema.analyzeLoad(block, src, object, object_src)
  25201                     else
  25202                         object;
  25203                     return sema.analyzeSliceLen(block, src, slice);
  25204                 } else {
  25205                     return sema.fail(
  25206                         block,
  25207                         field_name_src,
  25208                         "no member named '{}' in '{}'",
  25209                         .{ field_name.fmt(ip), object_ty.fmt(mod) },
  25210                     );
  25211                 }
  25212             }
  25213         },
  25214         .Type => {
  25215             const dereffed_type = if (is_pointer_to)
  25216                 try sema.analyzeLoad(block, src, object, object_src)
  25217             else
  25218                 object;
  25219 
  25220             const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
  25221             const child_type = val.toType();
  25222 
  25223             switch (try child_type.zigTypeTagOrPoison(mod)) {
  25224                 .ErrorSet => {
  25225                     switch (ip.indexToKey(child_type.toIntern())) {
  25226                         .error_set_type => |error_set_type| blk: {
  25227                             if (error_set_type.nameIndex(ip, field_name) != null) break :blk;
  25228                             const msg = msg: {
  25229                                 const msg = try sema.errMsg(block, src, "no error named '{}' in '{}'", .{
  25230                                     field_name.fmt(ip), child_type.fmt(mod),
  25231                                 });
  25232                                 errdefer msg.destroy(sema.gpa);
  25233                                 try sema.addDeclaredHereNote(msg, child_type);
  25234                                 break :msg msg;
  25235                             };
  25236                             return sema.failWithOwnedErrorMsg(msg);
  25237                         },
  25238                         .inferred_error_set_type => {
  25239                             return sema.fail(block, src, "TODO handle inferred error sets here", .{});
  25240                         },
  25241                         .simple_type => |t| {
  25242                             assert(t == .anyerror);
  25243                             _ = try mod.getErrorValue(field_name);
  25244                         },
  25245                         else => unreachable,
  25246                     }
  25247 
  25248                     const error_set_type = if (!child_type.isAnyError(mod))
  25249                         child_type
  25250                     else
  25251                         try mod.singleErrorSetType(field_name);
  25252                     return sema.addConstant((try mod.intern(.{ .err = .{
  25253                         .ty = error_set_type.toIntern(),
  25254                         .name = field_name,
  25255                     } })).toValue());
  25256                 },
  25257                 .Union => {
  25258                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25259                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
  25260                             return inst;
  25261                         }
  25262                     }
  25263                     const union_ty = try sema.resolveTypeFields(child_type);
  25264                     if (union_ty.unionTagType(mod)) |enum_ty| {
  25265                         if (enum_ty.enumFieldIndex(field_name, mod)) |field_index_usize| {
  25266                             const field_index = @as(u32, @intCast(field_index_usize));
  25267                             return sema.addConstant(
  25268                                 try mod.enumValueFieldIndex(enum_ty, field_index),
  25269                             );
  25270                         }
  25271                     }
  25272                     return sema.failWithBadMemberAccess(block, union_ty, field_name_src, field_name);
  25273                 },
  25274                 .Enum => {
  25275                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25276                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
  25277                             return inst;
  25278                         }
  25279                     }
  25280                     const field_index_usize = child_type.enumFieldIndex(field_name, mod) orelse
  25281                         return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  25282                     const field_index = @as(u32, @intCast(field_index_usize));
  25283                     const enum_val = try mod.enumValueFieldIndex(child_type, field_index);
  25284                     return sema.addConstant(enum_val);
  25285                 },
  25286                 .Struct, .Opaque => {
  25287                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25288                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
  25289                             return inst;
  25290                         }
  25291                     }
  25292                     return sema.failWithBadMemberAccess(block, child_type, src, field_name);
  25293                 },
  25294                 else => {
  25295                     const msg = msg: {
  25296                         const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(mod)});
  25297                         errdefer msg.destroy(sema.gpa);
  25298                         if (child_type.isSlice(mod)) try sema.errNote(block, src, msg, "slice values have 'len' and 'ptr' members", .{});
  25299                         if (child_type.zigTypeTag(mod) == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{});
  25300                         break :msg msg;
  25301                     };
  25302                     return sema.failWithOwnedErrorMsg(msg);
  25303                 },
  25304             }
  25305         },
  25306         .Struct => if (is_pointer_to) {
  25307             // Avoid loading the entire struct by fetching a pointer and loading that
  25308             const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
  25309             return sema.analyzeLoad(block, src, field_ptr, object_src);
  25310         } else {
  25311             return sema.structFieldVal(block, src, object, field_name, field_name_src, inner_ty);
  25312         },
  25313         .Union => if (is_pointer_to) {
  25314             // Avoid loading the entire union by fetching a pointer and loading that
  25315             const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
  25316             return sema.analyzeLoad(block, src, field_ptr, object_src);
  25317         } else {
  25318             return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
  25319         },
  25320         else => {},
  25321     }
  25322     return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
  25323 }
  25324 
  25325 fn fieldPtr(
  25326     sema: *Sema,
  25327     block: *Block,
  25328     src: LazySrcLoc,
  25329     object_ptr: Air.Inst.Ref,
  25330     field_name: InternPool.NullTerminatedString,
  25331     field_name_src: LazySrcLoc,
  25332     initializing: bool,
  25333 ) CompileError!Air.Inst.Ref {
  25334     // When editing this function, note that there is corresponding logic to be edited
  25335     // in `fieldVal`. This function takes a pointer and returns a pointer.
  25336 
  25337     const mod = sema.mod;
  25338     const ip = &mod.intern_pool;
  25339     const object_ptr_src = src; // TODO better source location
  25340     const object_ptr_ty = sema.typeOf(object_ptr);
  25341     const object_ty = switch (object_ptr_ty.zigTypeTag(mod)) {
  25342         .Pointer => object_ptr_ty.childType(mod),
  25343         else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(mod)}),
  25344     };
  25345 
  25346     // Zig allows dereferencing a single pointer during field lookup. Note that
  25347     // we don't actually need to generate the dereference some field lookups, like the
  25348     // length of arrays and other comptime operations.
  25349     const is_pointer_to = object_ty.isSinglePointer(mod);
  25350 
  25351     const inner_ty = if (is_pointer_to)
  25352         object_ty.childType(mod)
  25353     else
  25354         object_ty;
  25355 
  25356     switch (inner_ty.zigTypeTag(mod)) {
  25357         .Array => {
  25358             if (ip.stringEqlSlice(field_name, "len")) {
  25359                 var anon_decl = try block.startAnonDecl();
  25360                 defer anon_decl.deinit();
  25361                 return sema.analyzeDeclRef(try anon_decl.finish(
  25362                     Type.usize,
  25363                     try mod.intValue(Type.usize, inner_ty.arrayLen(mod)),
  25364                     .none, // default alignment
  25365                 ));
  25366             } else {
  25367                 return sema.fail(
  25368                     block,
  25369                     field_name_src,
  25370                     "no member named '{}' in '{}'",
  25371                     .{ field_name.fmt(ip), object_ty.fmt(mod) },
  25372                 );
  25373             }
  25374         },
  25375         .Pointer => if (inner_ty.isSlice(mod)) {
  25376             const inner_ptr = if (is_pointer_to)
  25377                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  25378             else
  25379                 object_ptr;
  25380 
  25381             const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty;
  25382 
  25383             if (ip.stringEqlSlice(field_name, "ptr")) {
  25384                 const slice_ptr_ty = inner_ty.slicePtrFieldType(mod);
  25385 
  25386                 const result_ty = try mod.ptrType(.{
  25387                     .child = slice_ptr_ty.toIntern(),
  25388                     .flags = .{
  25389                         .is_const = !attr_ptr_ty.ptrIsMutable(mod),
  25390                         .is_volatile = attr_ptr_ty.isVolatilePtr(mod),
  25391                         .address_space = attr_ptr_ty.ptrAddressSpace(mod),
  25392                     },
  25393                 });
  25394 
  25395                 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
  25396                     return sema.addConstant((try mod.intern(.{ .ptr = .{
  25397                         .ty = result_ty.toIntern(),
  25398                         .addr = .{ .field = .{
  25399                             .base = val.toIntern(),
  25400                             .index = Value.slice_ptr_index,
  25401                         } },
  25402                     } })).toValue());
  25403                 }
  25404                 try sema.requireRuntimeBlock(block, src, null);
  25405 
  25406                 return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
  25407             } else if (ip.stringEqlSlice(field_name, "len")) {
  25408                 const result_ty = try mod.ptrType(.{
  25409                     .child = .usize_type,
  25410                     .flags = .{
  25411                         .is_const = !attr_ptr_ty.ptrIsMutable(mod),
  25412                         .is_volatile = attr_ptr_ty.isVolatilePtr(mod),
  25413                         .address_space = attr_ptr_ty.ptrAddressSpace(mod),
  25414                     },
  25415                 });
  25416 
  25417                 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
  25418                     return sema.addConstant((try mod.intern(.{ .ptr = .{
  25419                         .ty = result_ty.toIntern(),
  25420                         .addr = .{ .field = .{
  25421                             .base = val.toIntern(),
  25422                             .index = Value.slice_len_index,
  25423                         } },
  25424                     } })).toValue());
  25425                 }
  25426                 try sema.requireRuntimeBlock(block, src, null);
  25427 
  25428                 return block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr);
  25429             } else {
  25430                 return sema.fail(
  25431                     block,
  25432                     field_name_src,
  25433                     "no member named '{}' in '{}'",
  25434                     .{ field_name.fmt(ip), object_ty.fmt(mod) },
  25435                 );
  25436             }
  25437         },
  25438         .Type => {
  25439             _ = try sema.resolveConstValue(block, .unneeded, object_ptr, "");
  25440             const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
  25441             const inner = if (is_pointer_to)
  25442                 try sema.analyzeLoad(block, src, result, object_ptr_src)
  25443             else
  25444                 result;
  25445 
  25446             const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
  25447             const child_type = val.toType();
  25448 
  25449             switch (child_type.zigTypeTag(mod)) {
  25450                 .ErrorSet => {
  25451                     switch (ip.indexToKey(child_type.toIntern())) {
  25452                         .error_set_type => |error_set_type| blk: {
  25453                             if (error_set_type.nameIndex(ip, field_name) != null) {
  25454                                 break :blk;
  25455                             }
  25456                             return sema.fail(block, src, "no error named '{}' in '{}'", .{
  25457                                 field_name.fmt(ip), child_type.fmt(mod),
  25458                             });
  25459                         },
  25460                         .inferred_error_set_type => {
  25461                             return sema.fail(block, src, "TODO handle inferred error sets here", .{});
  25462                         },
  25463                         .simple_type => |t| {
  25464                             assert(t == .anyerror);
  25465                             _ = try mod.getErrorValue(field_name);
  25466                         },
  25467                         else => unreachable,
  25468                     }
  25469 
  25470                     var anon_decl = try block.startAnonDecl();
  25471                     defer anon_decl.deinit();
  25472                     const error_set_type = if (!child_type.isAnyError(mod))
  25473                         child_type
  25474                     else
  25475                         try mod.singleErrorSetType(field_name);
  25476                     return sema.analyzeDeclRef(try anon_decl.finish(
  25477                         error_set_type,
  25478                         (try mod.intern(.{ .err = .{
  25479                             .ty = error_set_type.toIntern(),
  25480                             .name = field_name,
  25481                         } })).toValue(),
  25482                         .none, // default alignment
  25483                     ));
  25484                 },
  25485                 .Union => {
  25486                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25487                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
  25488                             return inst;
  25489                         }
  25490                     }
  25491                     const union_ty = try sema.resolveTypeFields(child_type);
  25492                     if (union_ty.unionTagType(mod)) |enum_ty| {
  25493                         if (enum_ty.enumFieldIndex(field_name, mod)) |field_index| {
  25494                             const field_index_u32 = @as(u32, @intCast(field_index));
  25495                             var anon_decl = try block.startAnonDecl();
  25496                             defer anon_decl.deinit();
  25497                             return sema.analyzeDeclRef(try anon_decl.finish(
  25498                                 enum_ty,
  25499                                 try mod.enumValueFieldIndex(enum_ty, field_index_u32),
  25500                                 .none, // default alignment
  25501                             ));
  25502                         }
  25503                     }
  25504                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  25505                 },
  25506                 .Enum => {
  25507                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25508                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
  25509                             return inst;
  25510                         }
  25511                     }
  25512                     const field_index = child_type.enumFieldIndex(field_name, mod) orelse {
  25513                         return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  25514                     };
  25515                     const field_index_u32 = @as(u32, @intCast(field_index));
  25516                     var anon_decl = try block.startAnonDecl();
  25517                     defer anon_decl.deinit();
  25518                     return sema.analyzeDeclRef(try anon_decl.finish(
  25519                         child_type,
  25520                         try mod.enumValueFieldIndex(child_type, field_index_u32),
  25521                         .none, // default alignment
  25522                     ));
  25523                 },
  25524                 .Struct, .Opaque => {
  25525                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25526                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
  25527                             return inst;
  25528                         }
  25529                     }
  25530                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  25531                 },
  25532                 else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(mod)}),
  25533             }
  25534         },
  25535         .Struct => {
  25536             const inner_ptr = if (is_pointer_to)
  25537                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  25538             else
  25539                 object_ptr;
  25540             return sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
  25541         },
  25542         .Union => {
  25543             const inner_ptr = if (is_pointer_to)
  25544                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  25545             else
  25546                 object_ptr;
  25547             return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
  25548         },
  25549         else => {},
  25550     }
  25551     return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
  25552 }
  25553 
  25554 const ResolvedFieldCallee = union(enum) {
  25555     /// The LHS of the call was an actual field with this value.
  25556     direct: Air.Inst.Ref,
  25557     /// This is a method call, with the function and first argument given.
  25558     method: struct {
  25559         func_inst: Air.Inst.Ref,
  25560         arg0_inst: Air.Inst.Ref,
  25561     },
  25562 };
  25563 
  25564 fn fieldCallBind(
  25565     sema: *Sema,
  25566     block: *Block,
  25567     src: LazySrcLoc,
  25568     raw_ptr: Air.Inst.Ref,
  25569     field_name: InternPool.NullTerminatedString,
  25570     field_name_src: LazySrcLoc,
  25571 ) CompileError!ResolvedFieldCallee {
  25572     // When editing this function, note that there is corresponding logic to be edited
  25573     // in `fieldVal`. This function takes a pointer and returns a pointer.
  25574 
  25575     const mod = sema.mod;
  25576     const ip = &mod.intern_pool;
  25577     const raw_ptr_src = src; // TODO better source location
  25578     const raw_ptr_ty = sema.typeOf(raw_ptr);
  25579     const inner_ty = if (raw_ptr_ty.zigTypeTag(mod) == .Pointer and (raw_ptr_ty.ptrSize(mod) == .One or raw_ptr_ty.ptrSize(mod) == .C))
  25580         raw_ptr_ty.childType(mod)
  25581     else
  25582         return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(mod)});
  25583 
  25584     // Optionally dereference a second pointer to get the concrete type.
  25585     const is_double_ptr = inner_ty.zigTypeTag(mod) == .Pointer and inner_ty.ptrSize(mod) == .One;
  25586     const concrete_ty = if (is_double_ptr) inner_ty.childType(mod) else inner_ty;
  25587     const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty;
  25588     const object_ptr = if (is_double_ptr)
  25589         try sema.analyzeLoad(block, src, raw_ptr, src)
  25590     else
  25591         raw_ptr;
  25592 
  25593     find_field: {
  25594         switch (concrete_ty.zigTypeTag(mod)) {
  25595             .Struct => {
  25596                 const struct_ty = try sema.resolveTypeFields(concrete_ty);
  25597                 if (mod.typeToStruct(struct_ty)) |struct_obj| {
  25598                     const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
  25599                         break :find_field;
  25600                     const field_index = @as(u32, @intCast(field_index_usize));
  25601                     const field = struct_obj.fields.values()[field_index];
  25602 
  25603                     return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr);
  25604                 } else if (struct_ty.isTuple(mod)) {
  25605                     if (ip.stringEqlSlice(field_name, "len")) {
  25606                         return .{ .direct = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount(mod)) };
  25607                     }
  25608                     if (field_name.toUnsigned(ip)) |field_index| {
  25609                         if (field_index >= struct_ty.structFieldCount(mod)) break :find_field;
  25610                         return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(field_index, mod), field_index, object_ptr);
  25611                     }
  25612                 } else {
  25613                     const max = struct_ty.structFieldCount(mod);
  25614                     for (0..max) |i_usize| {
  25615                         const i = @as(u32, @intCast(i_usize));
  25616                         if (field_name == struct_ty.structFieldName(i, mod)) {
  25617                             return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(i, mod), i, object_ptr);
  25618                         }
  25619                     }
  25620                 }
  25621             },
  25622             .Union => {
  25623                 const union_ty = try sema.resolveTypeFields(concrete_ty);
  25624                 const fields = union_ty.unionFields(mod);
  25625                 const field_index_usize = fields.getIndex(field_name) orelse break :find_field;
  25626                 const field_index = @as(u32, @intCast(field_index_usize));
  25627                 const field = fields.values()[field_index];
  25628 
  25629                 return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr);
  25630             },
  25631             .Type => {
  25632                 const namespace = try sema.analyzeLoad(block, src, object_ptr, src);
  25633                 return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) };
  25634             },
  25635             else => {},
  25636         }
  25637     }
  25638 
  25639     // If we get here, we need to look for a decl in the struct type instead.
  25640     const found_decl = switch (concrete_ty.zigTypeTag(mod)) {
  25641         .Struct, .Opaque, .Union, .Enum => found_decl: {
  25642             if (concrete_ty.getNamespaceIndex(mod).unwrap()) |namespace| {
  25643                 if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| {
  25644                     try sema.addReferencedBy(block, src, decl_idx);
  25645                     const decl_val = try sema.analyzeDeclVal(block, src, decl_idx);
  25646                     const decl_type = sema.typeOf(decl_val);
  25647                     if (mod.typeToFunc(decl_type)) |func_type| f: {
  25648                         if (func_type.param_types.len == 0) break :f;
  25649 
  25650                         const first_param_type = func_type.param_types[0].toType();
  25651                         // zig fmt: off
  25652                         if (first_param_type.isGenericPoison() or (
  25653                                 first_param_type.zigTypeTag(mod) == .Pointer and
  25654                                 (first_param_type.ptrSize(mod) == .One or
  25655                                 first_param_type.ptrSize(mod) == .C) and
  25656                                 first_param_type.childType(mod).eql(concrete_ty, mod)))
  25657                         {
  25658                         // zig fmt: on
  25659                             // Note that if the param type is generic poison, we know that it must
  25660                             // specifically be `anytype` since it's the first parameter, meaning we
  25661                             // can safely assume it can be a pointer.
  25662                             // TODO: bound fn calls on rvalues should probably
  25663                             // generate a by-value argument somehow.
  25664                             return .{ .method = .{
  25665                                 .func_inst = decl_val,
  25666                                 .arg0_inst = object_ptr,
  25667                             } };
  25668                         } else if (first_param_type.eql(concrete_ty, mod)) {
  25669                             const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  25670                             return .{ .method = .{
  25671                                 .func_inst = decl_val,
  25672                                 .arg0_inst = deref,
  25673                             } };
  25674                         } else if (first_param_type.zigTypeTag(mod) == .Optional) {
  25675                             const child = first_param_type.optionalChild(mod);
  25676                             if (child.eql(concrete_ty, mod)) {
  25677                                 const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  25678                                 return .{ .method = .{
  25679                                     .func_inst = decl_val,
  25680                                     .arg0_inst = deref,
  25681                                 } };
  25682                             } else if (child.zigTypeTag(mod) == .Pointer and
  25683                                 child.ptrSize(mod) == .One and
  25684                                 child.childType(mod).eql(concrete_ty, mod))
  25685                             {
  25686                                 return .{ .method = .{
  25687                                     .func_inst = decl_val,
  25688                                     .arg0_inst = object_ptr,
  25689                                 } };
  25690                             }
  25691                         } else if (first_param_type.zigTypeTag(mod) == .ErrorUnion and
  25692                             first_param_type.errorUnionPayload(mod).eql(concrete_ty, mod))
  25693                         {
  25694                             const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  25695                             return .{ .method = .{
  25696                                 .func_inst = decl_val,
  25697                                 .arg0_inst = deref,
  25698                             } };
  25699                         }
  25700                     }
  25701                     break :found_decl decl_idx;
  25702                 }
  25703             }
  25704             break :found_decl null;
  25705         },
  25706         else => null,
  25707     };
  25708 
  25709     const msg = msg: {
  25710         const msg = try sema.errMsg(block, src, "no field or member function named '{}' in '{}'", .{
  25711             field_name.fmt(ip),
  25712             concrete_ty.fmt(mod),
  25713         });
  25714         errdefer msg.destroy(sema.gpa);
  25715         try sema.addDeclaredHereNote(msg, concrete_ty);
  25716         if (found_decl) |decl_idx| {
  25717             const decl = mod.declPtr(decl_idx);
  25718             try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "'{}' is not a member function", .{field_name.fmt(ip)});
  25719         }
  25720         break :msg msg;
  25721     };
  25722     return sema.failWithOwnedErrorMsg(msg);
  25723 }
  25724 
  25725 fn finishFieldCallBind(
  25726     sema: *Sema,
  25727     block: *Block,
  25728     src: LazySrcLoc,
  25729     ptr_ty: Type,
  25730     field_ty: Type,
  25731     field_index: u32,
  25732     object_ptr: Air.Inst.Ref,
  25733 ) CompileError!ResolvedFieldCallee {
  25734     const mod = sema.mod;
  25735     const ptr_field_ty = try mod.ptrType(.{
  25736         .child = field_ty.toIntern(),
  25737         .flags = .{
  25738             .is_const = !ptr_ty.ptrIsMutable(mod),
  25739             .address_space = ptr_ty.ptrAddressSpace(mod),
  25740         },
  25741     });
  25742 
  25743     const container_ty = ptr_ty.childType(mod);
  25744     if (container_ty.zigTypeTag(mod) == .Struct) {
  25745         if (try container_ty.structFieldValueComptime(mod, field_index)) |default_val| {
  25746             return .{ .direct = try sema.addConstant(default_val) };
  25747         }
  25748     }
  25749 
  25750     if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| {
  25751         const pointer = try sema.addConstant((try mod.intern(.{ .ptr = .{
  25752             .ty = ptr_field_ty.toIntern(),
  25753             .addr = .{ .field = .{
  25754                 .base = struct_ptr_val.toIntern(),
  25755                 .index = field_index,
  25756             } },
  25757         } })).toValue());
  25758         return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) };
  25759     }
  25760 
  25761     try sema.requireRuntimeBlock(block, src, null);
  25762     const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty);
  25763     return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) };
  25764 }
  25765 
  25766 fn namespaceLookup(
  25767     sema: *Sema,
  25768     block: *Block,
  25769     src: LazySrcLoc,
  25770     namespace: Namespace.Index,
  25771     decl_name: InternPool.NullTerminatedString,
  25772 ) CompileError!?Decl.Index {
  25773     const mod = sema.mod;
  25774     const gpa = sema.gpa;
  25775     if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
  25776         const decl = mod.declPtr(decl_index);
  25777         if (!decl.is_pub and decl.getFileScope(mod) != block.getFileScope(mod)) {
  25778             const msg = msg: {
  25779                 const msg = try sema.errMsg(block, src, "'{}' is not marked 'pub'", .{
  25780                     decl_name.fmt(&mod.intern_pool),
  25781                 });
  25782                 errdefer msg.destroy(gpa);
  25783                 try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "declared here", .{});
  25784                 break :msg msg;
  25785             };
  25786             return sema.failWithOwnedErrorMsg(msg);
  25787         }
  25788         return decl_index;
  25789     }
  25790     return null;
  25791 }
  25792 
  25793 fn namespaceLookupRef(
  25794     sema: *Sema,
  25795     block: *Block,
  25796     src: LazySrcLoc,
  25797     namespace: Namespace.Index,
  25798     decl_name: InternPool.NullTerminatedString,
  25799 ) CompileError!?Air.Inst.Ref {
  25800     const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
  25801     try sema.addReferencedBy(block, src, decl);
  25802     return try sema.analyzeDeclRef(decl);
  25803 }
  25804 
  25805 fn namespaceLookupVal(
  25806     sema: *Sema,
  25807     block: *Block,
  25808     src: LazySrcLoc,
  25809     namespace: Namespace.Index,
  25810     decl_name: InternPool.NullTerminatedString,
  25811 ) CompileError!?Air.Inst.Ref {
  25812     const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
  25813     return try sema.analyzeDeclVal(block, src, decl);
  25814 }
  25815 
  25816 fn structFieldPtr(
  25817     sema: *Sema,
  25818     block: *Block,
  25819     src: LazySrcLoc,
  25820     struct_ptr: Air.Inst.Ref,
  25821     field_name: InternPool.NullTerminatedString,
  25822     field_name_src: LazySrcLoc,
  25823     unresolved_struct_ty: Type,
  25824     initializing: bool,
  25825 ) CompileError!Air.Inst.Ref {
  25826     const mod = sema.mod;
  25827     assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct);
  25828 
  25829     const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty);
  25830     try sema.resolveStructLayout(struct_ty);
  25831 
  25832     if (struct_ty.isTuple(mod)) {
  25833         if (mod.intern_pool.stringEqlSlice(field_name, "len")) {
  25834             const len_inst = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount(mod));
  25835             return sema.analyzeRef(block, src, len_inst);
  25836         }
  25837         const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
  25838         return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing);
  25839     } else if (struct_ty.isAnonStruct(mod)) {
  25840         const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
  25841         return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing);
  25842     }
  25843 
  25844     const struct_obj = mod.typeToStruct(struct_ty).?;
  25845 
  25846     const field_index_big = struct_obj.fields.getIndex(field_name) orelse
  25847         return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
  25848     const field_index = @as(u32, @intCast(field_index_big));
  25849 
  25850     return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, field_name_src, struct_ty, initializing);
  25851 }
  25852 
  25853 fn structFieldPtrByIndex(
  25854     sema: *Sema,
  25855     block: *Block,
  25856     src: LazySrcLoc,
  25857     struct_ptr: Air.Inst.Ref,
  25858     field_index: u32,
  25859     field_src: LazySrcLoc,
  25860     struct_ty: Type,
  25861     initializing: bool,
  25862 ) CompileError!Air.Inst.Ref {
  25863     const mod = sema.mod;
  25864     if (struct_ty.isAnonStruct(mod)) {
  25865         return sema.tupleFieldPtr(block, src, struct_ptr, field_src, field_index, initializing);
  25866     }
  25867 
  25868     const struct_obj = mod.typeToStruct(struct_ty).?;
  25869     const field = struct_obj.fields.values()[field_index];
  25870     const struct_ptr_ty = sema.typeOf(struct_ptr);
  25871     const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod);
  25872 
  25873     var ptr_ty_data: InternPool.Key.PtrType = .{
  25874         .child = field.ty.toIntern(),
  25875         .flags = .{
  25876             .is_const = struct_ptr_ty_info.flags.is_const,
  25877             .is_volatile = struct_ptr_ty_info.flags.is_volatile,
  25878             .address_space = struct_ptr_ty_info.flags.address_space,
  25879         },
  25880     };
  25881 
  25882     const target = mod.getTarget();
  25883 
  25884     const parent_align = struct_ptr_ty_info.flags.alignment.toByteUnitsOptional() orelse
  25885         try sema.typeAbiAlignment(struct_ptr_ty_info.child.toType());
  25886 
  25887     if (struct_obj.layout == .Packed) {
  25888         comptime assert(Type.packed_struct_layout_version == 2);
  25889 
  25890         var running_bits: u16 = 0;
  25891         for (struct_obj.fields.values(), 0..) |f, i| {
  25892             if (!(try sema.typeHasRuntimeBits(f.ty))) continue;
  25893 
  25894             if (i == field_index) {
  25895                 ptr_ty_data.packed_offset.bit_offset = running_bits;
  25896             }
  25897             running_bits += @as(u16, @intCast(f.ty.bitSize(mod)));
  25898         }
  25899         ptr_ty_data.packed_offset.host_size = (running_bits + 7) / 8;
  25900 
  25901         // If this is a packed struct embedded in another one, we need to offset
  25902         // the bits against each other.
  25903         if (struct_ptr_ty_info.packed_offset.host_size != 0) {
  25904             ptr_ty_data.packed_offset.host_size = struct_ptr_ty_info.packed_offset.host_size;
  25905             ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset;
  25906         }
  25907 
  25908         ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align);
  25909 
  25910         // If the field happens to be byte-aligned, simplify the pointer type.
  25911         // The pointee type bit size must match its ABI byte size so that loads and stores
  25912         // do not interfere with the surrounding packed bits.
  25913         // We do not attempt this with big-endian targets yet because of nested
  25914         // structs and floats. I need to double-check the desired behavior for big endian
  25915         // targets before adding the necessary complications to this code. This will not
  25916         // cause miscompilations; it only means the field pointer uses bit masking when it
  25917         // might not be strictly necessary.
  25918         if (parent_align != 0 and ptr_ty_data.packed_offset.bit_offset % 8 == 0 and
  25919             target.cpu.arch.endian() == .Little)
  25920         {
  25921             const elem_size_bytes = ptr_ty_data.child.toType().abiSize(mod);
  25922             const elem_size_bits = ptr_ty_data.child.toType().bitSize(mod);
  25923             if (elem_size_bytes * 8 == elem_size_bits) {
  25924                 const byte_offset = ptr_ty_data.packed_offset.bit_offset / 8;
  25925                 const new_align = @as(Alignment, @enumFromInt(@ctz(byte_offset | parent_align)));
  25926                 assert(new_align != .none);
  25927                 ptr_ty_data.flags.alignment = new_align;
  25928                 ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 };
  25929             }
  25930         }
  25931     } else if (struct_obj.layout == .Extern) {
  25932         // For extern structs, field aligment might be bigger than type's natural alignment. Eg, in
  25933         // `extern struct { x: u32, y: u16 }` the second field is aligned as u32.
  25934         const field_offset = struct_ty.structFieldOffset(field_index, mod);
  25935         ptr_ty_data.flags.alignment = Alignment.fromByteUnits(
  25936             if (parent_align == 0) 0 else std.math.gcd(field_offset, parent_align),
  25937         );
  25938     } else {
  25939         // Our alignment is capped at the field alignment
  25940         const field_align = try sema.structFieldAlignment(field, struct_obj.layout);
  25941         ptr_ty_data.flags.alignment = Alignment.fromByteUnits(@min(field_align, parent_align));
  25942     }
  25943 
  25944     const ptr_field_ty = try mod.ptrType(ptr_ty_data);
  25945 
  25946     if (field.is_comptime) {
  25947         const val = try mod.intern(.{ .ptr = .{
  25948             .ty = ptr_field_ty.toIntern(),
  25949             .addr = .{ .comptime_field = field.default_val },
  25950         } });
  25951         return sema.addConstant(val.toValue());
  25952     }
  25953 
  25954     if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
  25955         const val = try mod.intern(.{ .ptr = .{
  25956             .ty = ptr_field_ty.toIntern(),
  25957             .addr = .{ .field = .{
  25958                 .base = try struct_ptr_val.intern(struct_ptr_ty, mod),
  25959                 .index = field_index,
  25960             } },
  25961         } });
  25962         return sema.addConstant(val.toValue());
  25963     }
  25964 
  25965     try sema.requireRuntimeBlock(block, src, null);
  25966     return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty);
  25967 }
  25968 
  25969 fn structFieldVal(
  25970     sema: *Sema,
  25971     block: *Block,
  25972     src: LazySrcLoc,
  25973     struct_byval: Air.Inst.Ref,
  25974     field_name: InternPool.NullTerminatedString,
  25975     field_name_src: LazySrcLoc,
  25976     unresolved_struct_ty: Type,
  25977 ) CompileError!Air.Inst.Ref {
  25978     const mod = sema.mod;
  25979     assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct);
  25980 
  25981     const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty);
  25982     switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) {
  25983         .struct_type => |struct_type| {
  25984             const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
  25985             if (struct_obj.is_tuple) return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty);
  25986 
  25987             const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
  25988                 return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
  25989             const field_index = @as(u32, @intCast(field_index_usize));
  25990             const field = struct_obj.fields.values()[field_index];
  25991 
  25992             if (field.is_comptime) {
  25993                 return sema.addConstant(field.default_val.toValue());
  25994             }
  25995 
  25996             if (try sema.resolveMaybeUndefVal(struct_byval)) |struct_val| {
  25997                 if (struct_val.isUndef(mod)) return sema.addConstUndef(field.ty);
  25998                 if ((try sema.typeHasOnePossibleValue(field.ty))) |opv| {
  25999                     return sema.addConstant(opv);
  26000                 }
  26001                 return sema.addConstant(try struct_val.fieldValue(mod, field_index));
  26002             }
  26003 
  26004             try sema.requireRuntimeBlock(block, src, null);
  26005             return block.addStructFieldVal(struct_byval, field_index, field.ty);
  26006         },
  26007         .anon_struct_type => |anon_struct| {
  26008             if (anon_struct.names.len == 0) {
  26009                 return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty);
  26010             } else {
  26011                 const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
  26012                 return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty);
  26013             }
  26014         },
  26015         else => unreachable,
  26016     }
  26017 }
  26018 
  26019 fn tupleFieldVal(
  26020     sema: *Sema,
  26021     block: *Block,
  26022     src: LazySrcLoc,
  26023     tuple_byval: Air.Inst.Ref,
  26024     field_name: InternPool.NullTerminatedString,
  26025     field_name_src: LazySrcLoc,
  26026     tuple_ty: Type,
  26027 ) CompileError!Air.Inst.Ref {
  26028     const mod = sema.mod;
  26029     if (mod.intern_pool.stringEqlSlice(field_name, "len")) {
  26030         return sema.addIntUnsigned(Type.usize, tuple_ty.structFieldCount(mod));
  26031     }
  26032     const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src);
  26033     return sema.tupleFieldValByIndex(block, src, tuple_byval, field_index, tuple_ty);
  26034 }
  26035 
  26036 /// Asserts that `field_name` is not "len".
  26037 fn tupleFieldIndex(
  26038     sema: *Sema,
  26039     block: *Block,
  26040     tuple_ty: Type,
  26041     field_name: InternPool.NullTerminatedString,
  26042     field_name_src: LazySrcLoc,
  26043 ) CompileError!u32 {
  26044     const mod = sema.mod;
  26045     assert(!mod.intern_pool.stringEqlSlice(field_name, "len"));
  26046     if (field_name.toUnsigned(&mod.intern_pool)) |field_index| {
  26047         if (field_index < tuple_ty.structFieldCount(mod)) return field_index;
  26048         return sema.fail(block, field_name_src, "index '{}' out of bounds of tuple '{}'", .{
  26049             field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod),
  26050         });
  26051     }
  26052 
  26053     return sema.fail(block, field_name_src, "no field named '{}' in tuple '{}'", .{
  26054         field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod),
  26055     });
  26056 }
  26057 
  26058 fn tupleFieldValByIndex(
  26059     sema: *Sema,
  26060     block: *Block,
  26061     src: LazySrcLoc,
  26062     tuple_byval: Air.Inst.Ref,
  26063     field_index: u32,
  26064     tuple_ty: Type,
  26065 ) CompileError!Air.Inst.Ref {
  26066     const mod = sema.mod;
  26067     const field_ty = tuple_ty.structFieldType(field_index, mod);
  26068 
  26069     if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| {
  26070         return sema.addConstant(default_value);
  26071     }
  26072 
  26073     if (try sema.resolveMaybeUndefVal(tuple_byval)) |tuple_val| {
  26074         if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| {
  26075             return sema.addConstant(opv);
  26076         }
  26077         return switch (mod.intern_pool.indexToKey(tuple_val.toIntern())) {
  26078             .undef => sema.addConstUndef(field_ty),
  26079             .aggregate => |aggregate| sema.addConstant(switch (aggregate.storage) {
  26080                 .bytes => |bytes| try mod.intValue(Type.u8, bytes[0]),
  26081                 .elems => |elems| elems[field_index].toValue(),
  26082                 .repeated_elem => |elem| elem.toValue(),
  26083             }),
  26084             else => unreachable,
  26085         };
  26086     }
  26087 
  26088     if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| {
  26089         return sema.addConstant(default_val);
  26090     }
  26091 
  26092     try sema.requireRuntimeBlock(block, src, null);
  26093     return block.addStructFieldVal(tuple_byval, field_index, field_ty);
  26094 }
  26095 
  26096 fn unionFieldPtr(
  26097     sema: *Sema,
  26098     block: *Block,
  26099     src: LazySrcLoc,
  26100     union_ptr: Air.Inst.Ref,
  26101     field_name: InternPool.NullTerminatedString,
  26102     field_name_src: LazySrcLoc,
  26103     unresolved_union_ty: Type,
  26104     initializing: bool,
  26105 ) CompileError!Air.Inst.Ref {
  26106     const mod = sema.mod;
  26107     const ip = &mod.intern_pool;
  26108 
  26109     assert(unresolved_union_ty.zigTypeTag(mod) == .Union);
  26110 
  26111     const union_ptr_ty = sema.typeOf(union_ptr);
  26112     const union_ptr_info = union_ptr_ty.ptrInfo(mod);
  26113     const union_ty = try sema.resolveTypeFields(unresolved_union_ty);
  26114     const union_obj = mod.typeToUnion(union_ty).?;
  26115     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
  26116     const field = union_obj.fields.values()[field_index];
  26117     const ptr_field_ty = try mod.ptrType(.{
  26118         .child = field.ty.toIntern(),
  26119         .flags = .{
  26120             .is_const = union_ptr_info.flags.is_const,
  26121             .is_volatile = union_ptr_info.flags.is_volatile,
  26122             .address_space = union_ptr_info.flags.address_space,
  26123             .alignment = if (union_obj.layout == .Auto) blk: {
  26124                 const union_align = union_ptr_info.flags.alignment.toByteUnitsOptional() orelse try sema.typeAbiAlignment(union_ty);
  26125                 const field_align = try sema.unionFieldAlignment(field);
  26126                 break :blk InternPool.Alignment.fromByteUnits(@min(union_align, field_align));
  26127             } else union_ptr_info.flags.alignment,
  26128         },
  26129         .packed_offset = union_ptr_info.packed_offset,
  26130     });
  26131     const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?));
  26132 
  26133     if (initializing and field.ty.zigTypeTag(mod) == .NoReturn) {
  26134         const msg = msg: {
  26135             const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{});
  26136             errdefer msg.destroy(sema.gpa);
  26137 
  26138             try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{
  26139                 field_name.fmt(ip),
  26140             });
  26141             try sema.addDeclaredHereNote(msg, union_ty);
  26142             break :msg msg;
  26143         };
  26144         return sema.failWithOwnedErrorMsg(msg);
  26145     }
  26146 
  26147     if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
  26148         switch (union_obj.layout) {
  26149             .Auto => if (!initializing) {
  26150                 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse
  26151                     break :ct;
  26152                 if (union_val.isUndef(mod)) {
  26153                     return sema.failWithUseOfUndef(block, src);
  26154                 }
  26155                 const un = ip.indexToKey(union_val.toIntern()).un;
  26156                 const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index);
  26157                 const tag_matches = un.tag == field_tag.toIntern();
  26158                 if (!tag_matches) {
  26159                     const msg = msg: {
  26160                         const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?;
  26161                         const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod);
  26162                         const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{
  26163                             field_name.fmt(ip),
  26164                             active_field_name.fmt(ip),
  26165                         });
  26166                         errdefer msg.destroy(sema.gpa);
  26167                         try sema.addDeclaredHereNote(msg, union_ty);
  26168                         break :msg msg;
  26169                     };
  26170                     return sema.failWithOwnedErrorMsg(msg);
  26171                 }
  26172             },
  26173             .Packed, .Extern => {},
  26174         }
  26175         return sema.addConstant((try mod.intern(.{ .ptr = .{
  26176             .ty = ptr_field_ty.toIntern(),
  26177             .addr = .{ .field = .{
  26178                 .base = union_ptr_val.toIntern(),
  26179                 .index = field_index,
  26180             } },
  26181         } })).toValue());
  26182     }
  26183 
  26184     try sema.requireRuntimeBlock(block, src, null);
  26185     if (!initializing and union_obj.layout == .Auto and block.wantSafety() and
  26186         union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1)
  26187     {
  26188         const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index);
  26189         const wanted_tag = try sema.addConstant(wanted_tag_val);
  26190         // TODO would it be better if get_union_tag supported pointers to unions?
  26191         const union_val = try block.addTyOp(.load, union_ty, union_ptr);
  26192         const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val);
  26193         try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
  26194     }
  26195     if (field.ty.zigTypeTag(mod) == .NoReturn) {
  26196         _ = try block.addNoOp(.unreach);
  26197         return Air.Inst.Ref.unreachable_value;
  26198     }
  26199     return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);
  26200 }
  26201 
  26202 fn unionFieldVal(
  26203     sema: *Sema,
  26204     block: *Block,
  26205     src: LazySrcLoc,
  26206     union_byval: Air.Inst.Ref,
  26207     field_name: InternPool.NullTerminatedString,
  26208     field_name_src: LazySrcLoc,
  26209     unresolved_union_ty: Type,
  26210 ) CompileError!Air.Inst.Ref {
  26211     const mod = sema.mod;
  26212     const ip = &mod.intern_pool;
  26213     assert(unresolved_union_ty.zigTypeTag(mod) == .Union);
  26214 
  26215     const union_ty = try sema.resolveTypeFields(unresolved_union_ty);
  26216     const union_obj = mod.typeToUnion(union_ty).?;
  26217     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
  26218     const field = union_obj.fields.values()[field_index];
  26219     const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?));
  26220 
  26221     if (try sema.resolveMaybeUndefVal(union_byval)) |union_val| {
  26222         if (union_val.isUndef(mod)) return sema.addConstUndef(field.ty);
  26223 
  26224         const un = ip.indexToKey(union_val.toIntern()).un;
  26225         const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index);
  26226         const tag_matches = un.tag == field_tag.toIntern();
  26227         switch (union_obj.layout) {
  26228             .Auto => {
  26229                 if (tag_matches) {
  26230                     return sema.addConstant(un.val.toValue());
  26231                 } else {
  26232                     const msg = msg: {
  26233                         const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?;
  26234                         const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod);
  26235                         const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{
  26236                             field_name.fmt(ip), active_field_name.fmt(ip),
  26237                         });
  26238                         errdefer msg.destroy(sema.gpa);
  26239                         try sema.addDeclaredHereNote(msg, union_ty);
  26240                         break :msg msg;
  26241                     };
  26242                     return sema.failWithOwnedErrorMsg(msg);
  26243                 }
  26244             },
  26245             .Packed, .Extern => {
  26246                 if (tag_matches) {
  26247                     return sema.addConstant(un.val.toValue());
  26248                 } else {
  26249                     const old_ty = union_ty.unionFieldType(un.tag.toValue(), mod);
  26250                     if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field.ty, 0)) |new_val| {
  26251                         return sema.addConstant(new_val);
  26252                     }
  26253                 }
  26254             },
  26255         }
  26256     }
  26257 
  26258     try sema.requireRuntimeBlock(block, src, null);
  26259     if (union_obj.layout == .Auto and block.wantSafety() and
  26260         union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1)
  26261     {
  26262         const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index);
  26263         const wanted_tag = try sema.addConstant(wanted_tag_val);
  26264         const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval);
  26265         try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
  26266     }
  26267     if (field.ty.zigTypeTag(mod) == .NoReturn) {
  26268         _ = try block.addNoOp(.unreach);
  26269         return Air.Inst.Ref.unreachable_value;
  26270     }
  26271     return block.addStructFieldVal(union_byval, field_index, field.ty);
  26272 }
  26273 
  26274 fn elemPtr(
  26275     sema: *Sema,
  26276     block: *Block,
  26277     src: LazySrcLoc,
  26278     indexable_ptr: Air.Inst.Ref,
  26279     elem_index: Air.Inst.Ref,
  26280     elem_index_src: LazySrcLoc,
  26281     init: bool,
  26282     oob_safety: bool,
  26283 ) CompileError!Air.Inst.Ref {
  26284     const mod = sema.mod;
  26285     const indexable_ptr_src = src; // TODO better source location
  26286     const indexable_ptr_ty = sema.typeOf(indexable_ptr);
  26287 
  26288     const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(mod)) {
  26289         .Pointer => indexable_ptr_ty.childType(mod),
  26290         else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(mod)}),
  26291     };
  26292     try checkIndexable(sema, block, src, indexable_ty);
  26293 
  26294     switch (indexable_ty.zigTypeTag(mod)) {
  26295         .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
  26296         .Struct => {
  26297             // Tuple field access.
  26298             const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
  26299             const index = @as(u32, @intCast(index_val.toUnsignedInt(mod)));
  26300             return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
  26301         },
  26302         else => {
  26303             const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
  26304             return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety);
  26305         },
  26306     }
  26307 }
  26308 
  26309 /// Asserts that the type of indexable is pointer.
  26310 fn elemPtrOneLayerOnly(
  26311     sema: *Sema,
  26312     block: *Block,
  26313     src: LazySrcLoc,
  26314     indexable: Air.Inst.Ref,
  26315     elem_index: Air.Inst.Ref,
  26316     elem_index_src: LazySrcLoc,
  26317     init: bool,
  26318     oob_safety: bool,
  26319 ) CompileError!Air.Inst.Ref {
  26320     const indexable_src = src; // TODO better source location
  26321     const indexable_ty = sema.typeOf(indexable);
  26322     const mod = sema.mod;
  26323 
  26324     try checkIndexable(sema, block, src, indexable_ty);
  26325 
  26326     switch (indexable_ty.ptrSize(mod)) {
  26327         .Slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  26328         .Many, .C => {
  26329             const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
  26330             const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  26331             const runtime_src = rs: {
  26332                 const ptr_val = maybe_ptr_val orelse break :rs indexable_src;
  26333                 const index_val = maybe_index_val orelse break :rs elem_index_src;
  26334                 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod)));
  26335                 const result_ty = try sema.elemPtrType(indexable_ty, index);
  26336                 const elem_ptr = try ptr_val.elemPtr(result_ty, index, mod);
  26337                 return sema.addConstant(elem_ptr);
  26338             };
  26339             const result_ty = try sema.elemPtrType(indexable_ty, null);
  26340 
  26341             try sema.requireRuntimeBlock(block, src, runtime_src);
  26342             return block.addPtrElemPtr(indexable, elem_index, result_ty);
  26343         },
  26344         .One => {
  26345             const child_ty = indexable_ty.childType(mod);
  26346             switch (child_ty.zigTypeTag(mod)) {
  26347                 .Array, .Vector => {
  26348                     return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety);
  26349                 },
  26350                 .Struct => {
  26351                     assert(child_ty.isTuple(mod));
  26352                     const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
  26353                     const index = @as(u32, @intCast(index_val.toUnsignedInt(mod)));
  26354                     return sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false);
  26355                 },
  26356                 else => unreachable, // Guaranteed by checkIndexable
  26357             }
  26358         },
  26359     }
  26360 }
  26361 
  26362 fn elemVal(
  26363     sema: *Sema,
  26364     block: *Block,
  26365     src: LazySrcLoc,
  26366     indexable: Air.Inst.Ref,
  26367     elem_index_uncasted: Air.Inst.Ref,
  26368     elem_index_src: LazySrcLoc,
  26369     oob_safety: bool,
  26370 ) CompileError!Air.Inst.Ref {
  26371     const indexable_src = src; // TODO better source location
  26372     const indexable_ty = sema.typeOf(indexable);
  26373     const mod = sema.mod;
  26374 
  26375     try checkIndexable(sema, block, src, indexable_ty);
  26376 
  26377     // TODO in case of a vector of pointers, we need to detect whether the element
  26378     // index is a scalar or vector instead of unconditionally casting to usize.
  26379     const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src);
  26380 
  26381     switch (indexable_ty.zigTypeTag(mod)) {
  26382         .Pointer => switch (indexable_ty.ptrSize(mod)) {
  26383             .Slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  26384             .Many, .C => {
  26385                 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
  26386                 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  26387 
  26388                 const runtime_src = rs: {
  26389                     const indexable_val = maybe_indexable_val orelse break :rs indexable_src;
  26390                     const index_val = maybe_index_val orelse break :rs elem_index_src;
  26391                     const index = @as(usize, @intCast(index_val.toUnsignedInt(mod)));
  26392                     const elem_ty = indexable_ty.elemType2(mod);
  26393                     const many_ptr_ty = try mod.manyConstPtrType(elem_ty);
  26394                     const many_ptr_val = try mod.getCoerced(indexable_val, many_ptr_ty);
  26395                     const elem_ptr_ty = try mod.singleConstPtrType(elem_ty);
  26396                     const elem_ptr_val = try many_ptr_val.elemPtr(elem_ptr_ty, index, mod);
  26397                     if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty)) |elem_val| {
  26398                         return sema.addConstant(try mod.getCoerced(elem_val, elem_ty));
  26399                     }
  26400                     break :rs indexable_src;
  26401                 };
  26402 
  26403                 try sema.requireRuntimeBlock(block, src, runtime_src);
  26404                 return block.addBinOp(.ptr_elem_val, indexable, elem_index);
  26405             },
  26406             .One => {
  26407                 arr_sent: {
  26408                     const inner_ty = indexable_ty.childType(mod);
  26409                     if (inner_ty.zigTypeTag(mod) != .Array) break :arr_sent;
  26410                     const sentinel = inner_ty.sentinel(mod) orelse break :arr_sent;
  26411                     const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent;
  26412                     const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(mod));
  26413                     if (index != inner_ty.arrayLen(mod)) break :arr_sent;
  26414                     return sema.addConstant(sentinel);
  26415                 }
  26416                 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);
  26417                 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
  26418             },
  26419         },
  26420         .Array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  26421         .Vector => {
  26422             // TODO: If the index is a vector, the result should be a vector.
  26423             return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety);
  26424         },
  26425         .Struct => {
  26426             // Tuple field access.
  26427             const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
  26428             const index = @as(u32, @intCast(index_val.toUnsignedInt(mod)));
  26429             return sema.tupleField(block, indexable_src, indexable, elem_index_src, index);
  26430         },
  26431         else => unreachable,
  26432     }
  26433 }
  26434 
  26435 fn validateRuntimeElemAccess(
  26436     sema: *Sema,
  26437     block: *Block,
  26438     elem_index_src: LazySrcLoc,
  26439     elem_ty: Type,
  26440     parent_ty: Type,
  26441     parent_src: LazySrcLoc,
  26442 ) CompileError!void {
  26443     const mod = sema.mod;
  26444     const valid_rt = try sema.validateRunTimeType(elem_ty, false);
  26445     if (!valid_rt) {
  26446         const msg = msg: {
  26447             const msg = try sema.errMsg(
  26448                 block,
  26449                 elem_index_src,
  26450                 "values of type '{}' must be comptime-known, but index value is runtime-known",
  26451                 .{parent_ty.fmt(mod)},
  26452             );
  26453             errdefer msg.destroy(sema.gpa);
  26454 
  26455             const src_decl = mod.declPtr(block.src_decl);
  26456             try sema.explainWhyTypeIsComptime(msg, parent_src.toSrcLoc(src_decl, mod), parent_ty);
  26457 
  26458             break :msg msg;
  26459         };
  26460         return sema.failWithOwnedErrorMsg(msg);
  26461     }
  26462 }
  26463 
  26464 fn tupleFieldPtr(
  26465     sema: *Sema,
  26466     block: *Block,
  26467     tuple_ptr_src: LazySrcLoc,
  26468     tuple_ptr: Air.Inst.Ref,
  26469     field_index_src: LazySrcLoc,
  26470     field_index: u32,
  26471     init: bool,
  26472 ) CompileError!Air.Inst.Ref {
  26473     const mod = sema.mod;
  26474     const tuple_ptr_ty = sema.typeOf(tuple_ptr);
  26475     const tuple_ty = tuple_ptr_ty.childType(mod);
  26476     _ = try sema.resolveTypeFields(tuple_ty);
  26477     const field_count = tuple_ty.structFieldCount(mod);
  26478 
  26479     if (field_count == 0) {
  26480         return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});
  26481     }
  26482 
  26483     if (field_index >= field_count) {
  26484         return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
  26485             field_index, field_count,
  26486         });
  26487     }
  26488 
  26489     const field_ty = tuple_ty.structFieldType(field_index, mod);
  26490     const ptr_field_ty = try mod.ptrType(.{
  26491         .child = field_ty.toIntern(),
  26492         .flags = .{
  26493             .is_const = !tuple_ptr_ty.ptrIsMutable(mod),
  26494             .is_volatile = tuple_ptr_ty.isVolatilePtr(mod),
  26495             .address_space = tuple_ptr_ty.ptrAddressSpace(mod),
  26496         },
  26497     });
  26498 
  26499     if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| {
  26500         return sema.addConstant((try mod.intern(.{ .ptr = .{
  26501             .ty = ptr_field_ty.toIntern(),
  26502             .addr = .{ .comptime_field = default_val.toIntern() },
  26503         } })).toValue());
  26504     }
  26505 
  26506     if (try sema.resolveMaybeUndefVal(tuple_ptr)) |tuple_ptr_val| {
  26507         return sema.addConstant((try mod.intern(.{ .ptr = .{
  26508             .ty = ptr_field_ty.toIntern(),
  26509             .addr = .{ .field = .{
  26510                 .base = tuple_ptr_val.toIntern(),
  26511                 .index = field_index,
  26512             } },
  26513         } })).toValue());
  26514     }
  26515 
  26516     if (!init) {
  26517         try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src);
  26518     }
  26519 
  26520     try sema.requireRuntimeBlock(block, tuple_ptr_src, null);
  26521     return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);
  26522 }
  26523 
  26524 fn tupleField(
  26525     sema: *Sema,
  26526     block: *Block,
  26527     tuple_src: LazySrcLoc,
  26528     tuple: Air.Inst.Ref,
  26529     field_index_src: LazySrcLoc,
  26530     field_index: u32,
  26531 ) CompileError!Air.Inst.Ref {
  26532     const mod = sema.mod;
  26533     const tuple_ty = try sema.resolveTypeFields(sema.typeOf(tuple));
  26534     const field_count = tuple_ty.structFieldCount(mod);
  26535 
  26536     if (field_count == 0) {
  26537         return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{});
  26538     }
  26539 
  26540     if (field_index >= field_count) {
  26541         return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
  26542             field_index, field_count,
  26543         });
  26544     }
  26545 
  26546     const field_ty = tuple_ty.structFieldType(field_index, mod);
  26547 
  26548     if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| {
  26549         return sema.addConstant(default_value); // comptime field
  26550     }
  26551 
  26552     if (try sema.resolveMaybeUndefVal(tuple)) |tuple_val| {
  26553         if (tuple_val.isUndef(mod)) return sema.addConstUndef(field_ty);
  26554         return sema.addConstant(try tuple_val.fieldValue(mod, field_index));
  26555     }
  26556 
  26557     try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src);
  26558 
  26559     try sema.requireRuntimeBlock(block, tuple_src, null);
  26560     return block.addStructFieldVal(tuple, field_index, field_ty);
  26561 }
  26562 
  26563 fn elemValArray(
  26564     sema: *Sema,
  26565     block: *Block,
  26566     src: LazySrcLoc,
  26567     array_src: LazySrcLoc,
  26568     array: Air.Inst.Ref,
  26569     elem_index_src: LazySrcLoc,
  26570     elem_index: Air.Inst.Ref,
  26571     oob_safety: bool,
  26572 ) CompileError!Air.Inst.Ref {
  26573     const mod = sema.mod;
  26574     const array_ty = sema.typeOf(array);
  26575     const array_sent = array_ty.sentinel(mod);
  26576     const array_len = array_ty.arrayLen(mod);
  26577     const array_len_s = array_len + @intFromBool(array_sent != null);
  26578     const elem_ty = array_ty.childType(mod);
  26579 
  26580     if (array_len_s == 0) {
  26581         return sema.fail(block, array_src, "indexing into empty array is not allowed", .{});
  26582     }
  26583 
  26584     const maybe_undef_array_val = try sema.resolveMaybeUndefVal(array);
  26585     // index must be defined since it can access out of bounds
  26586     const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  26587 
  26588     if (maybe_index_val) |index_val| {
  26589         const index = @as(usize, @intCast(index_val.toUnsignedInt(mod)));
  26590         if (array_sent) |s| {
  26591             if (index == array_len) {
  26592                 return sema.addConstant(s);
  26593             }
  26594         }
  26595         if (index >= array_len_s) {
  26596             const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else "";
  26597             return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
  26598         }
  26599     }
  26600     if (maybe_undef_array_val) |array_val| {
  26601         if (array_val.isUndef(mod)) {
  26602             return sema.addConstUndef(elem_ty);
  26603         }
  26604         if (maybe_index_val) |index_val| {
  26605             const index = @as(usize, @intCast(index_val.toUnsignedInt(mod)));
  26606             const elem_val = try array_val.elemValue(mod, index);
  26607             return sema.addConstant(elem_val);
  26608         }
  26609     }
  26610 
  26611     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src);
  26612 
  26613     const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src;
  26614     try sema.requireRuntimeBlock(block, src, runtime_src);
  26615     if (oob_safety and block.wantSafety()) {
  26616         // Runtime check is only needed if unable to comptime check
  26617         if (maybe_index_val == null) {
  26618             const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
  26619             const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
  26620             try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
  26621         }
  26622     }
  26623     return block.addBinOp(.array_elem_val, array, elem_index);
  26624 }
  26625 
  26626 fn elemPtrArray(
  26627     sema: *Sema,
  26628     block: *Block,
  26629     src: LazySrcLoc,
  26630     array_ptr_src: LazySrcLoc,
  26631     array_ptr: Air.Inst.Ref,
  26632     elem_index_src: LazySrcLoc,
  26633     elem_index: Air.Inst.Ref,
  26634     init: bool,
  26635     oob_safety: bool,
  26636 ) CompileError!Air.Inst.Ref {
  26637     const mod = sema.mod;
  26638     const array_ptr_ty = sema.typeOf(array_ptr);
  26639     const array_ty = array_ptr_ty.childType(mod);
  26640     const array_sent = array_ty.sentinel(mod) != null;
  26641     const array_len = array_ty.arrayLen(mod);
  26642     const array_len_s = array_len + @intFromBool(array_sent);
  26643 
  26644     if (array_len_s == 0) {
  26645         return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{});
  26646     }
  26647 
  26648     const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(array_ptr);
  26649     // The index must not be undefined since it can be out of bounds.
  26650     const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
  26651         const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod));
  26652         if (index >= array_len_s) {
  26653             const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
  26654             return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
  26655         }
  26656         break :o index;
  26657     } else null;
  26658 
  26659     const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset);
  26660 
  26661     if (maybe_undef_array_ptr_val) |array_ptr_val| {
  26662         if (array_ptr_val.isUndef(mod)) {
  26663             return sema.addConstUndef(elem_ptr_ty);
  26664         }
  26665         if (offset) |index| {
  26666             const elem_ptr = try array_ptr_val.elemPtr(elem_ptr_ty, index, mod);
  26667             return sema.addConstant(elem_ptr);
  26668         }
  26669     }
  26670 
  26671     if (!init) {
  26672         try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(mod), array_ty, array_ptr_src);
  26673     }
  26674 
  26675     const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src;
  26676     try sema.requireRuntimeBlock(block, src, runtime_src);
  26677 
  26678     // Runtime check is only needed if unable to comptime check.
  26679     if (oob_safety and block.wantSafety() and offset == null) {
  26680         const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
  26681         const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
  26682         try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
  26683     }
  26684 
  26685     return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
  26686 }
  26687 
  26688 fn elemValSlice(
  26689     sema: *Sema,
  26690     block: *Block,
  26691     src: LazySrcLoc,
  26692     slice_src: LazySrcLoc,
  26693     slice: Air.Inst.Ref,
  26694     elem_index_src: LazySrcLoc,
  26695     elem_index: Air.Inst.Ref,
  26696     oob_safety: bool,
  26697 ) CompileError!Air.Inst.Ref {
  26698     const mod = sema.mod;
  26699     const slice_ty = sema.typeOf(slice);
  26700     const slice_sent = slice_ty.sentinel(mod) != null;
  26701     const elem_ty = slice_ty.elemType2(mod);
  26702     var runtime_src = slice_src;
  26703 
  26704     // slice must be defined since it can dereferenced as null
  26705     const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice);
  26706     // index must be defined since it can index out of bounds
  26707     const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  26708 
  26709     if (maybe_slice_val) |slice_val| {
  26710         runtime_src = elem_index_src;
  26711         const slice_len = slice_val.sliceLen(mod);
  26712         const slice_len_s = slice_len + @intFromBool(slice_sent);
  26713         if (slice_len_s == 0) {
  26714             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
  26715         }
  26716         if (maybe_index_val) |index_val| {
  26717             const index = @as(usize, @intCast(index_val.toUnsignedInt(mod)));
  26718             if (index >= slice_len_s) {
  26719                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
  26720                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
  26721             }
  26722             const elem_ptr_ty = try sema.elemPtrType(slice_ty, index);
  26723             const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod);
  26724             if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| {
  26725                 return sema.addConstant(elem_val);
  26726             }
  26727             runtime_src = slice_src;
  26728         }
  26729     }
  26730 
  26731     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src);
  26732 
  26733     try sema.requireRuntimeBlock(block, src, runtime_src);
  26734     if (oob_safety and block.wantSafety()) {
  26735         const len_inst = if (maybe_slice_val) |slice_val|
  26736             try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod))
  26737         else
  26738             try block.addTyOp(.slice_len, Type.usize, slice);
  26739         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
  26740         try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
  26741     }
  26742     try sema.queueFullTypeResolution(sema.typeOf(slice));
  26743     return block.addBinOp(.slice_elem_val, slice, elem_index);
  26744 }
  26745 
  26746 fn elemPtrSlice(
  26747     sema: *Sema,
  26748     block: *Block,
  26749     src: LazySrcLoc,
  26750     slice_src: LazySrcLoc,
  26751     slice: Air.Inst.Ref,
  26752     elem_index_src: LazySrcLoc,
  26753     elem_index: Air.Inst.Ref,
  26754     oob_safety: bool,
  26755 ) CompileError!Air.Inst.Ref {
  26756     const mod = sema.mod;
  26757     const slice_ty = sema.typeOf(slice);
  26758     const slice_sent = slice_ty.sentinel(mod) != null;
  26759 
  26760     const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(slice);
  26761     // The index must not be undefined since it can be out of bounds.
  26762     const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
  26763         const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod));
  26764         break :o index;
  26765     } else null;
  26766 
  26767     const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset);
  26768 
  26769     if (maybe_undef_slice_val) |slice_val| {
  26770         if (slice_val.isUndef(mod)) {
  26771             return sema.addConstUndef(elem_ptr_ty);
  26772         }
  26773         const slice_len = slice_val.sliceLen(mod);
  26774         const slice_len_s = slice_len + @intFromBool(slice_sent);
  26775         if (slice_len_s == 0) {
  26776             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
  26777         }
  26778         if (offset) |index| {
  26779             if (index >= slice_len_s) {
  26780                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
  26781                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
  26782             }
  26783             const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod);
  26784             return sema.addConstant(elem_ptr_val);
  26785         }
  26786     }
  26787 
  26788     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src);
  26789 
  26790     const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src;
  26791     try sema.requireRuntimeBlock(block, src, runtime_src);
  26792     if (oob_safety and block.wantSafety()) {
  26793         const len_inst = len: {
  26794             if (maybe_undef_slice_val) |slice_val|
  26795                 if (!slice_val.isUndef(mod))
  26796                     break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod));
  26797             break :len try block.addTyOp(.slice_len, Type.usize, slice);
  26798         };
  26799         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
  26800         try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
  26801     }
  26802     return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
  26803 }
  26804 
  26805 fn coerce(
  26806     sema: *Sema,
  26807     block: *Block,
  26808     dest_ty_unresolved: Type,
  26809     inst: Air.Inst.Ref,
  26810     inst_src: LazySrcLoc,
  26811 ) CompileError!Air.Inst.Ref {
  26812     return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) {
  26813         error.NotCoercible => unreachable,
  26814         else => |e| return e,
  26815     };
  26816 }
  26817 
  26818 const CoersionError = CompileError || error{
  26819     /// When coerce is called recursively, this error should be returned instead of using `fail`
  26820     /// to ensure correct types in compile errors.
  26821     NotCoercible,
  26822 };
  26823 
  26824 const CoerceOpts = struct {
  26825     /// Should coerceExtra emit error messages.
  26826     report_err: bool = true,
  26827     /// Ignored if `report_err == false`.
  26828     is_ret: bool = false,
  26829     /// Should coercion to comptime_int ermit an error message.
  26830     no_cast_to_comptime_int: bool = false,
  26831 
  26832     param_src: struct {
  26833         func_inst: Air.Inst.Ref = .none,
  26834         param_i: u32 = undefined,
  26835 
  26836         fn get(info: @This(), sema: *Sema) !?Module.SrcLoc {
  26837             if (info.func_inst == .none) return null;
  26838             const mod = sema.mod;
  26839             const fn_decl = (try sema.funcDeclSrc(info.func_inst)) orelse return null;
  26840             const param_src = Module.paramSrc(0, mod, fn_decl, info.param_i);
  26841             if (param_src == .node_offset_param) {
  26842                 return Module.SrcLoc{
  26843                     .file_scope = fn_decl.getFileScope(mod),
  26844                     .parent_decl_node = fn_decl.src_node,
  26845                     .lazy = LazySrcLoc.nodeOffset(param_src.node_offset_param),
  26846                 };
  26847             }
  26848             return param_src.toSrcLoc(fn_decl, mod);
  26849         }
  26850     } = .{},
  26851 };
  26852 
  26853 fn coerceExtra(
  26854     sema: *Sema,
  26855     block: *Block,
  26856     dest_ty_unresolved: Type,
  26857     inst: Air.Inst.Ref,
  26858     inst_src: LazySrcLoc,
  26859     opts: CoerceOpts,
  26860 ) CoersionError!Air.Inst.Ref {
  26861     if (dest_ty_unresolved.isGenericPoison()) return inst;
  26862     const mod = sema.mod;
  26863     const dest_ty_src = inst_src; // TODO better source location
  26864     const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved);
  26865     const inst_ty = try sema.resolveTypeFields(sema.typeOf(inst));
  26866     const target = mod.getTarget();
  26867     // If the types are the same, we can return the operand.
  26868     if (dest_ty.eql(inst_ty, mod))
  26869         return inst;
  26870 
  26871     const maybe_inst_val = try sema.resolveMaybeUndefVal(inst);
  26872 
  26873     var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
  26874     if (in_memory_result == .ok) {
  26875         if (maybe_inst_val) |val| {
  26876             return sema.coerceInMemory(val, dest_ty);
  26877         }
  26878         try sema.requireRuntimeBlock(block, inst_src, null);
  26879         return block.addBitCast(dest_ty, inst);
  26880     }
  26881 
  26882     const is_undef = inst_ty.zigTypeTag(mod) == .Undefined;
  26883 
  26884     switch (dest_ty.zigTypeTag(mod)) {
  26885         .Optional => optional: {
  26886             // undefined sets the optional bit also to undefined.
  26887             if (is_undef) {
  26888                 return sema.addConstUndef(dest_ty);
  26889             }
  26890 
  26891             // null to ?T
  26892             if (inst_ty.zigTypeTag(mod) == .Null) {
  26893                 return sema.addConstant((try mod.intern(.{ .opt = .{
  26894                     .ty = dest_ty.toIntern(),
  26895                     .val = .none,
  26896                 } })).toValue());
  26897             }
  26898 
  26899             // cast from ?*T and ?[*]T to ?*anyopaque
  26900             // but don't do it if the source type is a double pointer
  26901             if (dest_ty.isPtrLikeOptional(mod) and
  26902                 dest_ty.elemType2(mod).toIntern() == .anyopaque_type and
  26903                 inst_ty.isPtrAtRuntime(mod))
  26904             anyopaque_check: {
  26905                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional;
  26906                 const elem_ty = inst_ty.elemType2(mod);
  26907                 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) {
  26908                     in_memory_result = .{ .double_ptr_to_anyopaque = .{
  26909                         .actual = inst_ty,
  26910                         .wanted = dest_ty,
  26911                     } };
  26912                     break :optional;
  26913                 }
  26914                 // Let the logic below handle wrapping the optional now that
  26915                 // it has been checked to correctly coerce.
  26916                 if (!inst_ty.isPtrLikeOptional(mod)) break :anyopaque_check;
  26917                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  26918             }
  26919 
  26920             // T to ?T
  26921             const child_type = dest_ty.optionalChild(mod);
  26922             const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  26923                 error.NotCoercible => {
  26924                     if (in_memory_result == .no_match) {
  26925                         // Try to give more useful notes
  26926                         in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src);
  26927                     }
  26928                     break :optional;
  26929                 },
  26930                 else => |e| return e,
  26931             };
  26932             return try sema.wrapOptional(block, dest_ty, intermediate, inst_src);
  26933         },
  26934         .Pointer => pointer: {
  26935             const dest_info = dest_ty.ptrInfo(mod);
  26936 
  26937             // Function body to function pointer.
  26938             if (inst_ty.zigTypeTag(mod) == .Fn) {
  26939                 const fn_val = try sema.resolveConstValue(block, .unneeded, inst, "");
  26940                 const fn_decl = fn_val.pointerDecl(mod).?;
  26941                 const inst_as_ptr = try sema.analyzeDeclRef(fn_decl);
  26942                 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);
  26943             }
  26944 
  26945             // *T to *[1]T
  26946             single_item: {
  26947                 if (dest_info.flags.size != .One) break :single_item;
  26948                 if (!inst_ty.isSinglePointer(mod)) break :single_item;
  26949                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  26950                 const ptr_elem_ty = inst_ty.childType(mod);
  26951                 const array_ty = dest_info.child.toType();
  26952                 if (array_ty.zigTypeTag(mod) != .Array) break :single_item;
  26953                 const array_elem_ty = array_ty.childType(mod);
  26954                 if (array_ty.arrayLen(mod) != 1) break :single_item;
  26955                 const dest_is_mut = !dest_info.flags.is_const;
  26956                 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
  26957                     .ok => {},
  26958                     else => break :single_item,
  26959                 }
  26960                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  26961             }
  26962 
  26963             // Coercions where the source is a single pointer to an array.
  26964             src_array_ptr: {
  26965                 if (!inst_ty.isSinglePointer(mod)) break :src_array_ptr;
  26966                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  26967                 const array_ty = inst_ty.childType(mod);
  26968                 if (array_ty.zigTypeTag(mod) != .Array) break :src_array_ptr;
  26969                 const array_elem_type = array_ty.childType(mod);
  26970                 const dest_is_mut = !dest_info.flags.is_const;
  26971 
  26972                 const dst_elem_type = dest_info.child.toType();
  26973                 const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src);
  26974                 switch (elem_res) {
  26975                     .ok => {},
  26976                     else => {
  26977                         in_memory_result = .{ .ptr_child = .{
  26978                             .child = try elem_res.dupe(sema.arena),
  26979                             .actual = array_elem_type,
  26980                             .wanted = dst_elem_type,
  26981                         } };
  26982                         break :src_array_ptr;
  26983                     },
  26984                 }
  26985 
  26986                 if (dest_info.sentinel != .none) {
  26987                     if (array_ty.sentinel(mod)) |inst_sent| {
  26988                         if (dest_info.sentinel != (try mod.getCoerced(inst_sent, dst_elem_type)).toIntern()) {
  26989                             in_memory_result = .{ .ptr_sentinel = .{
  26990                                 .actual = inst_sent,
  26991                                 .wanted = dest_info.sentinel.toValue(),
  26992                                 .ty = dst_elem_type,
  26993                             } };
  26994                             break :src_array_ptr;
  26995                         }
  26996                     } else {
  26997                         in_memory_result = .{ .ptr_sentinel = .{
  26998                             .actual = Value.@"unreachable",
  26999                             .wanted = dest_info.sentinel.toValue(),
  27000                             .ty = dst_elem_type,
  27001                         } };
  27002                         break :src_array_ptr;
  27003                     }
  27004                 }
  27005 
  27006                 switch (dest_info.flags.size) {
  27007                     .Slice => {
  27008                         // *[N]T to []T
  27009                         return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);
  27010                     },
  27011                     .C => {
  27012                         // *[N]T to [*c]T
  27013                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27014                     },
  27015                     .Many => {
  27016                         // *[N]T to [*]T
  27017                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27018                     },
  27019                     .One => {},
  27020                 }
  27021             }
  27022 
  27023             // coercion from C pointer
  27024             if (inst_ty.isCPtr(mod)) src_c_ptr: {
  27025                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr;
  27026                 // In this case we must add a safety check because the C pointer
  27027                 // could be null.
  27028                 const src_elem_ty = inst_ty.childType(mod);
  27029                 const dest_is_mut = !dest_info.flags.is_const;
  27030                 const dst_elem_type = dest_info.child.toType();
  27031                 switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
  27032                     .ok => {},
  27033                     else => break :src_c_ptr,
  27034                 }
  27035                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27036             }
  27037 
  27038             // cast from *T and [*]T to *anyopaque
  27039             // but don't do it if the source type is a double pointer
  27040             if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(mod) == .Pointer) to_anyopaque: {
  27041                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  27042                 const elem_ty = inst_ty.elemType2(mod);
  27043                 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) {
  27044                     in_memory_result = .{ .double_ptr_to_anyopaque = .{
  27045                         .actual = inst_ty,
  27046                         .wanted = dest_ty,
  27047                     } };
  27048                     break :pointer;
  27049                 }
  27050                 if (dest_ty.isSlice(mod)) break :to_anyopaque;
  27051                 if (inst_ty.isSlice(mod)) {
  27052                     in_memory_result = .{ .slice_to_anyopaque = .{
  27053                         .actual = inst_ty,
  27054                         .wanted = dest_ty,
  27055                     } };
  27056                     break :pointer;
  27057                 }
  27058                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27059             }
  27060 
  27061             switch (dest_info.flags.size) {
  27062                 // coercion to C pointer
  27063                 .C => switch (inst_ty.zigTypeTag(mod)) {
  27064                     .Null => {
  27065                         return sema.addConstant(try mod.getCoerced(Value.null, dest_ty));
  27066                     },
  27067                     .ComptimeInt => {
  27068                         const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  27069                             error.NotCoercible => break :pointer,
  27070                             else => |e| return e,
  27071                         };
  27072                         return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
  27073                     },
  27074                     .Int => {
  27075                         const ptr_size_ty = switch (inst_ty.intInfo(mod).signedness) {
  27076                             .signed => Type.isize,
  27077                             .unsigned => Type.usize,
  27078                         };
  27079                         const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  27080                             error.NotCoercible => {
  27081                                 // Try to give more useful notes
  27082                                 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src);
  27083                                 break :pointer;
  27084                             },
  27085                             else => |e| return e,
  27086                         };
  27087                         return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
  27088                     },
  27089                     .Pointer => p: {
  27090                         if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
  27091                         const inst_info = inst_ty.ptrInfo(mod);
  27092                         switch (try sema.coerceInMemoryAllowed(
  27093                             block,
  27094                             dest_info.child.toType(),
  27095                             inst_info.child.toType(),
  27096                             !dest_info.flags.is_const,
  27097                             target,
  27098                             dest_ty_src,
  27099                             inst_src,
  27100                         )) {
  27101                             .ok => {},
  27102                             else => break :p,
  27103                         }
  27104                         if (inst_info.flags.size == .Slice) {
  27105                             assert(dest_info.sentinel == .none);
  27106                             if (inst_info.sentinel == .none or
  27107                                 inst_info.sentinel != (try mod.intValue(inst_info.child.toType(), 0)).toIntern())
  27108                                 break :p;
  27109 
  27110                             const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
  27111                             return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
  27112                         }
  27113                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27114                     },
  27115                     else => {},
  27116                 },
  27117                 .One => switch (dest_info.child.toType().zigTypeTag(mod)) {
  27118                     .Union => {
  27119                         // pointer to anonymous struct to pointer to union
  27120                         if (inst_ty.isSinglePointer(mod) and
  27121                             inst_ty.childType(mod).isAnonStruct(mod) and
  27122                             sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))
  27123                         {
  27124                             return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
  27125                         }
  27126                     },
  27127                     .Struct => {
  27128                         // pointer to anonymous struct to pointer to struct
  27129                         if (inst_ty.isSinglePointer(mod) and
  27130                             inst_ty.childType(mod).isAnonStruct(mod) and
  27131                             sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))
  27132                         {
  27133                             return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src) catch |err| switch (err) {
  27134                                 error.NotCoercible => break :pointer,
  27135                                 else => |e| return e,
  27136                             };
  27137                         }
  27138                     },
  27139                     .Array => {
  27140                         // pointer to tuple to pointer to array
  27141                         if (inst_ty.isSinglePointer(mod) and
  27142                             inst_ty.childType(mod).isTuple(mod) and
  27143                             sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))
  27144                         {
  27145                             return sema.coerceTupleToArrayPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
  27146                         }
  27147                     },
  27148                     else => {},
  27149                 },
  27150                 .Slice => to_slice: {
  27151                     if (inst_ty.zigTypeTag(mod) == .Array) {
  27152                         return sema.fail(
  27153                             block,
  27154                             inst_src,
  27155                             "array literal requires address-of operator (&) to coerce to slice type '{}'",
  27156                             .{dest_ty.fmt(mod)},
  27157                         );
  27158                     }
  27159 
  27160                     if (!inst_ty.isSinglePointer(mod)) break :to_slice;
  27161                     const inst_child_ty = inst_ty.childType(mod);
  27162                     if (!inst_child_ty.isTuple(mod)) break :to_slice;
  27163 
  27164                     // empty tuple to zero-length slice
  27165                     // note that this allows coercing to a mutable slice.
  27166                     if (inst_child_ty.structFieldCount(mod) == 0) {
  27167                         // Optional slice is represented with a null pointer so
  27168                         // we use a dummy pointer value with the required alignment.
  27169                         return sema.addConstant((try mod.intern(.{ .ptr = .{
  27170                             .ty = dest_ty.toIntern(),
  27171                             .addr = .{ .int = (if (dest_info.flags.alignment != .none)
  27172                                 try mod.intValue(Type.usize, dest_info.flags.alignment.toByteUnitsOptional().?)
  27173                             else
  27174                                 try mod.getCoerced(try dest_info.child.toType().lazyAbiAlignment(mod), Type.usize)).toIntern() },
  27175                             .len = (try mod.intValue(Type.usize, 0)).toIntern(),
  27176                         } })).toValue());
  27177                     }
  27178 
  27179                     // pointer to tuple to slice
  27180                     if (!dest_info.flags.is_const) {
  27181                         const err_msg = err_msg: {
  27182                             const err_msg = try sema.errMsg(block, inst_src, "cannot cast pointer to tuple to '{}'", .{dest_ty.fmt(mod)});
  27183                             errdefer err_msg.deinit(sema.gpa);
  27184                             try sema.errNote(block, dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{});
  27185                             break :err_msg err_msg;
  27186                         };
  27187                         return sema.failWithOwnedErrorMsg(err_msg);
  27188                     }
  27189                     return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);
  27190                 },
  27191                 .Many => p: {
  27192                     if (!inst_ty.isSlice(mod)) break :p;
  27193                     if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
  27194                     const inst_info = inst_ty.ptrInfo(mod);
  27195 
  27196                     switch (try sema.coerceInMemoryAllowed(
  27197                         block,
  27198                         dest_info.child.toType(),
  27199                         inst_info.child.toType(),
  27200                         !dest_info.flags.is_const,
  27201                         target,
  27202                         dest_ty_src,
  27203                         inst_src,
  27204                     )) {
  27205                         .ok => {},
  27206                         else => break :p,
  27207                     }
  27208 
  27209                     if (dest_info.sentinel == .none or inst_info.sentinel == .none or
  27210                         dest_info.sentinel !=
  27211                         try mod.intern_pool.getCoerced(sema.gpa, inst_info.sentinel, dest_info.child))
  27212                         break :p;
  27213 
  27214                     const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
  27215                     return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
  27216                 },
  27217             }
  27218         },
  27219         .Int, .ComptimeInt => switch (inst_ty.zigTypeTag(mod)) {
  27220             .Float, .ComptimeFloat => float: {
  27221                 if (is_undef) {
  27222                     return sema.addConstUndef(dest_ty);
  27223                 }
  27224                 const val = (try sema.resolveMaybeUndefVal(inst)) orelse {
  27225                     if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
  27226                         if (!opts.report_err) return error.NotCoercible;
  27227                         return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime-known");
  27228                     }
  27229                     break :float;
  27230                 };
  27231 
  27232                 if (val.floatHasFraction(mod)) {
  27233                     return sema.fail(
  27234                         block,
  27235                         inst_src,
  27236                         "fractional component prevents float value '{}' from coercion to type '{}'",
  27237                         .{ val.fmtValue(inst_ty, mod), dest_ty.fmt(mod) },
  27238                     );
  27239                 }
  27240                 const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty);
  27241                 return try sema.addConstant(result_val);
  27242             },
  27243             .Int, .ComptimeInt => {
  27244                 if (is_undef) {
  27245                     return sema.addConstUndef(dest_ty);
  27246                 }
  27247                 if (try sema.resolveMaybeUndefVal(inst)) |val| {
  27248                     // comptime-known integer to other number
  27249                     if (!(try sema.intFitsInType(val, dest_ty, null))) {
  27250                         if (!opts.report_err) return error.NotCoercible;
  27251                         return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) });
  27252                     }
  27253                     return try sema.addConstant(try mod.getCoerced(val, dest_ty));
  27254                 }
  27255                 if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
  27256                     if (!opts.report_err) return error.NotCoercible;
  27257                     if (opts.no_cast_to_comptime_int) return inst;
  27258                     return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime-known");
  27259                 }
  27260 
  27261                 // integer widening
  27262                 const dst_info = dest_ty.intInfo(mod);
  27263                 const src_info = inst_ty.intInfo(mod);
  27264                 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
  27265                     // small enough unsigned ints can get casted to large enough signed ints
  27266                     (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
  27267                 {
  27268                     try sema.requireRuntimeBlock(block, inst_src, null);
  27269                     return block.addTyOp(.intcast, dest_ty, inst);
  27270                 }
  27271             },
  27272             .Undefined => {
  27273                 return sema.addConstUndef(dest_ty);
  27274             },
  27275             else => {},
  27276         },
  27277         .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) {
  27278             .ComptimeFloat => {
  27279                 const val = try sema.resolveConstValue(block, .unneeded, inst, "");
  27280                 const result_val = try val.floatCast(dest_ty, mod);
  27281                 return try sema.addConstant(result_val);
  27282             },
  27283             .Float => {
  27284                 if (is_undef) {
  27285                     return sema.addConstUndef(dest_ty);
  27286                 }
  27287                 if (try sema.resolveMaybeUndefVal(inst)) |val| {
  27288                     const result_val = try val.floatCast(dest_ty, mod);
  27289                     if (!val.eql(try result_val.floatCast(inst_ty, mod), inst_ty, mod)) {
  27290                         return sema.fail(
  27291                             block,
  27292                             inst_src,
  27293                             "type '{}' cannot represent float value '{}'",
  27294                             .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) },
  27295                         );
  27296                     }
  27297                     return try sema.addConstant(result_val);
  27298                 } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) {
  27299                     if (!opts.report_err) return error.NotCoercible;
  27300                     return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime-known");
  27301                 }
  27302 
  27303                 // float widening
  27304                 const src_bits = inst_ty.floatBits(target);
  27305                 const dst_bits = dest_ty.floatBits(target);
  27306                 if (dst_bits >= src_bits) {
  27307                     try sema.requireRuntimeBlock(block, inst_src, null);
  27308                     return block.addTyOp(.fpext, dest_ty, inst);
  27309                 }
  27310             },
  27311             .Int, .ComptimeInt => int: {
  27312                 if (is_undef) {
  27313                     return sema.addConstUndef(dest_ty);
  27314                 }
  27315                 const val = (try sema.resolveMaybeUndefVal(inst)) orelse {
  27316                     if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) {
  27317                         if (!opts.report_err) return error.NotCoercible;
  27318                         return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime-known");
  27319                     }
  27320                     break :int;
  27321                 };
  27322                 const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, mod, sema);
  27323                 // TODO implement this compile error
  27324                 //const int_again_val = try result_val.intFromFloat(sema.arena, inst_ty);
  27325                 //if (!int_again_val.eql(val, inst_ty, mod)) {
  27326                 //    return sema.fail(
  27327                 //        block,
  27328                 //        inst_src,
  27329                 //        "type '{}' cannot represent integer value '{}'",
  27330                 //        .{ dest_ty.fmt(mod), val },
  27331                 //    );
  27332                 //}
  27333                 return try sema.addConstant(result_val);
  27334             },
  27335             .Undefined => {
  27336                 return sema.addConstUndef(dest_ty);
  27337             },
  27338             else => {},
  27339         },
  27340         .Enum => switch (inst_ty.zigTypeTag(mod)) {
  27341             .EnumLiteral => {
  27342                 // enum literal to enum
  27343                 const val = try sema.resolveConstValue(block, .unneeded, inst, "");
  27344                 const string = mod.intern_pool.indexToKey(val.toIntern()).enum_literal;
  27345                 const field_index = dest_ty.enumFieldIndex(string, mod) orelse {
  27346                     const msg = msg: {
  27347                         const msg = try sema.errMsg(
  27348                             block,
  27349                             inst_src,
  27350                             "no field named '{}' in enum '{}'",
  27351                             .{ string.fmt(&mod.intern_pool), dest_ty.fmt(mod) },
  27352                         );
  27353                         errdefer msg.destroy(sema.gpa);
  27354                         try sema.addDeclaredHereNote(msg, dest_ty);
  27355                         break :msg msg;
  27356                     };
  27357                     return sema.failWithOwnedErrorMsg(msg);
  27358                 };
  27359                 return sema.addConstant(
  27360                     try mod.enumValueFieldIndex(dest_ty, @as(u32, @intCast(field_index))),
  27361                 );
  27362             },
  27363             .Union => blk: {
  27364                 // union to its own tag type
  27365                 const union_tag_ty = inst_ty.unionTagType(mod) orelse break :blk;
  27366                 if (union_tag_ty.eql(dest_ty, mod)) {
  27367                     return sema.unionToTag(block, dest_ty, inst, inst_src);
  27368                 }
  27369             },
  27370             .Undefined => {
  27371                 return sema.addConstUndef(dest_ty);
  27372             },
  27373             else => {},
  27374         },
  27375         .ErrorUnion => switch (inst_ty.zigTypeTag(mod)) {
  27376             .ErrorUnion => eu: {
  27377                 if (maybe_inst_val) |inst_val| {
  27378                     switch (inst_val.toIntern()) {
  27379                         .undef => return sema.addConstUndef(dest_ty),
  27380                         else => switch (mod.intern_pool.indexToKey(inst_val.toIntern())) {
  27381                             .error_union => |error_union| switch (error_union.val) {
  27382                                 .err_name => |err_name| {
  27383                                     const error_set_ty = inst_ty.errorUnionSet(mod);
  27384                                     const error_set_val = try sema.addConstant((try mod.intern(.{ .err = .{
  27385                                         .ty = error_set_ty.toIntern(),
  27386                                         .name = err_name,
  27387                                     } })).toValue());
  27388                                     return sema.wrapErrorUnionSet(block, dest_ty, error_set_val, inst_src);
  27389                                 },
  27390                                 .payload => |payload| {
  27391                                     const payload_val = try sema.addConstant(
  27392                                         payload.toValue(),
  27393                                     );
  27394                                     return sema.wrapErrorUnionPayload(block, dest_ty, payload_val, inst_src) catch |err| switch (err) {
  27395                                         error.NotCoercible => break :eu,
  27396                                         else => |e| return e,
  27397                                     };
  27398                                 },
  27399                             },
  27400                             else => unreachable,
  27401                         },
  27402                     }
  27403                 }
  27404             },
  27405             .ErrorSet => {
  27406                 // E to E!T
  27407                 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);
  27408             },
  27409             .Undefined => {
  27410                 return sema.addConstUndef(dest_ty);
  27411             },
  27412             else => eu: {
  27413                 // T to E!T
  27414                 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) {
  27415                     error.NotCoercible => break :eu,
  27416                     else => |e| return e,
  27417                 };
  27418             },
  27419         },
  27420         .Union => switch (inst_ty.zigTypeTag(mod)) {
  27421             .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
  27422             .Struct => {
  27423                 if (inst_ty.isAnonStruct(mod)) {
  27424                     return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src);
  27425                 }
  27426             },
  27427             .Undefined => {
  27428                 return sema.addConstUndef(dest_ty);
  27429             },
  27430             else => {},
  27431         },
  27432         .Array => switch (inst_ty.zigTypeTag(mod)) {
  27433             .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
  27434             .Struct => {
  27435                 if (inst == .empty_struct) {
  27436                     return sema.arrayInitEmpty(block, inst_src, dest_ty);
  27437                 }
  27438                 if (inst_ty.isTuple(mod)) {
  27439                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
  27440                 }
  27441             },
  27442             .Undefined => {
  27443                 return sema.addConstUndef(dest_ty);
  27444             },
  27445             else => {},
  27446         },
  27447         .Vector => switch (inst_ty.zigTypeTag(mod)) {
  27448             .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
  27449             .Struct => {
  27450                 if (inst_ty.isTuple(mod)) {
  27451                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
  27452                 }
  27453             },
  27454             .Undefined => {
  27455                 return sema.addConstUndef(dest_ty);
  27456             },
  27457             else => {},
  27458         },
  27459         .Struct => blk: {
  27460             if (inst == .empty_struct) {
  27461                 return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src);
  27462             }
  27463             if (inst_ty.isTupleOrAnonStruct(mod)) {
  27464                 return sema.coerceTupleToStruct(block, dest_ty, inst, inst_src) catch |err| switch (err) {
  27465                     error.NotCoercible => break :blk,
  27466                     else => |e| return e,
  27467                 };
  27468             }
  27469         },
  27470         else => {},
  27471     }
  27472 
  27473     // undefined to anything. We do this after the big switch above so that
  27474     // special logic has a chance to run first, such as `*[N]T` to `[]T` which
  27475     // should initialize the length field of the slice.
  27476     if (is_undef) {
  27477         return sema.addConstUndef(dest_ty);
  27478     }
  27479 
  27480     if (!opts.report_err) return error.NotCoercible;
  27481 
  27482     if (opts.is_ret and dest_ty.zigTypeTag(mod) == .NoReturn) {
  27483         const msg = msg: {
  27484             const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{});
  27485             errdefer msg.destroy(sema.gpa);
  27486 
  27487             const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
  27488             const src_decl = mod.declPtr(sema.func.?.owner_decl);
  27489             try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "'noreturn' declared here", .{});
  27490             break :msg msg;
  27491         };
  27492         return sema.failWithOwnedErrorMsg(msg);
  27493     }
  27494 
  27495     const msg = msg: {
  27496         const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), inst_ty.fmt(mod) });
  27497         errdefer msg.destroy(sema.gpa);
  27498 
  27499         // E!T to T
  27500         if (inst_ty.zigTypeTag(mod) == .ErrorUnion and
  27501             (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
  27502         {
  27503             try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{});
  27504             try sema.errNote(block, inst_src, msg, "consider using 'try', 'catch', or 'if'", .{});
  27505         }
  27506 
  27507         // ?T to T
  27508         if (inst_ty.zigTypeTag(mod) == .Optional and
  27509             (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
  27510         {
  27511             try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{});
  27512             try sema.errNote(block, inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{});
  27513         }
  27514 
  27515         try in_memory_result.report(sema, block, inst_src, msg);
  27516 
  27517         // Add notes about function return type
  27518         if (opts.is_ret and mod.test_functions.get(sema.func.?.owner_decl) == null) {
  27519             const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
  27520             const src_decl = mod.declPtr(sema.func.?.owner_decl);
  27521             if (inst_ty.isError(mod) and !dest_ty.isError(mod)) {
  27522                 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function cannot return an error", .{});
  27523             } else {
  27524                 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function return type declared here", .{});
  27525             }
  27526         }
  27527 
  27528         if (try opts.param_src.get(sema)) |param_src| {
  27529             try mod.errNoteNonLazy(param_src, msg, "parameter type declared here", .{});
  27530         }
  27531 
  27532         // TODO maybe add "cannot store an error in type '{}'" note
  27533 
  27534         break :msg msg;
  27535     };
  27536     return sema.failWithOwnedErrorMsg(msg);
  27537 }
  27538 
  27539 fn coerceInMemory(
  27540     sema: *Sema,
  27541     val: Value,
  27542     dst_ty: Type,
  27543 ) CompileError!Air.Inst.Ref {
  27544     return sema.addConstant(try sema.mod.getCoerced(val, dst_ty));
  27545 }
  27546 
  27547 const InMemoryCoercionResult = union(enum) {
  27548     ok,
  27549     no_match: Pair,
  27550     int_not_coercible: Int,
  27551     error_union_payload: PairAndChild,
  27552     array_len: IntPair,
  27553     array_sentinel: Sentinel,
  27554     array_elem: PairAndChild,
  27555     vector_len: IntPair,
  27556     vector_elem: PairAndChild,
  27557     optional_shape: Pair,
  27558     optional_child: PairAndChild,
  27559     from_anyerror,
  27560     missing_error: []const InternPool.NullTerminatedString,
  27561     /// true if wanted is var args
  27562     fn_var_args: bool,
  27563     /// true if wanted is generic
  27564     fn_generic: bool,
  27565     fn_param_count: IntPair,
  27566     fn_param_noalias: IntPair,
  27567     fn_param_comptime: ComptimeParam,
  27568     fn_param: Param,
  27569     fn_cc: CC,
  27570     fn_return_type: PairAndChild,
  27571     ptr_child: PairAndChild,
  27572     ptr_addrspace: AddressSpace,
  27573     ptr_sentinel: Sentinel,
  27574     ptr_size: Size,
  27575     ptr_qualifiers: Qualifiers,
  27576     ptr_allowzero: Pair,
  27577     ptr_bit_range: BitRange,
  27578     ptr_alignment: IntPair,
  27579     double_ptr_to_anyopaque: Pair,
  27580     slice_to_anyopaque: Pair,
  27581 
  27582     const Pair = struct {
  27583         actual: Type,
  27584         wanted: Type,
  27585     };
  27586 
  27587     const PairAndChild = struct {
  27588         child: *InMemoryCoercionResult,
  27589         actual: Type,
  27590         wanted: Type,
  27591     };
  27592 
  27593     const Param = struct {
  27594         child: *InMemoryCoercionResult,
  27595         actual: Type,
  27596         wanted: Type,
  27597         index: u64,
  27598     };
  27599 
  27600     const ComptimeParam = struct {
  27601         index: u64,
  27602         wanted: bool,
  27603     };
  27604 
  27605     const Sentinel = struct {
  27606         // unreachable_value indicates no sentinel
  27607         actual: Value,
  27608         wanted: Value,
  27609         ty: Type,
  27610     };
  27611 
  27612     const Int = struct {
  27613         actual_signedness: std.builtin.Signedness,
  27614         wanted_signedness: std.builtin.Signedness,
  27615         actual_bits: u16,
  27616         wanted_bits: u16,
  27617     };
  27618 
  27619     const IntPair = struct {
  27620         actual: u64,
  27621         wanted: u64,
  27622     };
  27623 
  27624     const Size = struct {
  27625         actual: std.builtin.Type.Pointer.Size,
  27626         wanted: std.builtin.Type.Pointer.Size,
  27627     };
  27628 
  27629     const Qualifiers = struct {
  27630         actual_const: bool,
  27631         wanted_const: bool,
  27632         actual_volatile: bool,
  27633         wanted_volatile: bool,
  27634     };
  27635 
  27636     const AddressSpace = struct {
  27637         actual: std.builtin.AddressSpace,
  27638         wanted: std.builtin.AddressSpace,
  27639     };
  27640 
  27641     const CC = struct {
  27642         actual: std.builtin.CallingConvention,
  27643         wanted: std.builtin.CallingConvention,
  27644     };
  27645 
  27646     const BitRange = struct {
  27647         actual_host: u16,
  27648         wanted_host: u16,
  27649         actual_offset: u16,
  27650         wanted_offset: u16,
  27651     };
  27652 
  27653     fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult {
  27654         const res = try arena.create(InMemoryCoercionResult);
  27655         res.* = child.*;
  27656         return res;
  27657     }
  27658 
  27659     fn report(res: *const InMemoryCoercionResult, sema: *Sema, block: *Block, src: LazySrcLoc, msg: *Module.ErrorMsg) !void {
  27660         const mod = sema.mod;
  27661         var cur = res;
  27662         while (true) switch (cur.*) {
  27663             .ok => unreachable,
  27664             .no_match => |types| {
  27665                 try sema.addDeclaredHereNote(msg, types.wanted);
  27666                 try sema.addDeclaredHereNote(msg, types.actual);
  27667                 break;
  27668             },
  27669             .int_not_coercible => |int| {
  27670                 try sema.errNote(block, src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{
  27671                     @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits,
  27672                 });
  27673                 break;
  27674             },
  27675             .error_union_payload => |pair| {
  27676                 try sema.errNote(block, src, msg, "error union payload '{}' cannot cast into error union payload '{}'", .{
  27677                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27678                 });
  27679                 cur = pair.child;
  27680             },
  27681             .array_len => |lens| {
  27682                 try sema.errNote(block, src, msg, "array of length {d} cannot cast into an array of length {d}", .{
  27683                     lens.actual, lens.wanted,
  27684                 });
  27685                 break;
  27686             },
  27687             .array_sentinel => |sentinel| {
  27688                 if (sentinel.actual.toIntern() != .unreachable_value) {
  27689                     try sema.errNote(block, src, msg, "array sentinel '{}' cannot cast into array sentinel '{}'", .{
  27690                         sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod),
  27691                     });
  27692                 } else {
  27693                     try sema.errNote(block, src, msg, "destination array requires '{}' sentinel", .{
  27694                         sentinel.wanted.fmtValue(sentinel.ty, mod),
  27695                     });
  27696                 }
  27697                 break;
  27698             },
  27699             .array_elem => |pair| {
  27700                 try sema.errNote(block, src, msg, "array element type '{}' cannot cast into array element type '{}'", .{
  27701                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27702                 });
  27703                 cur = pair.child;
  27704             },
  27705             .vector_len => |lens| {
  27706                 try sema.errNote(block, src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{
  27707                     lens.actual, lens.wanted,
  27708                 });
  27709                 break;
  27710             },
  27711             .vector_elem => |pair| {
  27712                 try sema.errNote(block, src, msg, "vector element type '{}' cannot cast into vector element type '{}'", .{
  27713                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27714                 });
  27715                 cur = pair.child;
  27716             },
  27717             .optional_shape => |pair| {
  27718                 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{
  27719                     pair.actual.optionalChild(mod).fmt(mod), pair.wanted.optionalChild(mod).fmt(mod),
  27720                 });
  27721                 break;
  27722             },
  27723             .optional_child => |pair| {
  27724                 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{
  27725                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27726                 });
  27727                 cur = pair.child;
  27728             },
  27729             .from_anyerror => {
  27730                 try sema.errNote(block, src, msg, "global error set cannot cast into a smaller set", .{});
  27731                 break;
  27732             },
  27733             .missing_error => |missing_errors| {
  27734                 for (missing_errors) |err| {
  27735                     try sema.errNote(block, src, msg, "'error.{}' not a member of destination error set", .{err.fmt(&mod.intern_pool)});
  27736                 }
  27737                 break;
  27738             },
  27739             .fn_var_args => |wanted_var_args| {
  27740                 if (wanted_var_args) {
  27741                     try sema.errNote(block, src, msg, "non-variadic function cannot cast into a variadic function", .{});
  27742                 } else {
  27743                     try sema.errNote(block, src, msg, "variadic function cannot cast into a non-variadic function", .{});
  27744                 }
  27745                 break;
  27746             },
  27747             .fn_generic => |wanted_generic| {
  27748                 if (wanted_generic) {
  27749                     try sema.errNote(block, src, msg, "non-generic function cannot cast into a generic function", .{});
  27750                 } else {
  27751                     try sema.errNote(block, src, msg, "generic function cannot cast into a non-generic function", .{});
  27752                 }
  27753                 break;
  27754             },
  27755             .fn_param_count => |lens| {
  27756                 try sema.errNote(block, src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{
  27757                     lens.actual, lens.wanted,
  27758                 });
  27759                 break;
  27760             },
  27761             .fn_param_noalias => |param| {
  27762                 var index: u6 = 0;
  27763                 var actual_noalias = false;
  27764                 while (true) : (index += 1) {
  27765                     const actual = @as(u1, @truncate(param.actual >> index));
  27766                     const wanted = @as(u1, @truncate(param.wanted >> index));
  27767                     if (actual != wanted) {
  27768                         actual_noalias = actual == 1;
  27769                         break;
  27770                     }
  27771                 }
  27772                 if (!actual_noalias) {
  27773                     try sema.errNote(block, src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index});
  27774                 } else {
  27775                     try sema.errNote(block, src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index});
  27776                 }
  27777                 break;
  27778             },
  27779             .fn_param_comptime => |param| {
  27780                 if (param.wanted) {
  27781                     try sema.errNote(block, src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index});
  27782                 } else {
  27783                     try sema.errNote(block, src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index});
  27784                 }
  27785                 break;
  27786             },
  27787             .fn_param => |param| {
  27788                 try sema.errNote(block, src, msg, "parameter {d} '{}' cannot cast into '{}'", .{
  27789                     param.index, param.actual.fmt(mod), param.wanted.fmt(mod),
  27790                 });
  27791                 cur = param.child;
  27792             },
  27793             .fn_cc => |cc| {
  27794                 try sema.errNote(block, src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) });
  27795                 break;
  27796             },
  27797             .fn_return_type => |pair| {
  27798                 try sema.errNote(block, src, msg, "return type '{}' cannot cast into return type '{}'", .{
  27799                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27800                 });
  27801                 cur = pair.child;
  27802             },
  27803             .ptr_child => |pair| {
  27804                 try sema.errNote(block, src, msg, "pointer type child '{}' cannot cast into pointer type child '{}'", .{
  27805                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27806                 });
  27807                 cur = pair.child;
  27808             },
  27809             .ptr_addrspace => |@"addrspace"| {
  27810                 try sema.errNote(block, src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) });
  27811                 break;
  27812             },
  27813             .ptr_sentinel => |sentinel| {
  27814                 if (sentinel.actual.toIntern() != .unreachable_value) {
  27815                     try sema.errNote(block, src, msg, "pointer sentinel '{}' cannot cast into pointer sentinel '{}'", .{
  27816                         sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod),
  27817                     });
  27818                 } else {
  27819                     try sema.errNote(block, src, msg, "destination pointer requires '{}' sentinel", .{
  27820                         sentinel.wanted.fmtValue(sentinel.ty, mod),
  27821                     });
  27822                 }
  27823                 break;
  27824             },
  27825             .ptr_size => |size| {
  27826                 try sema.errNote(block, src, msg, "a {s} pointer cannot cast into a {s} pointer", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) });
  27827                 break;
  27828             },
  27829             .ptr_qualifiers => |qualifiers| {
  27830                 const ok_const = !qualifiers.actual_const or qualifiers.wanted_const;
  27831                 const ok_volatile = !qualifiers.actual_volatile or qualifiers.wanted_volatile;
  27832                 if (!ok_const) {
  27833                     try sema.errNote(block, src, msg, "cast discards const qualifier", .{});
  27834                 } else if (!ok_volatile) {
  27835                     try sema.errNote(block, src, msg, "cast discards volatile qualifier", .{});
  27836                 }
  27837                 break;
  27838             },
  27839             .ptr_allowzero => |pair| {
  27840                 const wanted_allow_zero = pair.wanted.ptrAllowsZero(mod);
  27841                 const actual_allow_zero = pair.actual.ptrAllowsZero(mod);
  27842                 if (actual_allow_zero and !wanted_allow_zero) {
  27843                     try sema.errNote(block, src, msg, "'{}' could have null values which are illegal in type '{}'", .{
  27844                         pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27845                     });
  27846                 } else {
  27847                     try sema.errNote(block, src, msg, "mutable '{}' allows illegal null values stored to type '{}'", .{
  27848                         pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27849                     });
  27850                 }
  27851                 break;
  27852             },
  27853             .ptr_bit_range => |bit_range| {
  27854                 if (bit_range.actual_host != bit_range.wanted_host) {
  27855                     try sema.errNote(block, src, msg, "pointer host size '{}' cannot cast into pointer host size '{}'", .{
  27856                         bit_range.actual_host, bit_range.wanted_host,
  27857                     });
  27858                 }
  27859                 if (bit_range.actual_offset != bit_range.wanted_offset) {
  27860                     try sema.errNote(block, src, msg, "pointer bit offset '{}' cannot cast into pointer bit offset '{}'", .{
  27861                         bit_range.actual_offset, bit_range.wanted_offset,
  27862                     });
  27863                 }
  27864                 break;
  27865             },
  27866             .ptr_alignment => |pair| {
  27867                 try sema.errNote(block, src, msg, "pointer alignment '{}' cannot cast into pointer alignment '{}'", .{
  27868                     pair.actual, pair.wanted,
  27869                 });
  27870                 break;
  27871             },
  27872             .double_ptr_to_anyopaque => |pair| {
  27873                 try sema.errNote(block, src, msg, "cannot implicitly cast double pointer '{}' to anyopaque pointer '{}'", .{
  27874                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27875                 });
  27876                 break;
  27877             },
  27878             .slice_to_anyopaque => |pair| {
  27879                 try sema.errNote(block, src, msg, "cannot implicitly cast slice '{}' to anyopaque pointer '{}'", .{
  27880                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27881                 });
  27882                 try sema.errNote(block, src, msg, "consider using '.ptr'", .{});
  27883                 break;
  27884             },
  27885         };
  27886     }
  27887 };
  27888 
  27889 fn pointerSizeString(size: std.builtin.Type.Pointer.Size) []const u8 {
  27890     return switch (size) {
  27891         .One => "single",
  27892         .Many => "many",
  27893         .C => "C",
  27894         .Slice => unreachable,
  27895     };
  27896 }
  27897 
  27898 /// If pointers have the same representation in runtime memory, a bitcast AIR instruction
  27899 /// may be used for the coercion.
  27900 /// * `const` attribute can be gained
  27901 /// * `volatile` attribute can be gained
  27902 /// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if !dest_is_mut
  27903 /// * alignment can be decreased
  27904 /// * bit offset attributes must match exactly
  27905 /// * `*`/`[*]` must match exactly, but `[*c]` matches either one
  27906 /// * sentinel-terminated pointers can coerce into `[*]`
  27907 fn coerceInMemoryAllowed(
  27908     sema: *Sema,
  27909     block: *Block,
  27910     dest_ty: Type,
  27911     src_ty: Type,
  27912     dest_is_mut: bool,
  27913     target: std.Target,
  27914     dest_src: LazySrcLoc,
  27915     src_src: LazySrcLoc,
  27916 ) CompileError!InMemoryCoercionResult {
  27917     const mod = sema.mod;
  27918 
  27919     if (dest_ty.eql(src_ty, mod))
  27920         return .ok;
  27921 
  27922     const dest_tag = dest_ty.zigTypeTag(mod);
  27923     const src_tag = src_ty.zigTypeTag(mod);
  27924 
  27925     // Differently-named integers with the same number of bits.
  27926     if (dest_tag == .Int and src_tag == .Int) {
  27927         const dest_info = dest_ty.intInfo(mod);
  27928         const src_info = src_ty.intInfo(mod);
  27929 
  27930         if (dest_info.signedness == src_info.signedness and
  27931             dest_info.bits == src_info.bits)
  27932         {
  27933             return .ok;
  27934         }
  27935 
  27936         if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or
  27937             // small enough unsigned ints can get casted to large enough signed ints
  27938             (dest_info.signedness == .signed and (src_info.signedness == .unsigned or dest_info.bits <= src_info.bits)) or
  27939             (dest_info.signedness == .unsigned and src_info.signedness == .signed))
  27940         {
  27941             return InMemoryCoercionResult{ .int_not_coercible = .{
  27942                 .actual_signedness = src_info.signedness,
  27943                 .wanted_signedness = dest_info.signedness,
  27944                 .actual_bits = src_info.bits,
  27945                 .wanted_bits = dest_info.bits,
  27946             } };
  27947         }
  27948     }
  27949 
  27950     // Differently-named floats with the same number of bits.
  27951     if (dest_tag == .Float and src_tag == .Float) {
  27952         const dest_bits = dest_ty.floatBits(target);
  27953         const src_bits = src_ty.floatBits(target);
  27954         if (dest_bits == src_bits) {
  27955             return .ok;
  27956         }
  27957     }
  27958 
  27959     // Pointers / Pointer-like Optionals
  27960     const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty);
  27961     const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty);
  27962     if (maybe_dest_ptr_ty) |dest_ptr_ty| {
  27963         if (maybe_src_ptr_ty) |src_ptr_ty| {
  27964             return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src);
  27965         }
  27966     }
  27967 
  27968     // Slices
  27969     if (dest_ty.isSlice(mod) and src_ty.isSlice(mod)) {
  27970         return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
  27971     }
  27972 
  27973     // Functions
  27974     if (dest_tag == .Fn and src_tag == .Fn) {
  27975         return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, target, dest_src, src_src);
  27976     }
  27977 
  27978     // Error Unions
  27979     if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) {
  27980         const dest_payload = dest_ty.errorUnionPayload(mod);
  27981         const src_payload = src_ty.errorUnionPayload(mod);
  27982         const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src);
  27983         if (child != .ok) {
  27984             return InMemoryCoercionResult{ .error_union_payload = .{
  27985                 .child = try child.dupe(sema.arena),
  27986                 .actual = src_payload,
  27987                 .wanted = dest_payload,
  27988             } };
  27989         }
  27990         return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(mod), src_ty.errorUnionSet(mod), dest_is_mut, target, dest_src, src_src);
  27991     }
  27992 
  27993     // Error Sets
  27994     if (dest_tag == .ErrorSet and src_tag == .ErrorSet) {
  27995         return try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src);
  27996     }
  27997 
  27998     // Arrays
  27999     if (dest_tag == .Array and src_tag == .Array) {
  28000         const dest_info = dest_ty.arrayInfo(mod);
  28001         const src_info = src_ty.arrayInfo(mod);
  28002         if (dest_info.len != src_info.len) {
  28003             return InMemoryCoercionResult{ .array_len = .{
  28004                 .actual = src_info.len,
  28005                 .wanted = dest_info.len,
  28006             } };
  28007         }
  28008 
  28009         const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src);
  28010         if (child != .ok) {
  28011             return InMemoryCoercionResult{ .array_elem = .{
  28012                 .child = try child.dupe(sema.arena),
  28013                 .actual = src_info.elem_type,
  28014                 .wanted = dest_info.elem_type,
  28015             } };
  28016         }
  28017         const ok_sent = dest_info.sentinel == null or
  28018             (src_info.sentinel != null and
  28019             dest_info.sentinel.?.eql(
  28020             try mod.getCoerced(src_info.sentinel.?, dest_info.elem_type),
  28021             dest_info.elem_type,
  28022             mod,
  28023         ));
  28024         if (!ok_sent) {
  28025             return InMemoryCoercionResult{ .array_sentinel = .{
  28026                 .actual = src_info.sentinel orelse Value.@"unreachable",
  28027                 .wanted = dest_info.sentinel orelse Value.@"unreachable",
  28028                 .ty = dest_info.elem_type,
  28029             } };
  28030         }
  28031         return .ok;
  28032     }
  28033 
  28034     // Vectors
  28035     if (dest_tag == .Vector and src_tag == .Vector) {
  28036         const dest_len = dest_ty.vectorLen(mod);
  28037         const src_len = src_ty.vectorLen(mod);
  28038         if (dest_len != src_len) {
  28039             return InMemoryCoercionResult{ .vector_len = .{
  28040                 .actual = src_len,
  28041                 .wanted = dest_len,
  28042             } };
  28043         }
  28044 
  28045         const dest_elem_ty = dest_ty.scalarType(mod);
  28046         const src_elem_ty = src_ty.scalarType(mod);
  28047         const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src);
  28048         if (child != .ok) {
  28049             return InMemoryCoercionResult{ .vector_elem = .{
  28050                 .child = try child.dupe(sema.arena),
  28051                 .actual = src_elem_ty,
  28052                 .wanted = dest_elem_ty,
  28053             } };
  28054         }
  28055 
  28056         return .ok;
  28057     }
  28058 
  28059     // Optionals
  28060     if (dest_tag == .Optional and src_tag == .Optional) {
  28061         if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
  28062             return InMemoryCoercionResult{ .optional_shape = .{
  28063                 .actual = src_ty,
  28064                 .wanted = dest_ty,
  28065             } };
  28066         }
  28067         const dest_child_type = dest_ty.optionalChild(mod);
  28068         const src_child_type = src_ty.optionalChild(mod);
  28069 
  28070         const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src);
  28071         if (child != .ok) {
  28072             return InMemoryCoercionResult{ .optional_child = .{
  28073                 .child = try child.dupe(sema.arena),
  28074                 .actual = src_child_type,
  28075                 .wanted = dest_child_type,
  28076             } };
  28077         }
  28078 
  28079         return .ok;
  28080     }
  28081 
  28082     // Tuples (with in-memory-coercible fields)
  28083     if (dest_ty.isTuple(mod) and src_ty.isTuple(mod)) tuple: {
  28084         if (dest_ty.containerLayout(mod) != src_ty.containerLayout(mod)) break :tuple;
  28085         if (dest_ty.structFieldCount(mod) != src_ty.structFieldCount(mod)) break :tuple;
  28086         const field_count = dest_ty.structFieldCount(mod);
  28087         for (0..field_count) |field_idx| {
  28088             if (dest_ty.structFieldIsComptime(field_idx, mod) != src_ty.structFieldIsComptime(field_idx, mod)) break :tuple;
  28089             if (dest_ty.structFieldAlign(field_idx, mod) != src_ty.structFieldAlign(field_idx, mod)) break :tuple;
  28090             const dest_field_ty = dest_ty.structFieldType(field_idx, mod);
  28091             const src_field_ty = src_ty.structFieldType(field_idx, mod);
  28092             const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src);
  28093             if (field != .ok) break :tuple;
  28094         }
  28095         return .ok;
  28096     }
  28097 
  28098     return InMemoryCoercionResult{ .no_match = .{
  28099         .actual = dest_ty,
  28100         .wanted = src_ty,
  28101     } };
  28102 }
  28103 
  28104 fn coerceInMemoryAllowedErrorSets(
  28105     sema: *Sema,
  28106     block: *Block,
  28107     dest_ty: Type,
  28108     src_ty: Type,
  28109     dest_src: LazySrcLoc,
  28110     src_src: LazySrcLoc,
  28111 ) !InMemoryCoercionResult {
  28112     const mod = sema.mod;
  28113     const gpa = sema.gpa;
  28114     const ip = &mod.intern_pool;
  28115 
  28116     // Coercion to `anyerror`. Note that this check can return false negatives
  28117     // in case the error sets did not get resolved.
  28118     if (dest_ty.isAnyError(mod)) {
  28119         return .ok;
  28120     }
  28121 
  28122     if (mod.typeToInferredErrorSetIndex(dest_ty).unwrap()) |dst_ies_index| {
  28123         const dst_ies = mod.inferredErrorSetPtr(dst_ies_index);
  28124         // We will make an effort to return `ok` without resolving either error set, to
  28125         // avoid unnecessary "unable to resolve error set" dependency loop errors.
  28126         switch (src_ty.toIntern()) {
  28127             .anyerror_type => {},
  28128             else => switch (ip.indexToKey(src_ty.toIntern())) {
  28129                 .inferred_error_set_type => |src_index| {
  28130                     // If both are inferred error sets of functions, and
  28131                     // the dest includes the source function, the coercion is OK.
  28132                     // This check is important because it works without forcing a full resolution
  28133                     // of inferred error sets.
  28134                     if (dst_ies.inferred_error_sets.contains(src_index)) {
  28135                         return .ok;
  28136                     }
  28137                 },
  28138                 .error_set_type => |error_set_type| {
  28139                     for (error_set_type.names) |name| {
  28140                         if (!dst_ies.errors.contains(name)) break;
  28141                     } else return .ok;
  28142                 },
  28143                 else => unreachable,
  28144             },
  28145         }
  28146 
  28147         if (dst_ies.func == sema.owner_func_index.unwrap()) {
  28148             // We are trying to coerce an error set to the current function's
  28149             // inferred error set.
  28150             try dst_ies.addErrorSet(src_ty, ip, gpa);
  28151             return .ok;
  28152         }
  28153 
  28154         try sema.resolveInferredErrorSet(block, dest_src, dst_ies_index);
  28155         // isAnyError might have changed from a false negative to a true positive after resolution.
  28156         if (dest_ty.isAnyError(mod)) {
  28157             return .ok;
  28158         }
  28159     }
  28160 
  28161     var missing_error_buf = std.ArrayList(InternPool.NullTerminatedString).init(gpa);
  28162     defer missing_error_buf.deinit();
  28163 
  28164     switch (src_ty.toIntern()) {
  28165         .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) {
  28166             .simple_type => unreachable, // filtered out above
  28167             .error_set_type, .inferred_error_set_type => return .from_anyerror,
  28168             else => unreachable,
  28169         },
  28170 
  28171         else => switch (ip.indexToKey(src_ty.toIntern())) {
  28172             .inferred_error_set_type => |src_index| {
  28173                 const src_data = mod.inferredErrorSetPtr(src_index);
  28174 
  28175                 try sema.resolveInferredErrorSet(block, src_src, src_index);
  28176                 // src anyerror status might have changed after the resolution.
  28177                 if (src_ty.isAnyError(mod)) {
  28178                     // dest_ty.isAnyError(mod) == true is already checked for at this point.
  28179                     return .from_anyerror;
  28180                 }
  28181 
  28182                 for (src_data.errors.keys()) |key| {
  28183                     if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) {
  28184                         try missing_error_buf.append(key);
  28185                     }
  28186                 }
  28187 
  28188                 if (missing_error_buf.items.len != 0) {
  28189                     return InMemoryCoercionResult{
  28190                         .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
  28191                     };
  28192                 }
  28193 
  28194                 return .ok;
  28195             },
  28196             .error_set_type => |error_set_type| {
  28197                 for (error_set_type.names) |name| {
  28198                     if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) {
  28199                         try missing_error_buf.append(name);
  28200                     }
  28201                 }
  28202 
  28203                 if (missing_error_buf.items.len != 0) {
  28204                     return InMemoryCoercionResult{
  28205                         .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
  28206                     };
  28207                 }
  28208 
  28209                 return .ok;
  28210             },
  28211             else => unreachable,
  28212         },
  28213     }
  28214 }
  28215 
  28216 fn coerceInMemoryAllowedFns(
  28217     sema: *Sema,
  28218     block: *Block,
  28219     dest_ty: Type,
  28220     src_ty: Type,
  28221     target: std.Target,
  28222     dest_src: LazySrcLoc,
  28223     src_src: LazySrcLoc,
  28224 ) !InMemoryCoercionResult {
  28225     const mod = sema.mod;
  28226 
  28227     {
  28228         const dest_info = mod.typeToFunc(dest_ty).?;
  28229         const src_info = mod.typeToFunc(src_ty).?;
  28230 
  28231         if (dest_info.is_var_args != src_info.is_var_args) {
  28232             return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args };
  28233         }
  28234 
  28235         if (dest_info.is_generic != src_info.is_generic) {
  28236             return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic };
  28237         }
  28238 
  28239         if (dest_info.cc != src_info.cc) {
  28240             return InMemoryCoercionResult{ .fn_cc = .{
  28241                 .actual = src_info.cc,
  28242                 .wanted = dest_info.cc,
  28243             } };
  28244         }
  28245 
  28246         switch (src_info.return_type) {
  28247             .noreturn_type, .generic_poison_type => {},
  28248             else => {
  28249                 const dest_return_type = dest_info.return_type.toType();
  28250                 const src_return_type = src_info.return_type.toType();
  28251                 const rt = try sema.coerceInMemoryAllowed(block, dest_return_type, src_return_type, false, target, dest_src, src_src);
  28252                 if (rt != .ok) {
  28253                     return InMemoryCoercionResult{ .fn_return_type = .{
  28254                         .child = try rt.dupe(sema.arena),
  28255                         .actual = src_return_type,
  28256                         .wanted = dest_return_type,
  28257                     } };
  28258                 }
  28259             },
  28260         }
  28261     }
  28262 
  28263     const params_len = params_len: {
  28264         const dest_info = mod.typeToFunc(dest_ty).?;
  28265         const src_info = mod.typeToFunc(src_ty).?;
  28266 
  28267         if (dest_info.param_types.len != src_info.param_types.len) {
  28268             return InMemoryCoercionResult{ .fn_param_count = .{
  28269                 .actual = src_info.param_types.len,
  28270                 .wanted = dest_info.param_types.len,
  28271             } };
  28272         }
  28273 
  28274         if (dest_info.noalias_bits != src_info.noalias_bits) {
  28275             return InMemoryCoercionResult{ .fn_param_noalias = .{
  28276                 .actual = src_info.noalias_bits,
  28277                 .wanted = dest_info.noalias_bits,
  28278             } };
  28279         }
  28280 
  28281         break :params_len dest_info.param_types.len;
  28282     };
  28283 
  28284     for (0..params_len) |param_i| {
  28285         const dest_info = mod.typeToFunc(dest_ty).?;
  28286         const src_info = mod.typeToFunc(src_ty).?;
  28287 
  28288         const dest_param_ty = dest_info.param_types[param_i].toType();
  28289         const src_param_ty = src_info.param_types[param_i].toType();
  28290 
  28291         const param_i_small = @as(u5, @intCast(param_i));
  28292         if (dest_info.paramIsComptime(param_i_small) != src_info.paramIsComptime(param_i_small)) {
  28293             return InMemoryCoercionResult{ .fn_param_comptime = .{
  28294                 .index = param_i,
  28295                 .wanted = dest_info.paramIsComptime(param_i_small),
  28296             } };
  28297         }
  28298 
  28299         switch (src_param_ty.toIntern()) {
  28300             .generic_poison_type => {},
  28301             else => {
  28302                 // Note: Cast direction is reversed here.
  28303                 const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src);
  28304                 if (param != .ok) {
  28305                     return InMemoryCoercionResult{ .fn_param = .{
  28306                         .child = try param.dupe(sema.arena),
  28307                         .actual = src_param_ty,
  28308                         .wanted = dest_param_ty,
  28309                         .index = param_i,
  28310                     } };
  28311                 }
  28312             },
  28313         }
  28314     }
  28315 
  28316     return .ok;
  28317 }
  28318 
  28319 fn coerceInMemoryAllowedPtrs(
  28320     sema: *Sema,
  28321     block: *Block,
  28322     dest_ty: Type,
  28323     src_ty: Type,
  28324     dest_ptr_ty: Type,
  28325     src_ptr_ty: Type,
  28326     dest_is_mut: bool,
  28327     target: std.Target,
  28328     dest_src: LazySrcLoc,
  28329     src_src: LazySrcLoc,
  28330 ) !InMemoryCoercionResult {
  28331     const mod = sema.mod;
  28332     const dest_info = dest_ptr_ty.ptrInfo(mod);
  28333     const src_info = src_ptr_ty.ptrInfo(mod);
  28334 
  28335     const ok_ptr_size = src_info.flags.size == dest_info.flags.size or
  28336         src_info.flags.size == .C or dest_info.flags.size == .C;
  28337     if (!ok_ptr_size) {
  28338         return InMemoryCoercionResult{ .ptr_size = .{
  28339             .actual = src_info.flags.size,
  28340             .wanted = dest_info.flags.size,
  28341         } };
  28342     }
  28343 
  28344     const ok_cv_qualifiers =
  28345         (!src_info.flags.is_const or dest_info.flags.is_const) and
  28346         (!src_info.flags.is_volatile or dest_info.flags.is_volatile);
  28347 
  28348     if (!ok_cv_qualifiers) {
  28349         return InMemoryCoercionResult{ .ptr_qualifiers = .{
  28350             .actual_const = src_info.flags.is_const,
  28351             .wanted_const = dest_info.flags.is_const,
  28352             .actual_volatile = src_info.flags.is_volatile,
  28353             .wanted_volatile = dest_info.flags.is_volatile,
  28354         } };
  28355     }
  28356 
  28357     if (dest_info.flags.address_space != src_info.flags.address_space) {
  28358         return InMemoryCoercionResult{ .ptr_addrspace = .{
  28359             .actual = src_info.flags.address_space,
  28360             .wanted = dest_info.flags.address_space,
  28361         } };
  28362     }
  28363 
  28364     const child = try sema.coerceInMemoryAllowed(block, dest_info.child.toType(), src_info.child.toType(), !dest_info.flags.is_const, target, dest_src, src_src);
  28365     if (child != .ok) {
  28366         return InMemoryCoercionResult{ .ptr_child = .{
  28367             .child = try child.dupe(sema.arena),
  28368             .actual = src_info.child.toType(),
  28369             .wanted = dest_info.child.toType(),
  28370         } };
  28371     }
  28372 
  28373     const dest_allow_zero = dest_ty.ptrAllowsZero(mod);
  28374     const src_allow_zero = src_ty.ptrAllowsZero(mod);
  28375 
  28376     const ok_allows_zero = (dest_allow_zero and
  28377         (src_allow_zero or !dest_is_mut)) or
  28378         (!dest_allow_zero and !src_allow_zero);
  28379     if (!ok_allows_zero) {
  28380         return InMemoryCoercionResult{ .ptr_allowzero = .{
  28381             .actual = src_ty,
  28382             .wanted = dest_ty,
  28383         } };
  28384     }
  28385 
  28386     if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or
  28387         src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset)
  28388     {
  28389         return InMemoryCoercionResult{ .ptr_bit_range = .{
  28390             .actual_host = src_info.packed_offset.host_size,
  28391             .wanted_host = dest_info.packed_offset.host_size,
  28392             .actual_offset = src_info.packed_offset.bit_offset,
  28393             .wanted_offset = dest_info.packed_offset.bit_offset,
  28394         } };
  28395     }
  28396 
  28397     const ok_sent = dest_info.sentinel == .none or src_info.flags.size == .C or
  28398         (src_info.sentinel != .none and
  28399         dest_info.sentinel == try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child));
  28400     if (!ok_sent) {
  28401         return InMemoryCoercionResult{ .ptr_sentinel = .{
  28402             .actual = switch (src_info.sentinel) {
  28403                 .none => Value.@"unreachable",
  28404                 else => src_info.sentinel.toValue(),
  28405             },
  28406             .wanted = switch (dest_info.sentinel) {
  28407                 .none => Value.@"unreachable",
  28408                 else => dest_info.sentinel.toValue(),
  28409             },
  28410             .ty = dest_info.child.toType(),
  28411         } };
  28412     }
  28413 
  28414     // If both pointers have alignment 0, it means they both want ABI alignment.
  28415     // In this case, if they share the same child type, no need to resolve
  28416     // pointee type alignment. Otherwise both pointee types must have their alignment
  28417     // resolved and we compare the alignment numerically.
  28418     if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or
  28419         dest_info.child != src_info.child)
  28420     {
  28421         const src_align = src_info.flags.alignment.toByteUnitsOptional() orelse
  28422             src_info.child.toType().abiAlignment(mod);
  28423 
  28424         const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse
  28425             dest_info.child.toType().abiAlignment(mod);
  28426 
  28427         if (dest_align > src_align) {
  28428             return InMemoryCoercionResult{ .ptr_alignment = .{
  28429                 .actual = src_align,
  28430                 .wanted = dest_align,
  28431             } };
  28432         }
  28433     }
  28434 
  28435     return .ok;
  28436 }
  28437 
  28438 fn coerceVarArgParam(
  28439     sema: *Sema,
  28440     block: *Block,
  28441     inst: Air.Inst.Ref,
  28442     inst_src: LazySrcLoc,
  28443 ) !Air.Inst.Ref {
  28444     if (block.is_typeof) return inst;
  28445 
  28446     const mod = sema.mod;
  28447     const uncasted_ty = sema.typeOf(inst);
  28448     const coerced = switch (uncasted_ty.zigTypeTag(mod)) {
  28449         // TODO consider casting to c_int/f64 if they fit
  28450         .ComptimeInt, .ComptimeFloat => return sema.fail(
  28451             block,
  28452             inst_src,
  28453             "integer and float literals passed to variadic function must be casted to a fixed-size number type",
  28454             .{},
  28455         ),
  28456         .Fn => blk: {
  28457             const fn_val = try sema.resolveConstValue(block, .unneeded, inst, "");
  28458             const fn_decl = fn_val.pointerDecl(mod).?;
  28459             break :blk try sema.analyzeDeclRef(fn_decl);
  28460         },
  28461         .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
  28462         .Float => float: {
  28463             const target = sema.mod.getTarget();
  28464             const double_bits = target.c_type_bit_size(.double);
  28465             const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget());
  28466             if (inst_bits >= double_bits) break :float inst;
  28467             switch (double_bits) {
  28468                 32 => break :float try sema.coerce(block, Type.f32, inst, inst_src),
  28469                 64 => break :float try sema.coerce(block, Type.f64, inst, inst_src),
  28470                 else => unreachable,
  28471             }
  28472         },
  28473         else => inst,
  28474     };
  28475 
  28476     const coerced_ty = sema.typeOf(coerced);
  28477     if (!try sema.validateExternType(coerced_ty, .param_ty)) {
  28478         const msg = msg: {
  28479             const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)});
  28480             errdefer msg.destroy(sema.gpa);
  28481 
  28482             const src_decl = sema.mod.declPtr(block.src_decl);
  28483             try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl, mod), coerced_ty, .param_ty);
  28484 
  28485             try sema.addDeclaredHereNote(msg, coerced_ty);
  28486             break :msg msg;
  28487         };
  28488         return sema.failWithOwnedErrorMsg(msg);
  28489     }
  28490     return coerced;
  28491 }
  28492 
  28493 // TODO migrate callsites to use storePtr2 instead.
  28494 fn storePtr(
  28495     sema: *Sema,
  28496     block: *Block,
  28497     src: LazySrcLoc,
  28498     ptr: Air.Inst.Ref,
  28499     uncasted_operand: Air.Inst.Ref,
  28500 ) CompileError!void {
  28501     const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store;
  28502     return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag);
  28503 }
  28504 
  28505 fn storePtr2(
  28506     sema: *Sema,
  28507     block: *Block,
  28508     src: LazySrcLoc,
  28509     ptr: Air.Inst.Ref,
  28510     ptr_src: LazySrcLoc,
  28511     uncasted_operand: Air.Inst.Ref,
  28512     operand_src: LazySrcLoc,
  28513     air_tag: Air.Inst.Tag,
  28514 ) CompileError!void {
  28515     const mod = sema.mod;
  28516     const ptr_ty = sema.typeOf(ptr);
  28517     if (ptr_ty.isConstPtr(mod))
  28518         return sema.fail(block, ptr_src, "cannot assign to constant", .{});
  28519 
  28520     const elem_ty = ptr_ty.childType(mod);
  28521 
  28522     // To generate better code for tuples, we detect a tuple operand here, and
  28523     // analyze field loads and stores directly. This avoids an extra allocation + memcpy
  28524     // which would occur if we used `coerce`.
  28525     // However, we avoid this mechanism if the destination element type is a tuple,
  28526     // because the regular store will be better for this case.
  28527     // If the destination type is a struct we don't want this mechanism to trigger, because
  28528     // this code does not handle tuple-to-struct coercion which requires dealing with missing
  28529     // fields.
  28530     const operand_ty = sema.typeOf(uncasted_operand);
  28531     if (operand_ty.isTuple(mod) and elem_ty.zigTypeTag(mod) == .Array) {
  28532         const field_count = operand_ty.structFieldCount(mod);
  28533         var i: u32 = 0;
  28534         while (i < field_count) : (i += 1) {
  28535             const elem_src = operand_src; // TODO better source location
  28536             const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i);
  28537             const elem_index = try sema.addIntUnsigned(Type.usize, i);
  28538             const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true);
  28539             try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
  28540         }
  28541         return;
  28542     }
  28543 
  28544     // TODO do the same thing for anon structs as for tuples above.
  28545     // However, beware of the need to handle missing/extra fields.
  28546 
  28547     const is_ret = air_tag == .ret_ptr;
  28548 
  28549     // Detect if we are storing an array operand to a bitcasted vector pointer.
  28550     // If so, we instead reach through the bitcasted pointer to the vector pointer,
  28551     // bitcast the array operand to a vector, and then lower this as a store of
  28552     // a vector value to a vector pointer. This generally results in better code,
  28553     // as well as working around an LLVM bug:
  28554     // https://github.com/ziglang/zig/issues/11154
  28555     if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
  28556         const vector_ty = sema.typeOf(vector_ptr).childType(mod);
  28557         const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
  28558             error.NotCoercible => unreachable,
  28559             else => |e| return e,
  28560         };
  28561         try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store);
  28562         return;
  28563     }
  28564 
  28565     const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
  28566         error.NotCoercible => unreachable,
  28567         else => |e| return e,
  28568     };
  28569     const maybe_operand_val = try sema.resolveMaybeUndefVal(operand);
  28570 
  28571     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  28572         const operand_val = maybe_operand_val orelse {
  28573             try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src);
  28574             break :rs operand_src;
  28575         };
  28576         if (ptr_val.isComptimeMutablePtr(mod)) {
  28577             try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
  28578             return;
  28579         } else break :rs ptr_src;
  28580     } else ptr_src;
  28581 
  28582     // We do this after the possible comptime store above, for the case of field_ptr stores
  28583     // to unions because we want the comptime tag to be set, even if the field type is void.
  28584     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null)
  28585         return;
  28586 
  28587     if (air_tag == .bitcast) {
  28588         // `air_tag == .bitcast` is used as a special case for `zirCoerceResultPtr`
  28589         // to avoid calling `requireRuntimeBlock` for the dummy block.
  28590         _ = try block.addBinOp(.store, ptr, operand);
  28591         return;
  28592     }
  28593 
  28594     try sema.requireRuntimeBlock(block, src, runtime_src);
  28595     try sema.queueFullTypeResolution(elem_ty);
  28596 
  28597     if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) {
  28598         const ptr_inst = Air.refToIndex(ptr).?;
  28599         const air_tags = sema.air_instructions.items(.tag);
  28600         if (air_tags[ptr_inst] == .ptr_elem_ptr) {
  28601             const ty_pl = sema.air_instructions.items(.data)[ptr_inst].ty_pl;
  28602             const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data;
  28603             _ = try block.addInst(.{
  28604                 .tag = .vector_store_elem,
  28605                 .data = .{ .vector_store_elem = .{
  28606                     .vector_ptr = bin_op.lhs,
  28607                     .payload = try block.sema.addExtra(Air.Bin{
  28608                         .lhs = bin_op.rhs,
  28609                         .rhs = operand,
  28610                     }),
  28611                 } },
  28612             });
  28613             return;
  28614         }
  28615         return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{
  28616             ptr_ty.fmt(sema.mod),
  28617         });
  28618     }
  28619 
  28620     if (is_ret) {
  28621         _ = try block.addBinOp(.store, ptr, operand);
  28622     } else {
  28623         _ = try block.addBinOp(air_tag, ptr, operand);
  28624     }
  28625 }
  28626 
  28627 /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector
  28628 /// pointer. Only if the final element type matches the vector element type, and the
  28629 /// lengths match.
  28630 fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref {
  28631     const mod = sema.mod;
  28632     const array_ty = sema.typeOf(ptr).childType(mod);
  28633     if (array_ty.zigTypeTag(mod) != .Array) return null;
  28634     var ptr_ref = ptr;
  28635     var ptr_inst = Air.refToIndex(ptr_ref) orelse return null;
  28636     const air_datas = sema.air_instructions.items(.data);
  28637     const air_tags = sema.air_instructions.items(.tag);
  28638     const vector_ty = while (air_tags[ptr_inst] == .bitcast) {
  28639         ptr_ref = air_datas[ptr_inst].ty_op.operand;
  28640         if (!sema.isKnownZigType(ptr_ref, .Pointer)) return null;
  28641         const child_ty = sema.typeOf(ptr_ref).childType(mod);
  28642         if (child_ty.zigTypeTag(mod) == .Vector) break child_ty;
  28643         ptr_inst = Air.refToIndex(ptr_ref) orelse return null;
  28644     } else return null;
  28645 
  28646     // We have a pointer-to-array and a pointer-to-vector. If the elements and
  28647     // lengths match, return the result.
  28648     if (array_ty.childType(mod).eql(vector_ty.childType(mod), sema.mod) and
  28649         array_ty.arrayLen(mod) == vector_ty.vectorLen(mod))
  28650     {
  28651         return ptr_ref;
  28652     } else {
  28653         return null;
  28654     }
  28655 }
  28656 
  28657 /// Call when you have Value objects rather than Air instructions, and you want to
  28658 /// assert the store must be done at comptime.
  28659 fn storePtrVal(
  28660     sema: *Sema,
  28661     block: *Block,
  28662     src: LazySrcLoc,
  28663     ptr_val: Value,
  28664     operand_val: Value,
  28665     operand_ty: Type,
  28666 ) !void {
  28667     const mod = sema.mod;
  28668     var mut_kit = try sema.beginComptimePtrMutation(block, src, ptr_val, operand_ty);
  28669     try sema.checkComptimeVarStore(block, src, mut_kit.mut_decl);
  28670 
  28671     switch (mut_kit.pointee) {
  28672         .direct => |val_ptr| {
  28673             if (mut_kit.mut_decl.runtime_index == .comptime_field_ptr) {
  28674                 if (!operand_val.eql(val_ptr.*, operand_ty, mod)) {
  28675                     // TODO use failWithInvalidComptimeFieldStore
  28676                     return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});
  28677                 }
  28678                 return;
  28679             }
  28680             val_ptr.* = (try operand_val.intern(operand_ty, mod)).toValue();
  28681         },
  28682         .reinterpret => |reinterpret| {
  28683             const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(mod));
  28684             const buffer = try sema.gpa.alloc(u8, abi_size);
  28685             defer sema.gpa.free(buffer);
  28686             reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, mod, buffer) catch |err| switch (err) {
  28687                 error.OutOfMemory => return error.OutOfMemory,
  28688                 error.ReinterpretDeclRef => unreachable,
  28689                 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
  28690                 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
  28691             };
  28692             operand_val.writeToMemory(operand_ty, mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) {
  28693                 error.OutOfMemory => return error.OutOfMemory,
  28694                 error.ReinterpretDeclRef => unreachable,
  28695                 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
  28696                 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
  28697             };
  28698 
  28699             reinterpret.val_ptr.* = (try (try Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena)).intern(mut_kit.ty, mod)).toValue();
  28700         },
  28701         .bad_decl_ty, .bad_ptr_ty => {
  28702             // TODO show the decl declaration site in a note and explain whether the decl
  28703             // or the pointer is the problematic type
  28704             return sema.fail(
  28705                 block,
  28706                 src,
  28707                 "comptime mutation of a reinterpreted pointer requires type '{}' to have a well-defined memory layout",
  28708                 .{mut_kit.ty.fmt(mod)},
  28709             );
  28710         },
  28711     }
  28712 }
  28713 
  28714 const ComptimePtrMutationKit = struct {
  28715     mut_decl: InternPool.Key.Ptr.Addr.MutDecl,
  28716     pointee: union(enum) {
  28717         /// The pointer type matches the actual comptime Value so a direct
  28718         /// modification is possible.
  28719         direct: *Value,
  28720         /// The largest parent Value containing pointee and having a well-defined memory layout.
  28721         /// This is used for bitcasting, if direct dereferencing failed.
  28722         reinterpret: struct {
  28723             val_ptr: *Value,
  28724             byte_offset: usize,
  28725         },
  28726         /// If the root decl could not be used as parent, this means `ty` is the type that
  28727         /// caused that by not having a well-defined layout.
  28728         /// This one means the Decl that owns the value trying to be modified does not
  28729         /// have a well defined memory layout.
  28730         bad_decl_ty,
  28731         /// If the root decl could not be used as parent, this means `ty` is the type that
  28732         /// caused that by not having a well-defined layout.
  28733         /// This one means the pointer type that is being stored through does not
  28734         /// have a well defined memory layout.
  28735         bad_ptr_ty,
  28736     },
  28737     ty: Type,
  28738 };
  28739 
  28740 fn beginComptimePtrMutation(
  28741     sema: *Sema,
  28742     block: *Block,
  28743     src: LazySrcLoc,
  28744     ptr_val: Value,
  28745     ptr_elem_ty: Type,
  28746 ) CompileError!ComptimePtrMutationKit {
  28747     const mod = sema.mod;
  28748     const ptr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr;
  28749     switch (ptr.addr) {
  28750         .decl, .int => unreachable, // isComptimeMutablePtr has been checked already
  28751         .mut_decl => |mut_decl| {
  28752             const decl = mod.declPtr(mut_decl.decl);
  28753             return sema.beginComptimePtrMutationInner(block, src, decl.ty, &decl.val, ptr_elem_ty, mut_decl);
  28754         },
  28755         .comptime_field => |comptime_field| {
  28756             const duped = try sema.arena.create(Value);
  28757             duped.* = comptime_field.toValue();
  28758             return sema.beginComptimePtrMutationInner(block, src, mod.intern_pool.typeOf(comptime_field).toType(), duped, ptr_elem_ty, .{
  28759                 .decl = undefined,
  28760                 .runtime_index = .comptime_field_ptr,
  28761             });
  28762         },
  28763         .eu_payload => |eu_ptr| {
  28764             const eu_ty = mod.intern_pool.typeOf(eu_ptr).toType().childType(mod);
  28765             var parent = try sema.beginComptimePtrMutation(block, src, eu_ptr.toValue(), eu_ty);
  28766             switch (parent.pointee) {
  28767                 .direct => |val_ptr| {
  28768                     const payload_ty = parent.ty.errorUnionPayload(mod);
  28769                     if (val_ptr.ip_index == .none and val_ptr.tag() == .eu_payload) {
  28770                         return ComptimePtrMutationKit{
  28771                             .mut_decl = parent.mut_decl,
  28772                             .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data },
  28773                             .ty = payload_ty,
  28774                         };
  28775                     } else {
  28776                         // An error union has been initialized to undefined at comptime and now we
  28777                         // are for the first time setting the payload. We must change the
  28778                         // representation of the error union from `undef` to `opt_payload`.
  28779 
  28780                         const payload = try sema.arena.create(Value.Payload.SubValue);
  28781                         payload.* = .{
  28782                             .base = .{ .tag = .eu_payload },
  28783                             .data = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(),
  28784                         };
  28785 
  28786                         val_ptr.* = Value.initPayload(&payload.base);
  28787 
  28788                         return ComptimePtrMutationKit{
  28789                             .mut_decl = parent.mut_decl,
  28790                             .pointee = .{ .direct = &payload.data },
  28791                             .ty = payload_ty,
  28792                         };
  28793                     }
  28794                 },
  28795                 .bad_decl_ty, .bad_ptr_ty => return parent,
  28796                 // Even though the parent value type has well-defined memory layout, our
  28797                 // pointer type does not.
  28798                 .reinterpret => return ComptimePtrMutationKit{
  28799                     .mut_decl = parent.mut_decl,
  28800                     .pointee = .bad_ptr_ty,
  28801                     .ty = eu_ty,
  28802                 },
  28803             }
  28804         },
  28805         .opt_payload => |opt_ptr| {
  28806             const opt_ty = mod.intern_pool.typeOf(opt_ptr).toType().childType(mod);
  28807             var parent = try sema.beginComptimePtrMutation(block, src, opt_ptr.toValue(), opt_ty);
  28808             switch (parent.pointee) {
  28809                 .direct => |val_ptr| {
  28810                     const payload_ty = parent.ty.optionalChild(mod);
  28811                     switch (val_ptr.ip_index) {
  28812                         .none => return ComptimePtrMutationKit{
  28813                             .mut_decl = parent.mut_decl,
  28814                             .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data },
  28815                             .ty = payload_ty,
  28816                         },
  28817                         else => {
  28818                             const payload_val = switch (mod.intern_pool.indexToKey(val_ptr.ip_index)) {
  28819                                 .undef => try mod.intern(.{ .undef = payload_ty.toIntern() }),
  28820                                 .opt => |opt| switch (opt.val) {
  28821                                     .none => try mod.intern(.{ .undef = payload_ty.toIntern() }),
  28822                                     else => |payload| payload,
  28823                                 },
  28824                                 else => unreachable,
  28825                             };
  28826 
  28827                             // An optional has been initialized to undefined at comptime and now we
  28828                             // are for the first time setting the payload. We must change the
  28829                             // representation of the optional from `undef` to `opt_payload`.
  28830 
  28831                             const payload = try sema.arena.create(Value.Payload.SubValue);
  28832                             payload.* = .{
  28833                                 .base = .{ .tag = .opt_payload },
  28834                                 .data = payload_val.toValue(),
  28835                             };
  28836 
  28837                             val_ptr.* = Value.initPayload(&payload.base);
  28838 
  28839                             return ComptimePtrMutationKit{
  28840                                 .mut_decl = parent.mut_decl,
  28841                                 .pointee = .{ .direct = &payload.data },
  28842                                 .ty = payload_ty,
  28843                             };
  28844                         },
  28845                     }
  28846                 },
  28847                 .bad_decl_ty, .bad_ptr_ty => return parent,
  28848                 // Even though the parent value type has well-defined memory layout, our
  28849                 // pointer type does not.
  28850                 .reinterpret => return ComptimePtrMutationKit{
  28851                     .mut_decl = parent.mut_decl,
  28852                     .pointee = .bad_ptr_ty,
  28853                     .ty = opt_ty,
  28854                 },
  28855             }
  28856         },
  28857         .elem => |elem_ptr| {
  28858             const base_elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod);
  28859             var parent = try sema.beginComptimePtrMutation(block, src, elem_ptr.base.toValue(), base_elem_ty);
  28860 
  28861             switch (parent.pointee) {
  28862                 .direct => |val_ptr| switch (parent.ty.zigTypeTag(mod)) {
  28863                     .Array, .Vector => {
  28864                         const check_len = parent.ty.arrayLenIncludingSentinel(mod);
  28865                         if (elem_ptr.index >= check_len) {
  28866                             // TODO have the parent include the decl so we can say "declared here"
  28867                             return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
  28868                                 elem_ptr.index, check_len,
  28869                             });
  28870                         }
  28871                         const elem_ty = parent.ty.childType(mod);
  28872 
  28873                         // We might have a pointer to multiple elements of the array (e.g. a pointer
  28874                         // to a sub-array). In this case, we just have to reinterpret the relevant
  28875                         // bytes of the whole array rather than any single element.
  28876                         const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty);
  28877                         if (elem_abi_size_u64 < try sema.typeAbiSize(ptr_elem_ty)) {
  28878                             const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
  28879                             const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
  28880                             return .{
  28881                                 .mut_decl = parent.mut_decl,
  28882                                 .pointee = .{ .reinterpret = .{
  28883                                     .val_ptr = val_ptr,
  28884                                     .byte_offset = elem_abi_size * elem_idx,
  28885                                 } },
  28886                                 .ty = parent.ty,
  28887                             };
  28888                         }
  28889 
  28890                         switch (val_ptr.ip_index) {
  28891                             .none => switch (val_ptr.tag()) {
  28892                                 .bytes => {
  28893                                     // An array is memory-optimized to store a slice of bytes, but we are about
  28894                                     // to modify an individual field and the representation has to change.
  28895                                     // If we wanted to avoid this, there would need to be special detection
  28896                                     // elsewhere to identify when writing a value to an array element that is stored
  28897                                     // using the `bytes` tag, and handle it without making a call to this function.
  28898                                     const arena = mod.tmp_hack_arena.allocator();
  28899 
  28900                                     const bytes = val_ptr.castTag(.bytes).?.data;
  28901                                     const dest_len = parent.ty.arrayLenIncludingSentinel(mod);
  28902                                     // bytes.len may be one greater than dest_len because of the case when
  28903                                     // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
  28904                                     assert(bytes.len >= dest_len);
  28905                                     const elems = try arena.alloc(Value, @as(usize, @intCast(dest_len)));
  28906                                     for (elems, 0..) |*elem, i| {
  28907                                         elem.* = try mod.intValue(elem_ty, bytes[i]);
  28908                                     }
  28909 
  28910                                     val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  28911 
  28912                                     return beginComptimePtrMutationInner(
  28913                                         sema,
  28914                                         block,
  28915                                         src,
  28916                                         elem_ty,
  28917                                         &elems[@as(usize, @intCast(elem_ptr.index))],
  28918                                         ptr_elem_ty,
  28919                                         parent.mut_decl,
  28920                                     );
  28921                                 },
  28922                                 .repeated => {
  28923                                     // An array is memory-optimized to store only a single element value, and
  28924                                     // that value is understood to be the same for the entire length of the array.
  28925                                     // However, now we want to modify an individual field and so the
  28926                                     // representation has to change.  If we wanted to avoid this, there would
  28927                                     // need to be special detection elsewhere to identify when writing a value to an
  28928                                     // array element that is stored using the `repeated` tag, and handle it
  28929                                     // without making a call to this function.
  28930                                     const arena = mod.tmp_hack_arena.allocator();
  28931 
  28932                                     const repeated_val = try val_ptr.castTag(.repeated).?.data.intern(parent.ty.childType(mod), mod);
  28933                                     const array_len_including_sentinel =
  28934                                         try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
  28935                                     const elems = try arena.alloc(Value, array_len_including_sentinel);
  28936                                     @memset(elems, repeated_val.toValue());
  28937 
  28938                                     val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  28939 
  28940                                     return beginComptimePtrMutationInner(
  28941                                         sema,
  28942                                         block,
  28943                                         src,
  28944                                         elem_ty,
  28945                                         &elems[@as(usize, @intCast(elem_ptr.index))],
  28946                                         ptr_elem_ty,
  28947                                         parent.mut_decl,
  28948                                     );
  28949                                 },
  28950 
  28951                                 .aggregate => return beginComptimePtrMutationInner(
  28952                                     sema,
  28953                                     block,
  28954                                     src,
  28955                                     elem_ty,
  28956                                     &val_ptr.castTag(.aggregate).?.data[@as(usize, @intCast(elem_ptr.index))],
  28957                                     ptr_elem_ty,
  28958                                     parent.mut_decl,
  28959                                 ),
  28960 
  28961                                 else => unreachable,
  28962                             },
  28963                             else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
  28964                                 .undef => {
  28965                                     // An array has been initialized to undefined at comptime and now we
  28966                                     // are for the first time setting an element. We must change the representation
  28967                                     // of the array from `undef` to `array`.
  28968                                     const arena = mod.tmp_hack_arena.allocator();
  28969 
  28970                                     const array_len_including_sentinel =
  28971                                         try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
  28972                                     const elems = try arena.alloc(Value, array_len_including_sentinel);
  28973                                     @memset(elems, (try mod.intern(.{ .undef = elem_ty.toIntern() })).toValue());
  28974 
  28975                                     val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  28976 
  28977                                     return beginComptimePtrMutationInner(
  28978                                         sema,
  28979                                         block,
  28980                                         src,
  28981                                         elem_ty,
  28982                                         &elems[@as(usize, @intCast(elem_ptr.index))],
  28983                                         ptr_elem_ty,
  28984                                         parent.mut_decl,
  28985                                     );
  28986                                 },
  28987                                 else => unreachable,
  28988                             },
  28989                         }
  28990                     },
  28991                     else => {
  28992                         if (elem_ptr.index != 0) {
  28993                             // TODO include a "declared here" note for the decl
  28994                             return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
  28995                                 elem_ptr.index,
  28996                             });
  28997                         }
  28998                         return beginComptimePtrMutationInner(
  28999                             sema,
  29000                             block,
  29001                             src,
  29002                             parent.ty,
  29003                             val_ptr,
  29004                             ptr_elem_ty,
  29005                             parent.mut_decl,
  29006                         );
  29007                     },
  29008                 },
  29009                 .reinterpret => |reinterpret| {
  29010                     if (!base_elem_ty.hasWellDefinedLayout(mod)) {
  29011                         // Even though the parent value type has well-defined memory layout, our
  29012                         // pointer type does not.
  29013                         return ComptimePtrMutationKit{
  29014                             .mut_decl = parent.mut_decl,
  29015                             .pointee = .bad_ptr_ty,
  29016                             .ty = base_elem_ty,
  29017                         };
  29018                     }
  29019 
  29020                     const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty);
  29021                     const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
  29022                     const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
  29023                     return ComptimePtrMutationKit{
  29024                         .mut_decl = parent.mut_decl,
  29025                         .pointee = .{ .reinterpret = .{
  29026                             .val_ptr = reinterpret.val_ptr,
  29027                             .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_idx,
  29028                         } },
  29029                         .ty = parent.ty,
  29030                     };
  29031                 },
  29032                 .bad_decl_ty, .bad_ptr_ty => return parent,
  29033             }
  29034         },
  29035         .field => |field_ptr| {
  29036             const base_child_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod);
  29037             const field_index = @as(u32, @intCast(field_ptr.index));
  29038 
  29039             var parent = try sema.beginComptimePtrMutation(block, src, field_ptr.base.toValue(), base_child_ty);
  29040             switch (parent.pointee) {
  29041                 .direct => |val_ptr| switch (val_ptr.ip_index) {
  29042                     .empty_struct => {
  29043                         const duped = try sema.arena.create(Value);
  29044                         duped.* = val_ptr.*;
  29045                         return beginComptimePtrMutationInner(
  29046                             sema,
  29047                             block,
  29048                             src,
  29049                             parent.ty.structFieldType(field_index, mod),
  29050                             duped,
  29051                             ptr_elem_ty,
  29052                             parent.mut_decl,
  29053                         );
  29054                     },
  29055                     .none => switch (val_ptr.tag()) {
  29056                         .aggregate => return beginComptimePtrMutationInner(
  29057                             sema,
  29058                             block,
  29059                             src,
  29060                             parent.ty.structFieldType(field_index, mod),
  29061                             &val_ptr.castTag(.aggregate).?.data[field_index],
  29062                             ptr_elem_ty,
  29063                             parent.mut_decl,
  29064                         ),
  29065                         .repeated => {
  29066                             const arena = mod.tmp_hack_arena.allocator();
  29067 
  29068                             const elems = try arena.alloc(Value, parent.ty.structFieldCount(mod));
  29069                             @memset(elems, val_ptr.castTag(.repeated).?.data);
  29070                             val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  29071 
  29072                             return beginComptimePtrMutationInner(
  29073                                 sema,
  29074                                 block,
  29075                                 src,
  29076                                 parent.ty.structFieldType(field_index, mod),
  29077                                 &elems[field_index],
  29078                                 ptr_elem_ty,
  29079                                 parent.mut_decl,
  29080                             );
  29081                         },
  29082                         .@"union" => {
  29083                             // We need to set the active field of the union.
  29084                             const union_tag_ty = base_child_ty.unionTagTypeHypothetical(mod);
  29085 
  29086                             const payload = &val_ptr.castTag(.@"union").?.data;
  29087                             payload.tag = try mod.enumValueFieldIndex(union_tag_ty, field_index);
  29088 
  29089                             return beginComptimePtrMutationInner(
  29090                                 sema,
  29091                                 block,
  29092                                 src,
  29093                                 parent.ty.structFieldType(field_index, mod),
  29094                                 &payload.val,
  29095                                 ptr_elem_ty,
  29096                                 parent.mut_decl,
  29097                             );
  29098                         },
  29099                         .slice => switch (field_index) {
  29100                             Value.slice_ptr_index => return beginComptimePtrMutationInner(
  29101                                 sema,
  29102                                 block,
  29103                                 src,
  29104                                 parent.ty.slicePtrFieldType(mod),
  29105                                 &val_ptr.castTag(.slice).?.data.ptr,
  29106                                 ptr_elem_ty,
  29107                                 parent.mut_decl,
  29108                             ),
  29109 
  29110                             Value.slice_len_index => return beginComptimePtrMutationInner(
  29111                                 sema,
  29112                                 block,
  29113                                 src,
  29114                                 Type.usize,
  29115                                 &val_ptr.castTag(.slice).?.data.len,
  29116                                 ptr_elem_ty,
  29117                                 parent.mut_decl,
  29118                             ),
  29119 
  29120                             else => unreachable,
  29121                         },
  29122                         else => unreachable,
  29123                     },
  29124                     else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
  29125                         .undef => {
  29126                             // A struct or union has been initialized to undefined at comptime and now we
  29127                             // are for the first time setting a field. We must change the representation
  29128                             // of the struct/union from `undef` to `struct`/`union`.
  29129                             const arena = mod.tmp_hack_arena.allocator();
  29130 
  29131                             switch (parent.ty.zigTypeTag(mod)) {
  29132                                 .Struct => {
  29133                                     const fields = try arena.alloc(Value, parent.ty.structFieldCount(mod));
  29134                                     for (fields, 0..) |*field, i| field.* = (try mod.intern(.{
  29135                                         .undef = parent.ty.structFieldType(i, mod).toIntern(),
  29136                                     })).toValue();
  29137 
  29138                                     val_ptr.* = try Value.Tag.aggregate.create(arena, fields);
  29139 
  29140                                     return beginComptimePtrMutationInner(
  29141                                         sema,
  29142                                         block,
  29143                                         src,
  29144                                         parent.ty.structFieldType(field_index, mod),
  29145                                         &fields[field_index],
  29146                                         ptr_elem_ty,
  29147                                         parent.mut_decl,
  29148                                     );
  29149                                 },
  29150                                 .Union => {
  29151                                     const payload = try arena.create(Value.Payload.Union);
  29152                                     const tag_ty = parent.ty.unionTagTypeHypothetical(mod);
  29153                                     const payload_ty = parent.ty.structFieldType(field_index, mod);
  29154                                     payload.* = .{ .data = .{
  29155                                         .tag = try mod.enumValueFieldIndex(tag_ty, field_index),
  29156                                         .val = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(),
  29157                                     } };
  29158 
  29159                                     val_ptr.* = Value.initPayload(&payload.base);
  29160 
  29161                                     return beginComptimePtrMutationInner(
  29162                                         sema,
  29163                                         block,
  29164                                         src,
  29165                                         payload_ty,
  29166                                         &payload.data.val,
  29167                                         ptr_elem_ty,
  29168                                         parent.mut_decl,
  29169                                     );
  29170                                 },
  29171                                 .Pointer => {
  29172                                     assert(parent.ty.isSlice(mod));
  29173                                     const ptr_ty = parent.ty.slicePtrFieldType(mod);
  29174                                     val_ptr.* = try Value.Tag.slice.create(arena, .{
  29175                                         .ptr = (try mod.intern(.{ .undef = ptr_ty.toIntern() })).toValue(),
  29176                                         .len = (try mod.intern(.{ .undef = .usize_type })).toValue(),
  29177                                     });
  29178 
  29179                                     switch (field_index) {
  29180                                         Value.slice_ptr_index => return beginComptimePtrMutationInner(
  29181                                             sema,
  29182                                             block,
  29183                                             src,
  29184                                             ptr_ty,
  29185                                             &val_ptr.castTag(.slice).?.data.ptr,
  29186                                             ptr_elem_ty,
  29187                                             parent.mut_decl,
  29188                                         ),
  29189                                         Value.slice_len_index => return beginComptimePtrMutationInner(
  29190                                             sema,
  29191                                             block,
  29192                                             src,
  29193                                             Type.usize,
  29194                                             &val_ptr.castTag(.slice).?.data.len,
  29195                                             ptr_elem_ty,
  29196                                             parent.mut_decl,
  29197                                         ),
  29198 
  29199                                         else => unreachable,
  29200                                     }
  29201                                 },
  29202                                 else => unreachable,
  29203                             }
  29204                         },
  29205                         else => unreachable,
  29206                     },
  29207                 },
  29208                 .reinterpret => |reinterpret| {
  29209                     const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod);
  29210                     const field_offset = try sema.usizeCast(block, src, field_offset_u64);
  29211                     return ComptimePtrMutationKit{
  29212                         .mut_decl = parent.mut_decl,
  29213                         .pointee = .{ .reinterpret = .{
  29214                             .val_ptr = reinterpret.val_ptr,
  29215                             .byte_offset = reinterpret.byte_offset + field_offset,
  29216                         } },
  29217                         .ty = parent.ty,
  29218                     };
  29219                 },
  29220                 .bad_decl_ty, .bad_ptr_ty => return parent,
  29221             }
  29222         },
  29223     }
  29224 }
  29225 
  29226 fn beginComptimePtrMutationInner(
  29227     sema: *Sema,
  29228     block: *Block,
  29229     src: LazySrcLoc,
  29230     decl_ty: Type,
  29231     decl_val: *Value,
  29232     ptr_elem_ty: Type,
  29233     mut_decl: InternPool.Key.Ptr.Addr.MutDecl,
  29234 ) CompileError!ComptimePtrMutationKit {
  29235     const mod = sema.mod;
  29236     const target = mod.getTarget();
  29237     const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok;
  29238 
  29239     decl_val.* = try decl_val.unintern(sema.arena, mod);
  29240 
  29241     if (coerce_ok) {
  29242         return ComptimePtrMutationKit{
  29243             .mut_decl = mut_decl,
  29244             .pointee = .{ .direct = decl_val },
  29245             .ty = decl_ty,
  29246         };
  29247     }
  29248 
  29249     // Handle the case that the decl is an array and we're actually trying to point to an element.
  29250     if (decl_ty.isArrayOrVector(mod)) {
  29251         const decl_elem_ty = decl_ty.childType(mod);
  29252         if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) {
  29253             return ComptimePtrMutationKit{
  29254                 .mut_decl = mut_decl,
  29255                 .pointee = .{ .direct = decl_val },
  29256                 .ty = decl_ty,
  29257             };
  29258         }
  29259     }
  29260 
  29261     if (!decl_ty.hasWellDefinedLayout(mod)) {
  29262         return ComptimePtrMutationKit{
  29263             .mut_decl = mut_decl,
  29264             .pointee = .bad_decl_ty,
  29265             .ty = decl_ty,
  29266         };
  29267     }
  29268     if (!ptr_elem_ty.hasWellDefinedLayout(mod)) {
  29269         return ComptimePtrMutationKit{
  29270             .mut_decl = mut_decl,
  29271             .pointee = .bad_ptr_ty,
  29272             .ty = ptr_elem_ty,
  29273         };
  29274     }
  29275     return ComptimePtrMutationKit{
  29276         .mut_decl = mut_decl,
  29277         .pointee = .{ .reinterpret = .{
  29278             .val_ptr = decl_val,
  29279             .byte_offset = 0,
  29280         } },
  29281         .ty = decl_ty,
  29282     };
  29283 }
  29284 
  29285 const TypedValueAndOffset = struct {
  29286     tv: TypedValue,
  29287     byte_offset: usize,
  29288 };
  29289 
  29290 const ComptimePtrLoadKit = struct {
  29291     /// The Value and Type corresponding to the pointee of the provided pointer.
  29292     /// If a direct dereference is not possible, this is null.
  29293     pointee: ?TypedValue,
  29294     /// The largest parent Value containing `pointee` and having a well-defined memory layout.
  29295     /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null).
  29296     parent: ?TypedValueAndOffset,
  29297     /// Whether the `pointee` could be mutated by further
  29298     /// semantic analysis and a copy must be performed.
  29299     is_mutable: bool,
  29300     /// If the root decl could not be used as `parent`, this is the type that
  29301     /// caused that by not having a well-defined layout
  29302     ty_without_well_defined_layout: ?Type,
  29303 };
  29304 
  29305 const ComptimePtrLoadError = CompileError || error{
  29306     RuntimeLoad,
  29307 };
  29308 
  29309 /// If `maybe_array_ty` is provided, it will be used to directly dereference an
  29310 /// .elem_ptr of type T to a value of [N]T, if necessary.
  29311 fn beginComptimePtrLoad(
  29312     sema: *Sema,
  29313     block: *Block,
  29314     src: LazySrcLoc,
  29315     ptr_val: Value,
  29316     maybe_array_ty: ?Type,
  29317 ) ComptimePtrLoadError!ComptimePtrLoadKit {
  29318     const mod = sema.mod;
  29319     const target = mod.getTarget();
  29320 
  29321     var deref: ComptimePtrLoadKit = switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) {
  29322         .ptr => |ptr| switch (ptr.addr) {
  29323             .decl, .mut_decl => blk: {
  29324                 const decl_index = switch (ptr.addr) {
  29325                     .decl => |decl| decl,
  29326                     .mut_decl => |mut_decl| mut_decl.decl,
  29327                     else => unreachable,
  29328                 };
  29329                 const is_mutable = ptr.addr == .mut_decl;
  29330                 const decl = mod.declPtr(decl_index);
  29331                 const decl_tv = try decl.typedValue();
  29332                 if (decl.val.getVariable(mod) != null) return error.RuntimeLoad;
  29333 
  29334                 const layout_defined = decl.ty.hasWellDefinedLayout(mod);
  29335                 break :blk ComptimePtrLoadKit{
  29336                     .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
  29337                     .pointee = decl_tv,
  29338                     .is_mutable = is_mutable,
  29339                     .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null,
  29340                 };
  29341             },
  29342             .int => return error.RuntimeLoad,
  29343             .eu_payload, .opt_payload => |container_ptr| blk: {
  29344                 const container_ty = mod.intern_pool.typeOf(container_ptr).toType().childType(mod);
  29345                 const payload_ty = switch (ptr.addr) {
  29346                     .eu_payload => container_ty.errorUnionPayload(mod),
  29347                     .opt_payload => container_ty.optionalChild(mod),
  29348                     else => unreachable,
  29349                 };
  29350                 var deref = try sema.beginComptimePtrLoad(block, src, container_ptr.toValue(), container_ty);
  29351 
  29352                 // eu_payload and opt_payload never have a well-defined layout
  29353                 if (deref.parent != null) {
  29354                     deref.parent = null;
  29355                     deref.ty_without_well_defined_layout = container_ty;
  29356                 }
  29357 
  29358                 if (deref.pointee) |*tv| {
  29359                     const coerce_in_mem_ok =
  29360                         (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or
  29361                         (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;
  29362                     if (coerce_in_mem_ok) {
  29363                         const payload_val = switch (tv.val.ip_index) {
  29364                             .none => tv.val.cast(Value.Payload.SubValue).?.data,
  29365                             .null_value => return sema.fail(block, src, "attempt to use null value", .{}),
  29366                             else => switch (mod.intern_pool.indexToKey(tv.val.toIntern())) {
  29367                                 .error_union => |error_union| switch (error_union.val) {
  29368                                     .err_name => |err_name| return sema.fail(
  29369                                         block,
  29370                                         src,
  29371                                         "attempt to unwrap error: {}",
  29372                                         .{err_name.fmt(&mod.intern_pool)},
  29373                                     ),
  29374                                     .payload => |payload| payload,
  29375                                 },
  29376                                 .opt => |opt| switch (opt.val) {
  29377                                     .none => return sema.fail(block, src, "attempt to use null value", .{}),
  29378                                     else => |payload| payload,
  29379                                 },
  29380                                 else => unreachable,
  29381                             }.toValue(),
  29382                         };
  29383                         tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
  29384                         break :blk deref;
  29385                     }
  29386                 }
  29387                 deref.pointee = null;
  29388                 break :blk deref;
  29389             },
  29390             .comptime_field => |comptime_field| blk: {
  29391                 const field_ty = mod.intern_pool.typeOf(comptime_field).toType();
  29392                 break :blk ComptimePtrLoadKit{
  29393                     .parent = null,
  29394                     .pointee = .{ .ty = field_ty, .val = comptime_field.toValue() },
  29395                     .is_mutable = false,
  29396                     .ty_without_well_defined_layout = field_ty,
  29397                 };
  29398             },
  29399             .elem => |elem_ptr| blk: {
  29400                 const elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod);
  29401                 var deref = try sema.beginComptimePtrLoad(block, src, elem_ptr.base.toValue(), null);
  29402 
  29403                 // This code assumes that elem_ptrs have been "flattened" in order for direct dereference
  29404                 // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that
  29405                 // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened"
  29406                 switch (mod.intern_pool.indexToKey(elem_ptr.base)) {
  29407                     .ptr => |base_ptr| switch (base_ptr.addr) {
  29408                         .elem => |base_elem| assert(!mod.intern_pool.typeOf(base_elem.base).toType().elemType2(mod).eql(elem_ty, mod)),
  29409                         else => {},
  29410                     },
  29411                     else => {},
  29412                 }
  29413 
  29414                 if (elem_ptr.index != 0) {
  29415                     if (elem_ty.hasWellDefinedLayout(mod)) {
  29416                         if (deref.parent) |*parent| {
  29417                             // Update the byte offset (in-place)
  29418                             const elem_size = try sema.typeAbiSize(elem_ty);
  29419                             const offset = parent.byte_offset + elem_size * elem_ptr.index;
  29420                             parent.byte_offset = try sema.usizeCast(block, src, offset);
  29421                         }
  29422                     } else {
  29423                         deref.parent = null;
  29424                         deref.ty_without_well_defined_layout = elem_ty;
  29425                     }
  29426                 }
  29427 
  29428                 // If we're loading an elem that was derived from a different type
  29429                 // than the true type of the underlying decl, we cannot deref directly
  29430                 const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector(mod)) x: {
  29431                     const deref_elem_ty = deref.pointee.?.ty.childType(mod);
  29432                     break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or
  29433                         (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok;
  29434                 } else false;
  29435                 if (!ty_matches) {
  29436                     deref.pointee = null;
  29437                     break :blk deref;
  29438                 }
  29439 
  29440                 var array_tv = deref.pointee.?;
  29441                 const check_len = array_tv.ty.arrayLenIncludingSentinel(mod);
  29442                 if (maybe_array_ty) |load_ty| {
  29443                     // It's possible that we're loading a [N]T, in which case we'd like to slice
  29444                     // the pointee array directly from our parent array.
  29445                     if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) {
  29446                         const len = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod));
  29447                         const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
  29448                         deref.pointee = if (elem_ptr.index + len <= check_len) TypedValue{
  29449                             .ty = try mod.arrayType(.{
  29450                                 .len = len,
  29451                                 .child = elem_ty.toIntern(),
  29452                             }),
  29453                             .val = try array_tv.val.sliceArray(mod, sema.arena, elem_idx, elem_idx + len),
  29454                         } else null;
  29455                         break :blk deref;
  29456                     }
  29457                 }
  29458 
  29459                 if (elem_ptr.index >= check_len) {
  29460                     deref.pointee = null;
  29461                     break :blk deref;
  29462                 }
  29463                 if (elem_ptr.index == check_len - 1) {
  29464                     if (array_tv.ty.sentinel(mod)) |sent| {
  29465                         deref.pointee = TypedValue{
  29466                             .ty = elem_ty,
  29467                             .val = sent,
  29468                         };
  29469                         break :blk deref;
  29470                     }
  29471                 }
  29472                 deref.pointee = TypedValue{
  29473                     .ty = elem_ty,
  29474                     .val = try array_tv.val.elemValue(mod, @as(usize, @intCast(elem_ptr.index))),
  29475                 };
  29476                 break :blk deref;
  29477             },
  29478             .field => |field_ptr| blk: {
  29479                 const field_index = @as(u32, @intCast(field_ptr.index));
  29480                 const container_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod);
  29481                 var deref = try sema.beginComptimePtrLoad(block, src, field_ptr.base.toValue(), container_ty);
  29482 
  29483                 if (container_ty.hasWellDefinedLayout(mod)) {
  29484                     const struct_obj = mod.typeToStruct(container_ty);
  29485                     if (struct_obj != null and struct_obj.?.layout == .Packed) {
  29486                         // packed structs are not byte addressable
  29487                         deref.parent = null;
  29488                     } else if (deref.parent) |*parent| {
  29489                         // Update the byte offset (in-place)
  29490                         try sema.resolveTypeLayout(container_ty);
  29491                         const field_offset = container_ty.structFieldOffset(field_index, mod);
  29492                         parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset);
  29493                     }
  29494                 } else {
  29495                     deref.parent = null;
  29496                     deref.ty_without_well_defined_layout = container_ty;
  29497                 }
  29498 
  29499                 const tv = deref.pointee orelse {
  29500                     deref.pointee = null;
  29501                     break :blk deref;
  29502                 };
  29503                 const coerce_in_mem_ok =
  29504                     (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or
  29505                     (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;
  29506                 if (!coerce_in_mem_ok) {
  29507                     deref.pointee = null;
  29508                     break :blk deref;
  29509                 }
  29510 
  29511                 if (container_ty.isSlice(mod)) {
  29512                     deref.pointee = switch (field_index) {
  29513                         Value.slice_ptr_index => TypedValue{
  29514                             .ty = container_ty.slicePtrFieldType(mod),
  29515                             .val = tv.val.slicePtr(mod),
  29516                         },
  29517                         Value.slice_len_index => TypedValue{
  29518                             .ty = Type.usize,
  29519                             .val = mod.intern_pool.indexToKey(try tv.val.intern(tv.ty, mod)).ptr.len.toValue(),
  29520                         },
  29521                         else => unreachable,
  29522                     };
  29523                 } else {
  29524                     const field_ty = container_ty.structFieldType(field_index, mod);
  29525                     deref.pointee = TypedValue{
  29526                         .ty = field_ty,
  29527                         .val = try tv.val.fieldValue(mod, field_index),
  29528                     };
  29529                 }
  29530                 break :blk deref;
  29531             },
  29532         },
  29533         .opt => |opt| switch (opt.val) {
  29534             .none => return sema.fail(block, src, "attempt to use null value", .{}),
  29535             else => |payload| try sema.beginComptimePtrLoad(block, src, payload.toValue(), null),
  29536         },
  29537         else => unreachable,
  29538     };
  29539 
  29540     if (deref.pointee) |tv| {
  29541         if (deref.parent == null and tv.ty.hasWellDefinedLayout(mod)) {
  29542             deref.parent = .{ .tv = tv, .byte_offset = 0 };
  29543         }
  29544     }
  29545     return deref;
  29546 }
  29547 
  29548 fn bitCast(
  29549     sema: *Sema,
  29550     block: *Block,
  29551     dest_ty_unresolved: Type,
  29552     inst: Air.Inst.Ref,
  29553     inst_src: LazySrcLoc,
  29554     operand_src: ?LazySrcLoc,
  29555 ) CompileError!Air.Inst.Ref {
  29556     const mod = sema.mod;
  29557     const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved);
  29558     try sema.resolveTypeLayout(dest_ty);
  29559 
  29560     const old_ty = try sema.resolveTypeFields(sema.typeOf(inst));
  29561     try sema.resolveTypeLayout(old_ty);
  29562 
  29563     const dest_bits = dest_ty.bitSize(mod);
  29564     const old_bits = old_ty.bitSize(mod);
  29565 
  29566     if (old_bits != dest_bits) {
  29567         return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{
  29568             dest_ty.fmt(mod),
  29569             dest_bits,
  29570             old_ty.fmt(mod),
  29571             old_bits,
  29572         });
  29573     }
  29574 
  29575     if (try sema.resolveMaybeUndefVal(inst)) |val| {
  29576         if (try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0)) |result_val| {
  29577             return sema.addConstant(result_val);
  29578         }
  29579     }
  29580     try sema.requireRuntimeBlock(block, inst_src, operand_src);
  29581     return block.addBitCast(dest_ty, inst);
  29582 }
  29583 
  29584 fn bitCastVal(
  29585     sema: *Sema,
  29586     block: *Block,
  29587     src: LazySrcLoc,
  29588     val: Value,
  29589     old_ty: Type,
  29590     new_ty: Type,
  29591     buffer_offset: usize,
  29592 ) !?Value {
  29593     const mod = sema.mod;
  29594     if (old_ty.eql(new_ty, mod)) return val;
  29595 
  29596     // For types with well-defined memory layouts, we serialize them a byte buffer,
  29597     // then deserialize to the new type.
  29598     const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(mod));
  29599     const buffer = try sema.gpa.alloc(u8, abi_size);
  29600     defer sema.gpa.free(buffer);
  29601     val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) {
  29602         error.OutOfMemory => return error.OutOfMemory,
  29603         error.ReinterpretDeclRef => return null,
  29604         error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
  29605         error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}),
  29606     };
  29607     return try Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena);
  29608 }
  29609 
  29610 fn coerceArrayPtrToSlice(
  29611     sema: *Sema,
  29612     block: *Block,
  29613     dest_ty: Type,
  29614     inst: Air.Inst.Ref,
  29615     inst_src: LazySrcLoc,
  29616 ) CompileError!Air.Inst.Ref {
  29617     const mod = sema.mod;
  29618     if (try sema.resolveMaybeUndefVal(inst)) |val| {
  29619         const ptr_array_ty = sema.typeOf(inst);
  29620         const array_ty = ptr_array_ty.childType(mod);
  29621         const slice_val = try mod.intern(.{ .ptr = .{
  29622             .ty = dest_ty.toIntern(),
  29623             .addr = switch (mod.intern_pool.indexToKey(val.toIntern())) {
  29624                 .undef => .{ .int = try mod.intern(.{ .undef = .usize_type }) },
  29625                 .ptr => |ptr| ptr.addr,
  29626                 else => unreachable,
  29627             },
  29628             .len = (try mod.intValue(Type.usize, array_ty.arrayLen(mod))).toIntern(),
  29629         } });
  29630         return sema.addConstant(slice_val.toValue());
  29631     }
  29632     try sema.requireRuntimeBlock(block, inst_src, null);
  29633     return block.addTyOp(.array_to_slice, dest_ty, inst);
  29634 }
  29635 
  29636 fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool {
  29637     const mod = sema.mod;
  29638     const dest_info = dest_ty.ptrInfo(mod);
  29639     const inst_info = inst_ty.ptrInfo(mod);
  29640     const len0 = (inst_info.child.toType().zigTypeTag(mod) == .Array and (inst_info.child.toType().arrayLenIncludingSentinel(mod) == 0 or
  29641         (inst_info.child.toType().arrayLen(mod) == 0 and dest_info.sentinel == .none and dest_info.flags.size != .C and dest_info.flags.size != .Many))) or
  29642         (inst_info.child.toType().isTuple(mod) and inst_info.child.toType().structFieldCount(mod) == 0);
  29643 
  29644     const ok_cv_qualifiers =
  29645         ((!inst_info.flags.is_const or dest_info.flags.is_const) or len0) and
  29646         (!inst_info.flags.is_volatile or dest_info.flags.is_volatile);
  29647 
  29648     if (!ok_cv_qualifiers) {
  29649         in_memory_result.* = .{ .ptr_qualifiers = .{
  29650             .actual_const = inst_info.flags.is_const,
  29651             .wanted_const = dest_info.flags.is_const,
  29652             .actual_volatile = inst_info.flags.is_volatile,
  29653             .wanted_volatile = dest_info.flags.is_volatile,
  29654         } };
  29655         return false;
  29656     }
  29657     if (dest_info.flags.address_space != inst_info.flags.address_space) {
  29658         in_memory_result.* = .{ .ptr_addrspace = .{
  29659             .actual = inst_info.flags.address_space,
  29660             .wanted = dest_info.flags.address_space,
  29661         } };
  29662         return false;
  29663     }
  29664     if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true;
  29665     if (len0) return true;
  29666 
  29667     const inst_align = inst_info.flags.alignment.toByteUnitsOptional() orelse
  29668         inst_info.child.toType().abiAlignment(mod);
  29669 
  29670     const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse
  29671         dest_info.child.toType().abiAlignment(mod);
  29672 
  29673     if (dest_align > inst_align) {
  29674         in_memory_result.* = .{ .ptr_alignment = .{
  29675             .actual = inst_align,
  29676             .wanted = dest_align,
  29677         } };
  29678         return false;
  29679     }
  29680     return true;
  29681 }
  29682 
  29683 fn coerceCompatiblePtrs(
  29684     sema: *Sema,
  29685     block: *Block,
  29686     dest_ty: Type,
  29687     inst: Air.Inst.Ref,
  29688     inst_src: LazySrcLoc,
  29689 ) !Air.Inst.Ref {
  29690     const mod = sema.mod;
  29691     const inst_ty = sema.typeOf(inst);
  29692     if (try sema.resolveMaybeUndefVal(inst)) |val| {
  29693         if (!val.isUndef(mod) and val.isNull(mod) and !dest_ty.isAllowzeroPtr(mod)) {
  29694             return sema.fail(block, inst_src, "null pointer casted to type '{}'", .{dest_ty.fmt(sema.mod)});
  29695         }
  29696         // The comptime Value representation is compatible with both types.
  29697         return sema.addConstant(
  29698             try mod.getCoerced((try val.intern(inst_ty, mod)).toValue(), dest_ty),
  29699         );
  29700     }
  29701     try sema.requireRuntimeBlock(block, inst_src, null);
  29702     const inst_allows_zero = inst_ty.zigTypeTag(mod) != .Pointer or inst_ty.ptrAllowsZero(mod);
  29703     if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(mod) and
  29704         (try sema.typeHasRuntimeBits(dest_ty.elemType2(mod)) or dest_ty.elemType2(mod).zigTypeTag(mod) == .Fn))
  29705     {
  29706         const actual_ptr = if (inst_ty.isSlice(mod))
  29707             try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty)
  29708         else
  29709             inst;
  29710         const ptr_int = try block.addUnOp(.int_from_ptr, actual_ptr);
  29711         const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
  29712         const ok = if (inst_ty.isSlice(mod)) ok: {
  29713             const len = try sema.analyzeSliceLen(block, inst_src, inst);
  29714             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
  29715             break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero);
  29716         } else is_non_zero;
  29717         try sema.addSafetyCheck(block, ok, .cast_to_null);
  29718     }
  29719     return sema.bitCast(block, dest_ty, inst, inst_src, null);
  29720 }
  29721 
  29722 fn coerceEnumToUnion(
  29723     sema: *Sema,
  29724     block: *Block,
  29725     union_ty: Type,
  29726     union_ty_src: LazySrcLoc,
  29727     inst: Air.Inst.Ref,
  29728     inst_src: LazySrcLoc,
  29729 ) !Air.Inst.Ref {
  29730     const mod = sema.mod;
  29731     const ip = &mod.intern_pool;
  29732     const inst_ty = sema.typeOf(inst);
  29733 
  29734     const tag_ty = union_ty.unionTagType(mod) orelse {
  29735         const msg = msg: {
  29736             const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
  29737                 union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
  29738             });
  29739             errdefer msg.destroy(sema.gpa);
  29740             try sema.errNote(block, union_ty_src, msg, "cannot coerce enum to untagged union", .{});
  29741             try sema.addDeclaredHereNote(msg, union_ty);
  29742             break :msg msg;
  29743         };
  29744         return sema.failWithOwnedErrorMsg(msg);
  29745     };
  29746 
  29747     const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
  29748     if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| {
  29749         const field_index = union_ty.unionTagFieldIndex(val, sema.mod) orelse {
  29750             const msg = msg: {
  29751                 const msg = try sema.errMsg(block, inst_src, "union '{}' has no tag with value '{}'", .{
  29752                     union_ty.fmt(sema.mod), val.fmtValue(tag_ty, sema.mod),
  29753                 });
  29754                 errdefer msg.destroy(sema.gpa);
  29755                 try sema.addDeclaredHereNote(msg, union_ty);
  29756                 break :msg msg;
  29757             };
  29758             return sema.failWithOwnedErrorMsg(msg);
  29759         };
  29760 
  29761         const union_obj = mod.typeToUnion(union_ty).?;
  29762         const field = union_obj.fields.values()[field_index];
  29763         const field_ty = try sema.resolveTypeFields(field.ty);
  29764         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  29765             const msg = msg: {
  29766                 const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{});
  29767                 errdefer msg.destroy(sema.gpa);
  29768 
  29769                 const field_name = union_obj.fields.keys()[field_index];
  29770                 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{
  29771                     field_name.fmt(ip),
  29772                 });
  29773                 try sema.addDeclaredHereNote(msg, union_ty);
  29774                 break :msg msg;
  29775             };
  29776             return sema.failWithOwnedErrorMsg(msg);
  29777         }
  29778         const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse {
  29779             const msg = msg: {
  29780                 const field_name = union_obj.fields.keys()[field_index];
  29781                 const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{}'", .{
  29782                     inst_ty.fmt(sema.mod),  union_ty.fmt(sema.mod),
  29783                     field_ty.fmt(sema.mod), field_name.fmt(ip),
  29784                 });
  29785                 errdefer msg.destroy(sema.gpa);
  29786 
  29787                 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{
  29788                     field_name.fmt(ip),
  29789                 });
  29790                 try sema.addDeclaredHereNote(msg, union_ty);
  29791                 break :msg msg;
  29792             };
  29793             return sema.failWithOwnedErrorMsg(msg);
  29794         };
  29795 
  29796         return sema.addConstant(try mod.unionValue(union_ty, val, opv));
  29797     }
  29798 
  29799     try sema.requireRuntimeBlock(block, inst_src, null);
  29800 
  29801     if (tag_ty.isNonexhaustiveEnum(mod)) {
  29802         const msg = msg: {
  29803             const msg = try sema.errMsg(block, inst_src, "runtime coercion to union '{}' from non-exhaustive enum", .{
  29804                 union_ty.fmt(sema.mod),
  29805             });
  29806             errdefer msg.destroy(sema.gpa);
  29807             try sema.addDeclaredHereNote(msg, tag_ty);
  29808             break :msg msg;
  29809         };
  29810         return sema.failWithOwnedErrorMsg(msg);
  29811     }
  29812 
  29813     const union_obj = mod.typeToUnion(union_ty).?;
  29814     {
  29815         var msg: ?*Module.ErrorMsg = null;
  29816         errdefer if (msg) |some| some.destroy(sema.gpa);
  29817 
  29818         for (union_obj.fields.values(), 0..) |field, i| {
  29819             if (field.ty.zigTypeTag(mod) == .NoReturn) {
  29820                 const err_msg = msg orelse try sema.errMsg(
  29821                     block,
  29822                     inst_src,
  29823                     "runtime coercion from enum '{}' to union '{}' which has a 'noreturn' field",
  29824                     .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) },
  29825                 );
  29826                 msg = err_msg;
  29827 
  29828                 try sema.addFieldErrNote(union_ty, i, err_msg, "'noreturn' field here", .{});
  29829             }
  29830         }
  29831         if (msg) |some| {
  29832             msg = null;
  29833             try sema.addDeclaredHereNote(some, union_ty);
  29834             return sema.failWithOwnedErrorMsg(some);
  29835         }
  29836     }
  29837 
  29838     // If the union has all fields 0 bits, the union value is just the enum value.
  29839     if (union_ty.unionHasAllZeroBitFieldTypes(mod)) {
  29840         return block.addBitCast(union_ty, enum_tag);
  29841     }
  29842 
  29843     const msg = msg: {
  29844         const msg = try sema.errMsg(
  29845             block,
  29846             inst_src,
  29847             "runtime coercion from enum '{}' to union '{}' which has non-void fields",
  29848             .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) },
  29849         );
  29850         errdefer msg.destroy(sema.gpa);
  29851 
  29852         var it = union_obj.fields.iterator();
  29853         var field_index: usize = 0;
  29854         while (it.next()) |field| : (field_index += 1) {
  29855             const field_name = field.key_ptr.*;
  29856             const field_ty = field.value_ptr.ty;
  29857             if (!(try sema.typeHasRuntimeBits(field_ty))) continue;
  29858             try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' has type '{}'", .{
  29859                 field_name.fmt(ip),
  29860                 field_ty.fmt(sema.mod),
  29861             });
  29862         }
  29863         try sema.addDeclaredHereNote(msg, union_ty);
  29864         break :msg msg;
  29865     };
  29866     return sema.failWithOwnedErrorMsg(msg);
  29867 }
  29868 
  29869 fn coerceAnonStructToUnion(
  29870     sema: *Sema,
  29871     block: *Block,
  29872     union_ty: Type,
  29873     union_ty_src: LazySrcLoc,
  29874     inst: Air.Inst.Ref,
  29875     inst_src: LazySrcLoc,
  29876 ) !Air.Inst.Ref {
  29877     const mod = sema.mod;
  29878     const inst_ty = sema.typeOf(inst);
  29879     const field_info: union(enum) {
  29880         name: InternPool.NullTerminatedString,
  29881         count: usize,
  29882     } = switch (mod.intern_pool.indexToKey(inst_ty.toIntern())) {
  29883         .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 1)
  29884             .{ .name = anon_struct_type.names[0] }
  29885         else
  29886             .{ .count = anon_struct_type.names.len },
  29887         .struct_type => |struct_type| name: {
  29888             const field_names = mod.structPtrUnwrap(struct_type.index).?.fields.keys();
  29889             break :name if (field_names.len == 1)
  29890                 .{ .name = field_names[0] }
  29891             else
  29892                 .{ .count = field_names.len };
  29893         },
  29894         else => unreachable,
  29895     };
  29896     switch (field_info) {
  29897         .name => |field_name| {
  29898             const init = try sema.structFieldVal(block, inst_src, inst, field_name, inst_src, inst_ty);
  29899             return sema.unionInit(block, init, inst_src, union_ty, union_ty_src, field_name, inst_src);
  29900         },
  29901         .count => |field_count| {
  29902             assert(field_count != 1);
  29903             const msg = msg: {
  29904                 const msg = if (field_count > 1) try sema.errMsg(
  29905                     block,
  29906                     inst_src,
  29907                     "cannot initialize multiple union fields at once; unions can only have one active field",
  29908                     .{},
  29909                 ) else try sema.errMsg(
  29910                     block,
  29911                     inst_src,
  29912                     "union initializer must initialize one field",
  29913                     .{},
  29914                 );
  29915                 errdefer msg.destroy(sema.gpa);
  29916 
  29917                 // TODO add notes for where the anon struct was created to point out
  29918                 // the extra fields.
  29919 
  29920                 try sema.addDeclaredHereNote(msg, union_ty);
  29921                 break :msg msg;
  29922             };
  29923             return sema.failWithOwnedErrorMsg(msg);
  29924         },
  29925     }
  29926 }
  29927 
  29928 fn coerceAnonStructToUnionPtrs(
  29929     sema: *Sema,
  29930     block: *Block,
  29931     ptr_union_ty: Type,
  29932     union_ty_src: LazySrcLoc,
  29933     ptr_anon_struct: Air.Inst.Ref,
  29934     anon_struct_src: LazySrcLoc,
  29935 ) !Air.Inst.Ref {
  29936     const mod = sema.mod;
  29937     const union_ty = ptr_union_ty.childType(mod);
  29938     const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src);
  29939     const union_inst = try sema.coerceAnonStructToUnion(block, union_ty, union_ty_src, anon_struct, anon_struct_src);
  29940     return sema.analyzeRef(block, union_ty_src, union_inst);
  29941 }
  29942 
  29943 fn coerceAnonStructToStructPtrs(
  29944     sema: *Sema,
  29945     block: *Block,
  29946     ptr_struct_ty: Type,
  29947     struct_ty_src: LazySrcLoc,
  29948     ptr_anon_struct: Air.Inst.Ref,
  29949     anon_struct_src: LazySrcLoc,
  29950 ) !Air.Inst.Ref {
  29951     const mod = sema.mod;
  29952     const struct_ty = ptr_struct_ty.childType(mod);
  29953     const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src);
  29954     const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, anon_struct, anon_struct_src);
  29955     return sema.analyzeRef(block, struct_ty_src, struct_inst);
  29956 }
  29957 
  29958 /// If the lengths match, coerces element-wise.
  29959 fn coerceArrayLike(
  29960     sema: *Sema,
  29961     block: *Block,
  29962     dest_ty: Type,
  29963     dest_ty_src: LazySrcLoc,
  29964     inst: Air.Inst.Ref,
  29965     inst_src: LazySrcLoc,
  29966 ) !Air.Inst.Ref {
  29967     const mod = sema.mod;
  29968     const inst_ty = sema.typeOf(inst);
  29969     const inst_len = inst_ty.arrayLen(mod);
  29970     const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod));
  29971     const target = mod.getTarget();
  29972 
  29973     if (dest_len != inst_len) {
  29974         const msg = msg: {
  29975             const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
  29976                 dest_ty.fmt(mod), inst_ty.fmt(mod),
  29977             });
  29978             errdefer msg.destroy(sema.gpa);
  29979             try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
  29980             try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
  29981             break :msg msg;
  29982         };
  29983         return sema.failWithOwnedErrorMsg(msg);
  29984     }
  29985 
  29986     const dest_elem_ty = dest_ty.childType(mod);
  29987     const inst_elem_ty = inst_ty.childType(mod);
  29988     const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src);
  29989     if (in_memory_result == .ok) {
  29990         if (try sema.resolveMaybeUndefVal(inst)) |inst_val| {
  29991             // These types share the same comptime value representation.
  29992             return sema.coerceInMemory(inst_val, dest_ty);
  29993         }
  29994         try sema.requireRuntimeBlock(block, inst_src, null);
  29995         return block.addBitCast(dest_ty, inst);
  29996     }
  29997 
  29998     const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
  29999     const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
  30000     var runtime_src: ?LazySrcLoc = null;
  30001 
  30002     for (element_vals, element_refs, 0..) |*val, *ref, i| {
  30003         const index_ref = try sema.addConstant(try mod.intValue(Type.usize, i));
  30004         const src = inst_src; // TODO better source location
  30005         const elem_src = inst_src; // TODO better source location
  30006         const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true);
  30007         const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
  30008         ref.* = coerced;
  30009         if (runtime_src == null) {
  30010             if (try sema.resolveMaybeUndefVal(coerced)) |elem_val| {
  30011                 val.* = try elem_val.intern(dest_elem_ty, mod);
  30012             } else {
  30013                 runtime_src = elem_src;
  30014             }
  30015         }
  30016     }
  30017 
  30018     if (runtime_src) |rs| {
  30019         try sema.requireRuntimeBlock(block, inst_src, rs);
  30020         return block.addAggregateInit(dest_ty, element_refs);
  30021     }
  30022 
  30023     return sema.addConstant((try mod.intern(.{ .aggregate = .{
  30024         .ty = dest_ty.toIntern(),
  30025         .storage = .{ .elems = element_vals },
  30026     } })).toValue());
  30027 }
  30028 
  30029 /// If the lengths match, coerces element-wise.
  30030 fn coerceTupleToArray(
  30031     sema: *Sema,
  30032     block: *Block,
  30033     dest_ty: Type,
  30034     dest_ty_src: LazySrcLoc,
  30035     inst: Air.Inst.Ref,
  30036     inst_src: LazySrcLoc,
  30037 ) !Air.Inst.Ref {
  30038     const mod = sema.mod;
  30039     const inst_ty = sema.typeOf(inst);
  30040     const inst_len = inst_ty.arrayLen(mod);
  30041     const dest_len = dest_ty.arrayLen(mod);
  30042 
  30043     if (dest_len != inst_len) {
  30044         const msg = msg: {
  30045             const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
  30046                 dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
  30047             });
  30048             errdefer msg.destroy(sema.gpa);
  30049             try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
  30050             try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
  30051             break :msg msg;
  30052         };
  30053         return sema.failWithOwnedErrorMsg(msg);
  30054     }
  30055 
  30056     const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len);
  30057     const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems);
  30058     const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);
  30059     const dest_elem_ty = dest_ty.childType(mod);
  30060 
  30061     var runtime_src: ?LazySrcLoc = null;
  30062     for (element_vals, element_refs, 0..) |*val, *ref, i_usize| {
  30063         const i = @as(u32, @intCast(i_usize));
  30064         if (i_usize == inst_len) {
  30065             const sentinel_val = dest_ty.sentinel(mod).?;
  30066             val.* = sentinel_val.toIntern();
  30067             ref.* = try sema.addConstant(sentinel_val);
  30068             break;
  30069         }
  30070         const elem_src = inst_src; // TODO better source location
  30071         const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i);
  30072         const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
  30073         ref.* = coerced;
  30074         if (runtime_src == null) {
  30075             if (try sema.resolveMaybeUndefVal(coerced)) |elem_val| {
  30076                 val.* = try elem_val.intern(dest_elem_ty, mod);
  30077             } else {
  30078                 runtime_src = elem_src;
  30079             }
  30080         }
  30081     }
  30082 
  30083     if (runtime_src) |rs| {
  30084         try sema.requireRuntimeBlock(block, inst_src, rs);
  30085         return block.addAggregateInit(dest_ty, element_refs);
  30086     }
  30087 
  30088     return sema.addConstant((try mod.intern(.{ .aggregate = .{
  30089         .ty = dest_ty.toIntern(),
  30090         .storage = .{ .elems = element_vals },
  30091     } })).toValue());
  30092 }
  30093 
  30094 /// If the lengths match, coerces element-wise.
  30095 fn coerceTupleToSlicePtrs(
  30096     sema: *Sema,
  30097     block: *Block,
  30098     slice_ty: Type,
  30099     slice_ty_src: LazySrcLoc,
  30100     ptr_tuple: Air.Inst.Ref,
  30101     tuple_src: LazySrcLoc,
  30102 ) !Air.Inst.Ref {
  30103     const mod = sema.mod;
  30104     const tuple_ty = sema.typeOf(ptr_tuple).childType(mod);
  30105     const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
  30106     const slice_info = slice_ty.ptrInfo(mod);
  30107     const array_ty = try mod.arrayType(.{
  30108         .len = tuple_ty.structFieldCount(mod),
  30109         .sentinel = slice_info.sentinel,
  30110         .child = slice_info.child,
  30111     });
  30112     const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src);
  30113     if (slice_info.flags.alignment != .none) {
  30114         return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{});
  30115     }
  30116     const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst);
  30117     return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);
  30118 }
  30119 
  30120 /// If the lengths match, coerces element-wise.
  30121 fn coerceTupleToArrayPtrs(
  30122     sema: *Sema,
  30123     block: *Block,
  30124     ptr_array_ty: Type,
  30125     array_ty_src: LazySrcLoc,
  30126     ptr_tuple: Air.Inst.Ref,
  30127     tuple_src: LazySrcLoc,
  30128 ) !Air.Inst.Ref {
  30129     const mod = sema.mod;
  30130     const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
  30131     const ptr_info = ptr_array_ty.ptrInfo(mod);
  30132     const array_ty = ptr_info.child.toType();
  30133     const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src);
  30134     if (ptr_info.flags.alignment != .none) {
  30135         return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{});
  30136     }
  30137     const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst);
  30138     return ptr_array;
  30139 }
  30140 
  30141 /// Handles both tuples and anon struct literals. Coerces field-wise. Reports
  30142 /// errors for both extra fields and missing fields.
  30143 fn coerceTupleToStruct(
  30144     sema: *Sema,
  30145     block: *Block,
  30146     dest_ty: Type,
  30147     inst: Air.Inst.Ref,
  30148     inst_src: LazySrcLoc,
  30149 ) !Air.Inst.Ref {
  30150     const mod = sema.mod;
  30151     const ip = &mod.intern_pool;
  30152     const struct_ty = try sema.resolveTypeFields(dest_ty);
  30153 
  30154     if (struct_ty.isTupleOrAnonStruct(mod)) {
  30155         return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src);
  30156     }
  30157 
  30158     const fields = struct_ty.structFields(mod);
  30159     const field_vals = try sema.arena.alloc(InternPool.Index, fields.count());
  30160     const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
  30161     @memset(field_refs, .none);
  30162 
  30163     const inst_ty = sema.typeOf(inst);
  30164     var runtime_src: ?LazySrcLoc = null;
  30165     const field_count = switch (ip.indexToKey(inst_ty.toIntern())) {
  30166         .anon_struct_type => |anon_struct_type| anon_struct_type.types.len,
  30167         .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj|
  30168             struct_obj.fields.count()
  30169         else
  30170             0,
  30171         else => unreachable,
  30172     };
  30173     for (0..field_count) |field_index_usize| {
  30174         const field_i = @as(u32, @intCast(field_index_usize));
  30175         const field_src = inst_src; // TODO better source location
  30176         // https://github.com/ziglang/zig/issues/15709
  30177         const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) {
  30178             .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0)
  30179                 anon_struct_type.names[field_i]
  30180             else
  30181                 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}),
  30182             .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i],
  30183             else => unreachable,
  30184         };
  30185         const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);
  30186         const field = fields.values()[field_index];
  30187         const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
  30188         const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);
  30189         field_refs[field_index] = coerced;
  30190         if (field.is_comptime) {
  30191             const init_val = (try sema.resolveMaybeUndefVal(coerced)) orelse {
  30192                 return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known");
  30193             };
  30194 
  30195             if (!init_val.eql(field.default_val.toValue(), field.ty, sema.mod)) {
  30196                 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i);
  30197             }
  30198         }
  30199         if (runtime_src == null) {
  30200             if (try sema.resolveMaybeUndefVal(coerced)) |field_val| {
  30201                 field_vals[field_index] = field_val.toIntern();
  30202             } else {
  30203                 runtime_src = field_src;
  30204             }
  30205         }
  30206     }
  30207 
  30208     // Populate default field values and report errors for missing fields.
  30209     var root_msg: ?*Module.ErrorMsg = null;
  30210     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  30211 
  30212     for (field_refs, 0..) |*field_ref, i| {
  30213         if (field_ref.* != .none) continue;
  30214 
  30215         const field_name = fields.keys()[i];
  30216         const field = fields.values()[i];
  30217         const field_src = inst_src; // TODO better source location
  30218         if (field.default_val == .none) {
  30219             const template = "missing struct field: {}";
  30220             const args = .{field_name.fmt(ip)};
  30221             if (root_msg) |msg| {
  30222                 try sema.errNote(block, field_src, msg, template, args);
  30223             } else {
  30224                 root_msg = try sema.errMsg(block, field_src, template, args);
  30225             }
  30226             continue;
  30227         }
  30228         if (runtime_src == null) {
  30229             field_vals[i] = field.default_val;
  30230         } else {
  30231             field_ref.* = try sema.addConstant(field.default_val.toValue());
  30232         }
  30233     }
  30234 
  30235     if (root_msg) |msg| {
  30236         try sema.addDeclaredHereNote(msg, struct_ty);
  30237         root_msg = null;
  30238         return sema.failWithOwnedErrorMsg(msg);
  30239     }
  30240 
  30241     if (runtime_src) |rs| {
  30242         try sema.requireRuntimeBlock(block, inst_src, rs);
  30243         return block.addAggregateInit(struct_ty, field_refs);
  30244     }
  30245 
  30246     const struct_val = try mod.intern(.{ .aggregate = .{
  30247         .ty = struct_ty.toIntern(),
  30248         .storage = .{ .elems = field_vals },
  30249     } });
  30250     // TODO: figure out InternPool removals for incremental compilation
  30251     //errdefer ip.remove(struct_val);
  30252 
  30253     return sema.addConstant(struct_val.toValue());
  30254 }
  30255 
  30256 fn coerceTupleToTuple(
  30257     sema: *Sema,
  30258     block: *Block,
  30259     tuple_ty: Type,
  30260     inst: Air.Inst.Ref,
  30261     inst_src: LazySrcLoc,
  30262 ) !Air.Inst.Ref {
  30263     const mod = sema.mod;
  30264     const ip = &mod.intern_pool;
  30265     const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) {
  30266         .anon_struct_type => |anon_struct_type| anon_struct_type.types.len,
  30267         .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj|
  30268             struct_obj.fields.count()
  30269         else
  30270             0,
  30271         else => unreachable,
  30272     };
  30273     const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count);
  30274     const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
  30275     @memset(field_refs, .none);
  30276 
  30277     const inst_ty = sema.typeOf(inst);
  30278     const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) {
  30279         .anon_struct_type => |anon_struct_type| anon_struct_type.types.len,
  30280         .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj|
  30281             struct_obj.fields.count()
  30282         else
  30283             0,
  30284         else => unreachable,
  30285     };
  30286     if (src_field_count > dest_field_count) return error.NotCoercible;
  30287 
  30288     var runtime_src: ?LazySrcLoc = null;
  30289     for (0..dest_field_count) |field_index_usize| {
  30290         const field_i = @as(u32, @intCast(field_index_usize));
  30291         const field_src = inst_src; // TODO better source location
  30292         // https://github.com/ziglang/zig/issues/15709
  30293         const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) {
  30294             .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0)
  30295                 anon_struct_type.names[field_i]
  30296             else
  30297                 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}),
  30298             .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i],
  30299             else => unreachable,
  30300         };
  30301 
  30302         if (ip.stringEqlSlice(field_name, "len"))
  30303             return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{});
  30304 
  30305         const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) {
  30306             .anon_struct_type => |anon_struct_type| anon_struct_type.types[field_index_usize].toType(),
  30307             .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].ty,
  30308             else => unreachable,
  30309         };
  30310         const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
  30311             .anon_struct_type => |anon_struct_type| anon_struct_type.values[field_index_usize],
  30312             .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].default_val,
  30313             else => unreachable,
  30314         };
  30315 
  30316         const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src);
  30317 
  30318         const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
  30319         const coerced = try sema.coerce(block, field_ty, elem_ref, field_src);
  30320         field_refs[field_index] = coerced;
  30321         if (default_val != .none) {
  30322             const init_val = (try sema.resolveMaybeUndefVal(coerced)) orelse {
  30323                 return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known");
  30324             };
  30325 
  30326             if (!init_val.eql(default_val.toValue(), field_ty, sema.mod)) {
  30327                 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i);
  30328             }
  30329         }
  30330         if (runtime_src == null) {
  30331             if (try sema.resolveMaybeUndefVal(coerced)) |field_val| {
  30332                 field_vals[field_index] = field_val.toIntern();
  30333             } else {
  30334                 runtime_src = field_src;
  30335             }
  30336         }
  30337     }
  30338 
  30339     // Populate default field values and report errors for missing fields.
  30340     var root_msg: ?*Module.ErrorMsg = null;
  30341     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  30342 
  30343     for (field_refs, 0..) |*field_ref, i| {
  30344         if (field_ref.* != .none) continue;
  30345 
  30346         const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
  30347             .anon_struct_type => |anon_struct_type| anon_struct_type.values[i],
  30348             .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[i].default_val,
  30349             else => unreachable,
  30350         };
  30351 
  30352         const field_src = inst_src; // TODO better source location
  30353         if (default_val == .none) {
  30354             if (tuple_ty.isTuple(mod)) {
  30355                 const template = "missing tuple field: {d}";
  30356                 if (root_msg) |msg| {
  30357                     try sema.errNote(block, field_src, msg, template, .{i});
  30358                 } else {
  30359                     root_msg = try sema.errMsg(block, field_src, template, .{i});
  30360                 }
  30361                 continue;
  30362             }
  30363             const template = "missing struct field: {}";
  30364             const args = .{tuple_ty.structFieldName(i, mod).fmt(ip)};
  30365             if (root_msg) |msg| {
  30366                 try sema.errNote(block, field_src, msg, template, args);
  30367             } else {
  30368                 root_msg = try sema.errMsg(block, field_src, template, args);
  30369             }
  30370             continue;
  30371         }
  30372         if (runtime_src == null) {
  30373             field_vals[i] = default_val;
  30374         } else {
  30375             field_ref.* = try sema.addConstant(default_val.toValue());
  30376         }
  30377     }
  30378 
  30379     if (root_msg) |msg| {
  30380         try sema.addDeclaredHereNote(msg, tuple_ty);
  30381         root_msg = null;
  30382         return sema.failWithOwnedErrorMsg(msg);
  30383     }
  30384 
  30385     if (runtime_src) |rs| {
  30386         try sema.requireRuntimeBlock(block, inst_src, rs);
  30387         return block.addAggregateInit(tuple_ty, field_refs);
  30388     }
  30389 
  30390     return sema.addConstant(
  30391         (try mod.intern(.{ .aggregate = .{
  30392             .ty = tuple_ty.toIntern(),
  30393             .storage = .{ .elems = field_vals },
  30394         } })).toValue(),
  30395     );
  30396 }
  30397 
  30398 fn analyzeDeclVal(
  30399     sema: *Sema,
  30400     block: *Block,
  30401     src: LazySrcLoc,
  30402     decl_index: Decl.Index,
  30403 ) CompileError!Air.Inst.Ref {
  30404     try sema.addReferencedBy(block, src, decl_index);
  30405     if (sema.decl_val_table.get(decl_index)) |result| {
  30406         return result;
  30407     }
  30408     const decl_ref = try sema.analyzeDeclRefInner(decl_index, false);
  30409     const result = try sema.analyzeLoad(block, src, decl_ref, src);
  30410     if (Air.refToInterned(result) != null) {
  30411         if (!block.is_typeof) {
  30412             try sema.decl_val_table.put(sema.gpa, decl_index, result);
  30413         }
  30414     }
  30415     return result;
  30416 }
  30417 
  30418 fn addReferencedBy(
  30419     sema: *Sema,
  30420     block: *Block,
  30421     src: LazySrcLoc,
  30422     decl_index: Decl.Index,
  30423 ) !void {
  30424     if (sema.mod.comp.reference_trace == @as(u32, 0)) return;
  30425     try sema.mod.reference_table.put(sema.gpa, decl_index, .{
  30426         .referencer = block.src_decl,
  30427         .src = src,
  30428     });
  30429 }
  30430 
  30431 fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void {
  30432     const mod = sema.mod;
  30433     const decl = mod.declPtr(decl_index);
  30434     if (decl.analysis == .in_progress) {
  30435         const msg = try Module.ErrorMsg.create(sema.gpa, decl.srcLoc(mod), "dependency loop detected", .{});
  30436         return sema.failWithOwnedErrorMsg(msg);
  30437     }
  30438 
  30439     mod.ensureDeclAnalyzed(decl_index) catch |err| {
  30440         if (sema.owner_func) |owner_func| {
  30441             owner_func.state = .dependency_failure;
  30442         } else {
  30443             sema.owner_decl.analysis = .dependency_failure;
  30444         }
  30445         return err;
  30446     };
  30447 }
  30448 
  30449 fn ensureFuncBodyAnalyzed(sema: *Sema, func: Module.Fn.Index) CompileError!void {
  30450     sema.mod.ensureFuncBodyAnalyzed(func) catch |err| {
  30451         if (sema.owner_func) |owner_func| {
  30452             owner_func.state = .dependency_failure;
  30453         } else {
  30454             sema.owner_decl.analysis = .dependency_failure;
  30455         }
  30456         return err;
  30457     };
  30458 }
  30459 
  30460 fn refValue(sema: *Sema, block: *Block, ty: Type, val: Value) !Value {
  30461     const mod = sema.mod;
  30462     var anon_decl = try block.startAnonDecl();
  30463     defer anon_decl.deinit();
  30464     const decl = try anon_decl.finish(
  30465         ty,
  30466         val,
  30467         .none, // default alignment
  30468     );
  30469     try sema.maybeQueueFuncBodyAnalysis(decl);
  30470     try mod.declareDeclDependency(sema.owner_decl_index, decl);
  30471     const result = try mod.intern(.{ .ptr = .{
  30472         .ty = (try mod.singleConstPtrType(ty)).toIntern(),
  30473         .addr = .{ .decl = decl },
  30474     } });
  30475     return result.toValue();
  30476 }
  30477 
  30478 fn optRefValue(sema: *Sema, block: *Block, ty: Type, opt_val: ?Value) !Value {
  30479     const mod = sema.mod;
  30480     const ptr_anyopaque_ty = try mod.singleConstPtrType(Type.anyopaque);
  30481     return (try mod.intern(.{ .opt = .{
  30482         .ty = (try mod.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(),
  30483         .val = if (opt_val) |val| (try mod.getCoerced(
  30484             try sema.refValue(block, ty, val),
  30485             ptr_anyopaque_ty,
  30486         )).toIntern() else .none,
  30487     } })).toValue();
  30488 }
  30489 
  30490 fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref {
  30491     return sema.analyzeDeclRefInner(decl_index, true);
  30492 }
  30493 
  30494 /// Analyze a reference to the decl at the given index. Ensures the underlying decl is analyzed, but
  30495 /// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a
  30496 /// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeDeclRef` wraps
  30497 /// this function with `analyze_fn_body` set to true.
  30498 fn analyzeDeclRefInner(sema: *Sema, decl_index: Decl.Index, analyze_fn_body: bool) CompileError!Air.Inst.Ref {
  30499     const mod = sema.mod;
  30500     try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
  30501     try sema.ensureDeclAnalyzed(decl_index);
  30502 
  30503     const decl = mod.declPtr(decl_index);
  30504     const decl_tv = try decl.typedValue();
  30505     const ptr_ty = try mod.ptrType(.{
  30506         .child = decl_tv.ty.toIntern(),
  30507         .flags = .{
  30508             .alignment = decl.alignment,
  30509             .is_const = if (decl.val.getVariable(mod)) |variable| variable.is_const else true,
  30510             .address_space = decl.@"addrspace",
  30511         },
  30512     });
  30513     if (analyze_fn_body) {
  30514         try sema.maybeQueueFuncBodyAnalysis(decl_index);
  30515     }
  30516     return sema.addConstant((try mod.intern(.{ .ptr = .{
  30517         .ty = ptr_ty.toIntern(),
  30518         .addr = .{ .decl = decl_index },
  30519     } })).toValue());
  30520 }
  30521 
  30522 fn maybeQueueFuncBodyAnalysis(sema: *Sema, decl_index: Decl.Index) !void {
  30523     const mod = sema.mod;
  30524     const decl = mod.declPtr(decl_index);
  30525     const tv = try decl.typedValue();
  30526     if (tv.ty.zigTypeTag(mod) != .Fn) return;
  30527     if (!try sema.fnHasRuntimeBits(tv.ty)) return;
  30528     const func_index = mod.intern_pool.indexToFunc(tv.val.toIntern()).unwrap() orelse return; // undef or extern_fn
  30529     try mod.ensureFuncBodyAnalysisQueued(func_index);
  30530 }
  30531 
  30532 fn analyzeRef(
  30533     sema: *Sema,
  30534     block: *Block,
  30535     src: LazySrcLoc,
  30536     operand: Air.Inst.Ref,
  30537 ) CompileError!Air.Inst.Ref {
  30538     const mod = sema.mod;
  30539     const operand_ty = sema.typeOf(operand);
  30540 
  30541     if (try sema.resolveMaybeUndefVal(operand)) |val| {
  30542         switch (mod.intern_pool.indexToKey(val.toIntern())) {
  30543             .extern_func => |extern_func| return sema.analyzeDeclRef(extern_func.decl),
  30544             .func => |func| return sema.analyzeDeclRef(mod.funcPtr(func.index).owner_decl),
  30545             else => {},
  30546         }
  30547         var anon_decl = try block.startAnonDecl();
  30548         defer anon_decl.deinit();
  30549         return sema.analyzeDeclRef(try anon_decl.finish(
  30550             operand_ty,
  30551             val,
  30552             .none, // default alignment
  30553         ));
  30554     }
  30555 
  30556     try sema.requireRuntimeBlock(block, src, null);
  30557     const address_space = target_util.defaultAddressSpace(mod.getTarget(), .local);
  30558     const ptr_type = try mod.ptrType(.{
  30559         .child = operand_ty.toIntern(),
  30560         .flags = .{
  30561             .is_const = true,
  30562             .address_space = address_space,
  30563         },
  30564     });
  30565     const mut_ptr_type = try mod.ptrType(.{
  30566         .child = operand_ty.toIntern(),
  30567         .flags = .{ .address_space = address_space },
  30568     });
  30569     const alloc = try block.addTy(.alloc, mut_ptr_type);
  30570     try sema.storePtr(block, src, alloc, operand);
  30571 
  30572     // TODO: Replace with sema.coerce when that supports adding pointer constness.
  30573     return sema.bitCast(block, ptr_type, alloc, src, null);
  30574 }
  30575 
  30576 fn analyzeLoad(
  30577     sema: *Sema,
  30578     block: *Block,
  30579     src: LazySrcLoc,
  30580     ptr: Air.Inst.Ref,
  30581     ptr_src: LazySrcLoc,
  30582 ) CompileError!Air.Inst.Ref {
  30583     const mod = sema.mod;
  30584     const ptr_ty = sema.typeOf(ptr);
  30585     const elem_ty = switch (ptr_ty.zigTypeTag(mod)) {
  30586         .Pointer => ptr_ty.childType(mod),
  30587         else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}),
  30588     };
  30589 
  30590     if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| {
  30591         return sema.addConstant(opv);
  30592     }
  30593 
  30594     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
  30595         if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| {
  30596             return sema.addConstant(elem_val);
  30597         }
  30598     }
  30599 
  30600     if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) {
  30601         const ptr_inst = Air.refToIndex(ptr).?;
  30602         const air_tags = sema.air_instructions.items(.tag);
  30603         if (air_tags[ptr_inst] == .ptr_elem_ptr) {
  30604             const ty_pl = sema.air_instructions.items(.data)[ptr_inst].ty_pl;
  30605             const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data;
  30606             return block.addBinOp(.ptr_elem_val, bin_op.lhs, bin_op.rhs);
  30607         }
  30608         return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{
  30609             ptr_ty.fmt(sema.mod),
  30610         });
  30611     }
  30612 
  30613     return block.addTyOp(.load, elem_ty, ptr);
  30614 }
  30615 
  30616 fn analyzeSlicePtr(
  30617     sema: *Sema,
  30618     block: *Block,
  30619     slice_src: LazySrcLoc,
  30620     slice: Air.Inst.Ref,
  30621     slice_ty: Type,
  30622 ) CompileError!Air.Inst.Ref {
  30623     const mod = sema.mod;
  30624     const result_ty = slice_ty.slicePtrFieldType(mod);
  30625     if (try sema.resolveMaybeUndefVal(slice)) |val| {
  30626         if (val.isUndef(mod)) return sema.addConstUndef(result_ty);
  30627         return sema.addConstant(val.slicePtr(mod));
  30628     }
  30629     try sema.requireRuntimeBlock(block, slice_src, null);
  30630     return block.addTyOp(.slice_ptr, result_ty, slice);
  30631 }
  30632 
  30633 fn analyzeSliceLen(
  30634     sema: *Sema,
  30635     block: *Block,
  30636     src: LazySrcLoc,
  30637     slice_inst: Air.Inst.Ref,
  30638 ) CompileError!Air.Inst.Ref {
  30639     const mod = sema.mod;
  30640     if (try sema.resolveMaybeUndefVal(slice_inst)) |slice_val| {
  30641         if (slice_val.isUndef(mod)) {
  30642             return sema.addConstUndef(Type.usize);
  30643         }
  30644         return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod));
  30645     }
  30646     try sema.requireRuntimeBlock(block, src, null);
  30647     return block.addTyOp(.slice_len, Type.usize, slice_inst);
  30648 }
  30649 
  30650 fn analyzeIsNull(
  30651     sema: *Sema,
  30652     block: *Block,
  30653     src: LazySrcLoc,
  30654     operand: Air.Inst.Ref,
  30655     invert_logic: bool,
  30656 ) CompileError!Air.Inst.Ref {
  30657     const mod = sema.mod;
  30658     const result_ty = Type.bool;
  30659     if (try sema.resolveMaybeUndefVal(operand)) |opt_val| {
  30660         if (opt_val.isUndef(mod)) {
  30661             return sema.addConstUndef(result_ty);
  30662         }
  30663         const is_null = opt_val.isNull(mod);
  30664         const bool_value = if (invert_logic) !is_null else is_null;
  30665         if (bool_value) {
  30666             return Air.Inst.Ref.bool_true;
  30667         } else {
  30668             return Air.Inst.Ref.bool_false;
  30669         }
  30670     }
  30671 
  30672     const inverted_non_null_res = if (invert_logic) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
  30673     const operand_ty = sema.typeOf(operand);
  30674     if (operand_ty.zigTypeTag(mod) == .Optional and operand_ty.optionalChild(mod).zigTypeTag(mod) == .NoReturn) {
  30675         return inverted_non_null_res;
  30676     }
  30677     if (operand_ty.zigTypeTag(mod) != .Optional and !operand_ty.isPtrLikeOptional(mod)) {
  30678         return inverted_non_null_res;
  30679     }
  30680     try sema.requireRuntimeBlock(block, src, null);
  30681     const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
  30682     return block.addUnOp(air_tag, operand);
  30683 }
  30684 
  30685 fn analyzePtrIsNonErrComptimeOnly(
  30686     sema: *Sema,
  30687     block: *Block,
  30688     src: LazySrcLoc,
  30689     operand: Air.Inst.Ref,
  30690 ) CompileError!Air.Inst.Ref {
  30691     const mod = sema.mod;
  30692     const ptr_ty = sema.typeOf(operand);
  30693     assert(ptr_ty.zigTypeTag(mod) == .Pointer);
  30694     const child_ty = ptr_ty.childType(mod);
  30695 
  30696     const child_tag = child_ty.zigTypeTag(mod);
  30697     if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return Air.Inst.Ref.bool_true;
  30698     if (child_tag == .ErrorSet) return Air.Inst.Ref.bool_false;
  30699     assert(child_tag == .ErrorUnion);
  30700 
  30701     _ = block;
  30702     _ = src;
  30703 
  30704     return Air.Inst.Ref.none;
  30705 }
  30706 
  30707 fn analyzeIsNonErrComptimeOnly(
  30708     sema: *Sema,
  30709     block: *Block,
  30710     src: LazySrcLoc,
  30711     operand: Air.Inst.Ref,
  30712 ) CompileError!Air.Inst.Ref {
  30713     const mod = sema.mod;
  30714     const operand_ty = sema.typeOf(operand);
  30715     const ot = operand_ty.zigTypeTag(mod);
  30716     if (ot != .ErrorSet and ot != .ErrorUnion) return Air.Inst.Ref.bool_true;
  30717     if (ot == .ErrorSet) return Air.Inst.Ref.bool_false;
  30718     assert(ot == .ErrorUnion);
  30719 
  30720     const payload_ty = operand_ty.errorUnionPayload(mod);
  30721     if (payload_ty.zigTypeTag(mod) == .NoReturn) {
  30722         return Air.Inst.Ref.bool_false;
  30723     }
  30724 
  30725     if (Air.refToIndex(operand)) |operand_inst| {
  30726         switch (sema.air_instructions.items(.tag)[operand_inst]) {
  30727             .wrap_errunion_payload => return Air.Inst.Ref.bool_true,
  30728             .wrap_errunion_err => return Air.Inst.Ref.bool_false,
  30729             else => {},
  30730         }
  30731     } else if (operand == .undef) {
  30732         return sema.addConstUndef(Type.bool);
  30733     } else if (@intFromEnum(operand) < InternPool.static_len) {
  30734         // None of the ref tags can be errors.
  30735         return Air.Inst.Ref.bool_true;
  30736     }
  30737 
  30738     const maybe_operand_val = try sema.resolveMaybeUndefVal(operand);
  30739 
  30740     // exception if the error union error set is known to be empty,
  30741     // we allow the comparison but always make it comptime-known.
  30742     const set_ty = operand_ty.errorUnionSet(mod);
  30743     switch (set_ty.toIntern()) {
  30744         .anyerror_type => {},
  30745         else => switch (mod.intern_pool.indexToKey(set_ty.toIntern())) {
  30746             .error_set_type => |error_set_type| {
  30747                 if (error_set_type.names.len == 0) return Air.Inst.Ref.bool_true;
  30748             },
  30749             .inferred_error_set_type => |ies_index| blk: {
  30750                 // If the error set is empty, we must return a comptime true or false.
  30751                 // However we want to avoid unnecessarily resolving an inferred error set
  30752                 // in case it is already non-empty.
  30753                 const ies = mod.inferredErrorSetPtr(ies_index);
  30754                 if (ies.is_anyerror) break :blk;
  30755                 if (ies.errors.count() != 0) break :blk;
  30756                 if (maybe_operand_val == null) {
  30757                     // Try to avoid resolving inferred error set if possible.
  30758                     if (ies.errors.count() != 0) break :blk;
  30759                     if (ies.is_anyerror) break :blk;
  30760                     for (ies.inferred_error_sets.keys()) |other_ies_index| {
  30761                         if (ies_index == other_ies_index) continue;
  30762                         try sema.resolveInferredErrorSet(block, src, other_ies_index);
  30763                         const other_ies = mod.inferredErrorSetPtr(other_ies_index);
  30764                         if (other_ies.is_anyerror) {
  30765                             ies.is_anyerror = true;
  30766                             ies.is_resolved = true;
  30767                             break :blk;
  30768                         }
  30769 
  30770                         if (other_ies.errors.count() != 0) break :blk;
  30771                     }
  30772                     if (ies.func == sema.owner_func_index.unwrap()) {
  30773                         // We're checking the inferred errorset of the current function and none of
  30774                         // its child inferred error sets contained any errors meaning that any value
  30775                         // so far with this type can't contain errors either.
  30776                         return Air.Inst.Ref.bool_true;
  30777                     }
  30778                     try sema.resolveInferredErrorSet(block, src, ies_index);
  30779                     if (ies.is_anyerror) break :blk;
  30780                     if (ies.errors.count() == 0) return Air.Inst.Ref.bool_true;
  30781                 }
  30782             },
  30783             else => unreachable,
  30784         },
  30785     }
  30786 
  30787     if (maybe_operand_val) |err_union| {
  30788         if (err_union.isUndef(mod)) {
  30789             return sema.addConstUndef(Type.bool);
  30790         }
  30791         if (err_union.getErrorName(mod) == .none) {
  30792             return Air.Inst.Ref.bool_true;
  30793         } else {
  30794             return Air.Inst.Ref.bool_false;
  30795         }
  30796     }
  30797     return Air.Inst.Ref.none;
  30798 }
  30799 
  30800 fn analyzeIsNonErr(
  30801     sema: *Sema,
  30802     block: *Block,
  30803     src: LazySrcLoc,
  30804     operand: Air.Inst.Ref,
  30805 ) CompileError!Air.Inst.Ref {
  30806     const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
  30807     if (result == .none) {
  30808         try sema.requireRuntimeBlock(block, src, null);
  30809         return block.addUnOp(.is_non_err, operand);
  30810     } else {
  30811         return result;
  30812     }
  30813 }
  30814 
  30815 fn analyzePtrIsNonErr(
  30816     sema: *Sema,
  30817     block: *Block,
  30818     src: LazySrcLoc,
  30819     operand: Air.Inst.Ref,
  30820 ) CompileError!Air.Inst.Ref {
  30821     const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand);
  30822     if (result == .none) {
  30823         try sema.requireRuntimeBlock(block, src, null);
  30824         return block.addUnOp(.is_non_err_ptr, operand);
  30825     } else {
  30826         return result;
  30827     }
  30828 }
  30829 
  30830 fn analyzeSlice(
  30831     sema: *Sema,
  30832     block: *Block,
  30833     src: LazySrcLoc,
  30834     ptr_ptr: Air.Inst.Ref,
  30835     uncasted_start: Air.Inst.Ref,
  30836     uncasted_end_opt: Air.Inst.Ref,
  30837     sentinel_opt: Air.Inst.Ref,
  30838     sentinel_src: LazySrcLoc,
  30839     ptr_src: LazySrcLoc,
  30840     start_src: LazySrcLoc,
  30841     end_src: LazySrcLoc,
  30842     by_length: bool,
  30843 ) CompileError!Air.Inst.Ref {
  30844     const mod = sema.mod;
  30845     // Slice expressions can operate on a variable whose type is an array. This requires
  30846     // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
  30847     const ptr_ptr_ty = sema.typeOf(ptr_ptr);
  30848     const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(mod)) {
  30849         .Pointer => ptr_ptr_ty.childType(mod),
  30850         else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(mod)}),
  30851     };
  30852 
  30853     var array_ty = ptr_ptr_child_ty;
  30854     var slice_ty = ptr_ptr_ty;
  30855     var ptr_or_slice = ptr_ptr;
  30856     var elem_ty: Type = undefined;
  30857     var ptr_sentinel: ?Value = null;
  30858     switch (ptr_ptr_child_ty.zigTypeTag(mod)) {
  30859         .Array => {
  30860             ptr_sentinel = ptr_ptr_child_ty.sentinel(mod);
  30861             elem_ty = ptr_ptr_child_ty.childType(mod);
  30862         },
  30863         .Pointer => switch (ptr_ptr_child_ty.ptrSize(mod)) {
  30864             .One => {
  30865                 const double_child_ty = ptr_ptr_child_ty.childType(mod);
  30866                 if (double_child_ty.zigTypeTag(mod) == .Array) {
  30867                     ptr_sentinel = double_child_ty.sentinel(mod);
  30868                     ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  30869                     slice_ty = ptr_ptr_child_ty;
  30870                     array_ty = double_child_ty;
  30871                     elem_ty = double_child_ty.childType(mod);
  30872                 } else {
  30873                     return sema.fail(block, src, "slice of single-item pointer", .{});
  30874                 }
  30875             },
  30876             .Many, .C => {
  30877                 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod);
  30878                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  30879                 slice_ty = ptr_ptr_child_ty;
  30880                 array_ty = ptr_ptr_child_ty;
  30881                 elem_ty = ptr_ptr_child_ty.childType(mod);
  30882 
  30883                 if (ptr_ptr_child_ty.ptrSize(mod) == .C) {
  30884                     if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| {
  30885                         if (ptr_val.isNull(mod)) {
  30886                             return sema.fail(block, src, "slice of null pointer", .{});
  30887                         }
  30888                     }
  30889                 }
  30890             },
  30891             .Slice => {
  30892                 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod);
  30893                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  30894                 slice_ty = ptr_ptr_child_ty;
  30895                 array_ty = ptr_ptr_child_ty;
  30896                 elem_ty = ptr_ptr_child_ty.childType(mod);
  30897             },
  30898         },
  30899         else => return sema.fail(block, src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(mod)}),
  30900     }
  30901 
  30902     const ptr = if (slice_ty.isSlice(mod))
  30903         try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty)
  30904     else if (array_ty.zigTypeTag(mod) == .Array) ptr: {
  30905         var manyptr_ty_key = mod.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type;
  30906         assert(manyptr_ty_key.child == array_ty.toIntern());
  30907         assert(manyptr_ty_key.flags.size == .One);
  30908         manyptr_ty_key.child = elem_ty.toIntern();
  30909         manyptr_ty_key.flags.size = .Many;
  30910         break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(manyptr_ty_key), ptr_or_slice, ptr_src);
  30911     } else ptr_or_slice;
  30912 
  30913     const start = try sema.coerce(block, Type.usize, uncasted_start, start_src);
  30914     const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
  30915     const new_ptr_ty = sema.typeOf(new_ptr);
  30916 
  30917     // true if and only if the end index of the slice, implicitly or explicitly, equals
  30918     // the length of the underlying object being sliced. we might learn the length of the
  30919     // underlying object because it is an array (which has the length in the type), or
  30920     // we might learn of the length because it is a comptime-known slice value.
  30921     var end_is_len = uncasted_end_opt == .none;
  30922     const end = e: {
  30923         if (array_ty.zigTypeTag(mod) == .Array) {
  30924             const len_val = try mod.intValue(Type.usize, array_ty.arrayLen(mod));
  30925 
  30926             if (!end_is_len) {
  30927                 const end = if (by_length) end: {
  30928                     const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  30929                     const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  30930                     break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
  30931                 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  30932                 if (try sema.resolveMaybeUndefVal(end)) |end_val| {
  30933                     const len_s_val = try mod.intValue(
  30934                         Type.usize,
  30935                         array_ty.arrayLenIncludingSentinel(mod),
  30936                     );
  30937                     if (!(try sema.compareAll(end_val, .lte, len_s_val, Type.usize))) {
  30938                         const sentinel_label: []const u8 = if (array_ty.sentinel(mod) != null)
  30939                             " +1 (sentinel)"
  30940                         else
  30941                             "";
  30942 
  30943                         return sema.fail(
  30944                             block,
  30945                             end_src,
  30946                             "end index {} out of bounds for array of length {}{s}",
  30947                             .{
  30948                                 end_val.fmtValue(Type.usize, mod),
  30949                                 len_val.fmtValue(Type.usize, mod),
  30950                                 sentinel_label,
  30951                             },
  30952                         );
  30953                     }
  30954 
  30955                     // end_is_len is only true if we are NOT using the sentinel
  30956                     // length. For sentinel-length, we don't want the type to
  30957                     // contain the sentinel.
  30958                     if (end_val.eql(len_val, Type.usize, mod)) {
  30959                         end_is_len = true;
  30960                     }
  30961                 }
  30962                 break :e end;
  30963             }
  30964 
  30965             break :e try sema.addConstant(len_val);
  30966         } else if (slice_ty.isSlice(mod)) {
  30967             if (!end_is_len) {
  30968                 const end = if (by_length) end: {
  30969                     const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  30970                     const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  30971                     break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
  30972                 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  30973                 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  30974                     if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| {
  30975                         if (slice_val.isUndef(mod)) {
  30976                             return sema.fail(block, src, "slice of undefined", .{});
  30977                         }
  30978                         const has_sentinel = slice_ty.sentinel(mod) != null;
  30979                         const slice_len = slice_val.sliceLen(mod);
  30980                         const len_plus_sent = slice_len + @intFromBool(has_sentinel);
  30981                         const slice_len_val_with_sentinel = try mod.intValue(Type.usize, len_plus_sent);
  30982                         if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, Type.usize))) {
  30983                             const sentinel_label: []const u8 = if (has_sentinel)
  30984                                 " +1 (sentinel)"
  30985                             else
  30986                                 "";
  30987 
  30988                             return sema.fail(
  30989                                 block,
  30990                                 end_src,
  30991                                 "end index {} out of bounds for slice of length {d}{s}",
  30992                                 .{
  30993                                     end_val.fmtValue(Type.usize, mod),
  30994                                     slice_val.sliceLen(mod),
  30995                                     sentinel_label,
  30996                                 },
  30997                             );
  30998                         }
  30999 
  31000                         // If the slice has a sentinel, we consider end_is_len
  31001                         // is only true if it equals the length WITHOUT the
  31002                         // sentinel, so we don't add a sentinel type.
  31003                         const slice_len_val = try mod.intValue(Type.usize, slice_len);
  31004                         if (end_val.eql(slice_len_val, Type.usize, mod)) {
  31005                             end_is_len = true;
  31006                         }
  31007                     }
  31008                 }
  31009                 break :e end;
  31010             }
  31011             break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
  31012         }
  31013         if (!end_is_len) {
  31014             if (by_length) {
  31015                 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  31016                 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  31017                 break :e try sema.coerce(block, Type.usize, uncasted_end, end_src);
  31018             } else break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  31019         }
  31020         return sema.fail(block, src, "slice of pointer must include end value", .{});
  31021     };
  31022 
  31023     const sentinel = s: {
  31024         if (sentinel_opt != .none) {
  31025             const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
  31026             break :s try sema.resolveConstValue(block, sentinel_src, casted, "slice sentinel must be comptime-known");
  31027         }
  31028         // If we are slicing to the end of something that is sentinel-terminated
  31029         // then the resulting slice type is also sentinel-terminated.
  31030         if (end_is_len) {
  31031             if (ptr_sentinel) |sent| {
  31032                 break :s sent;
  31033             }
  31034         }
  31035         break :s null;
  31036     };
  31037     const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
  31038 
  31039     var checked_start_lte_end = by_length;
  31040     var runtime_src: ?LazySrcLoc = null;
  31041 
  31042     // requirement: start <= end
  31043     if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  31044         if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
  31045             if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
  31046                 return sema.fail(
  31047                     block,
  31048                     start_src,
  31049                     "start index {} is larger than end index {}",
  31050                     .{
  31051                         start_val.fmtValue(Type.usize, mod),
  31052                         end_val.fmtValue(Type.usize, mod),
  31053                     },
  31054                 );
  31055             }
  31056             checked_start_lte_end = true;
  31057             if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: {
  31058                 const expected_sentinel = sentinel orelse break :sentinel_check;
  31059                 const start_int = start_val.getUnsignedInt(mod).?;
  31060                 const end_int = end_val.getUnsignedInt(mod).?;
  31061                 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
  31062 
  31063                 const many_ptr_ty = try mod.manyConstPtrType(elem_ty);
  31064                 const many_ptr_val = try mod.getCoerced(ptr_val, many_ptr_ty);
  31065                 const elem_ptr_ty = try mod.singleConstPtrType(elem_ty);
  31066                 const elem_ptr = try many_ptr_val.elemPtr(elem_ptr_ty, sentinel_index, mod);
  31067                 const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty);
  31068                 const actual_sentinel = switch (res) {
  31069                     .runtime_load => break :sentinel_check,
  31070                     .val => |v| v,
  31071                     .needed_well_defined => |ty| return sema.fail(
  31072                         block,
  31073                         src,
  31074                         "comptime dereference requires '{}' to have a well-defined layout, but it does not.",
  31075                         .{ty.fmt(mod)},
  31076                     ),
  31077                     .out_of_bounds => |ty| return sema.fail(
  31078                         block,
  31079                         end_src,
  31080                         "slice end index {d} exceeds bounds of containing decl of type '{}'",
  31081                         .{ end_int, ty.fmt(mod) },
  31082                     ),
  31083                 };
  31084 
  31085                 if (!actual_sentinel.eql(expected_sentinel, elem_ty, mod)) {
  31086                     const msg = msg: {
  31087                         const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{});
  31088                         errdefer msg.destroy(sema.gpa);
  31089                         try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{
  31090                             expected_sentinel.fmtValue(elem_ty, mod),
  31091                             actual_sentinel.fmtValue(elem_ty, mod),
  31092                         });
  31093 
  31094                         break :msg msg;
  31095                     };
  31096                     return sema.failWithOwnedErrorMsg(msg);
  31097                 }
  31098             } else {
  31099                 runtime_src = ptr_src;
  31100             }
  31101         } else {
  31102             runtime_src = start_src;
  31103         }
  31104     } else {
  31105         runtime_src = end_src;
  31106     }
  31107 
  31108     if (!checked_start_lte_end and block.wantSafety() and !block.is_comptime) {
  31109         // requirement: start <= end
  31110         assert(!block.is_comptime);
  31111         try sema.requireRuntimeBlock(block, src, runtime_src.?);
  31112         const ok = try block.addBinOp(.cmp_lte, start, end);
  31113         if (!sema.mod.comp.formatted_panics) {
  31114             try sema.addSafetyCheck(block, ok, .start_index_greater_than_end);
  31115         } else {
  31116             try sema.safetyCheckFormatted(block, ok, "panicStartGreaterThanEnd", &.{ start, end });
  31117         }
  31118     }
  31119     const new_len = if (by_length)
  31120         try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
  31121     else
  31122         try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
  31123     const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
  31124 
  31125     const new_ptr_ty_info = new_ptr_ty.ptrInfo(mod);
  31126     const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(mod) != .C;
  31127 
  31128     if (opt_new_len_val) |new_len_val| {
  31129         const new_len_int = new_len_val.toUnsignedInt(mod);
  31130 
  31131         const return_ty = try mod.ptrType(.{
  31132             .child = (try mod.arrayType(.{
  31133                 .len = new_len_int,
  31134                 .sentinel = if (sentinel) |s| s.toIntern() else .none,
  31135                 .child = elem_ty.toIntern(),
  31136             })).toIntern(),
  31137             .flags = .{
  31138                 .alignment = new_ptr_ty_info.flags.alignment,
  31139                 .is_const = new_ptr_ty_info.flags.is_const,
  31140                 .is_allowzero = new_allowzero,
  31141                 .is_volatile = new_ptr_ty_info.flags.is_volatile,
  31142                 .address_space = new_ptr_ty_info.flags.address_space,
  31143             },
  31144         });
  31145 
  31146         const opt_new_ptr_val = try sema.resolveMaybeUndefVal(new_ptr);
  31147         const new_ptr_val = opt_new_ptr_val orelse {
  31148             const result = try block.addBitCast(return_ty, new_ptr);
  31149             if (block.wantSafety()) {
  31150                 // requirement: slicing C ptr is non-null
  31151                 if (ptr_ptr_child_ty.isCPtr(mod)) {
  31152                     const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
  31153                     try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
  31154                 }
  31155 
  31156                 if (slice_ty.isSlice(mod)) {
  31157                     const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);
  31158                     const actual_len = if (slice_ty.sentinel(mod) == null)
  31159                         slice_len_inst
  31160                     else
  31161                         try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
  31162 
  31163                     const actual_end = if (slice_sentinel != null)
  31164                         try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
  31165                     else
  31166                         end;
  31167 
  31168                     try sema.panicIndexOutOfBounds(block, actual_end, actual_len, .cmp_lte);
  31169                 }
  31170 
  31171                 // requirement: result[new_len] == slice_sentinel
  31172                 try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
  31173             }
  31174             return result;
  31175         };
  31176 
  31177         if (!new_ptr_val.isUndef(mod)) {
  31178             return sema.addConstant(try mod.getCoerced(
  31179                 (try new_ptr_val.intern(new_ptr_ty, mod)).toValue(),
  31180                 return_ty,
  31181             ));
  31182         }
  31183 
  31184         // Special case: @as([]i32, undefined)[x..x]
  31185         if (new_len_int == 0) {
  31186             return sema.addConstUndef(return_ty);
  31187         }
  31188 
  31189         return sema.fail(block, src, "non-zero length slice of undefined pointer", .{});
  31190     }
  31191 
  31192     const return_ty = try mod.ptrType(.{
  31193         .child = elem_ty.toIntern(),
  31194         .sentinel = if (sentinel) |s| s.toIntern() else .none,
  31195         .flags = .{
  31196             .size = .Slice,
  31197             .alignment = new_ptr_ty_info.flags.alignment,
  31198             .is_const = new_ptr_ty_info.flags.is_const,
  31199             .is_volatile = new_ptr_ty_info.flags.is_volatile,
  31200             .is_allowzero = new_allowzero,
  31201             .address_space = new_ptr_ty_info.flags.address_space,
  31202         },
  31203     });
  31204 
  31205     try sema.requireRuntimeBlock(block, src, runtime_src.?);
  31206     if (block.wantSafety()) {
  31207         // requirement: slicing C ptr is non-null
  31208         if (ptr_ptr_child_ty.isCPtr(mod)) {
  31209             const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
  31210             try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
  31211         }
  31212 
  31213         // requirement: end <= len
  31214         const opt_len_inst = if (array_ty.zigTypeTag(mod) == .Array)
  31215             try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel(mod))
  31216         else if (slice_ty.isSlice(mod)) blk: {
  31217             if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
  31218                 // we don't need to add one for sentinels because the
  31219                 // underlying value data includes the sentinel
  31220                 break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod));
  31221             }
  31222 
  31223             const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);
  31224             if (slice_ty.sentinel(mod) == null) break :blk slice_len_inst;
  31225 
  31226             // we have to add one because slice lengths don't include the sentinel
  31227             break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
  31228         } else null;
  31229         if (opt_len_inst) |len_inst| {
  31230             const actual_end = if (slice_sentinel != null)
  31231                 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
  31232             else
  31233                 end;
  31234             try sema.panicIndexOutOfBounds(block, actual_end, len_inst, .cmp_lte);
  31235         }
  31236 
  31237         // requirement: start <= end
  31238         try sema.panicIndexOutOfBounds(block, start, end, .cmp_lte);
  31239     }
  31240     const result = try block.addInst(.{
  31241         .tag = .slice,
  31242         .data = .{ .ty_pl = .{
  31243             .ty = try sema.addType(return_ty),
  31244             .payload = try sema.addExtra(Air.Bin{
  31245                 .lhs = new_ptr,
  31246                 .rhs = new_len,
  31247             }),
  31248         } },
  31249     });
  31250     if (block.wantSafety()) {
  31251         // requirement: result[new_len] == slice_sentinel
  31252         try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
  31253     }
  31254     return result;
  31255 }
  31256 
  31257 /// Asserts that lhs and rhs types are both numeric.
  31258 fn cmpNumeric(
  31259     sema: *Sema,
  31260     block: *Block,
  31261     src: LazySrcLoc,
  31262     uncasted_lhs: Air.Inst.Ref,
  31263     uncasted_rhs: Air.Inst.Ref,
  31264     op: std.math.CompareOperator,
  31265     lhs_src: LazySrcLoc,
  31266     rhs_src: LazySrcLoc,
  31267 ) CompileError!Air.Inst.Ref {
  31268     const mod = sema.mod;
  31269     const lhs_ty = sema.typeOf(uncasted_lhs);
  31270     const rhs_ty = sema.typeOf(uncasted_rhs);
  31271 
  31272     assert(lhs_ty.isNumeric(mod));
  31273     assert(rhs_ty.isNumeric(mod));
  31274 
  31275     const lhs_ty_tag = lhs_ty.zigTypeTag(mod);
  31276     const rhs_ty_tag = rhs_ty.zigTypeTag(mod);
  31277     const target = mod.getTarget();
  31278 
  31279     // One exception to heterogeneous comparison: comptime_float needs to
  31280     // coerce to fixed-width float.
  31281 
  31282     const lhs = if (lhs_ty_tag == .ComptimeFloat and rhs_ty_tag == .Float)
  31283         try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src)
  31284     else
  31285         uncasted_lhs;
  31286 
  31287     const rhs = if (lhs_ty_tag == .Float and rhs_ty_tag == .ComptimeFloat)
  31288         try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src)
  31289     else
  31290         uncasted_rhs;
  31291 
  31292     const runtime_src: LazySrcLoc = src: {
  31293         if (try sema.resolveMaybeUndefVal(lhs)) |lhs_val| {
  31294             if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| {
  31295                 // Compare ints: const vs. undefined (or vice versa)
  31296                 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)) {
  31297                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
  31298                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
  31299                     }
  31300                 } 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)) {
  31301                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
  31302                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
  31303                     }
  31304                 }
  31305 
  31306                 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  31307                     return sema.addConstUndef(Type.bool);
  31308                 }
  31309                 if (lhs_val.isNan(mod) or rhs_val.isNan(mod)) {
  31310                     if (op == std.math.CompareOperator.neq) {
  31311                         return Air.Inst.Ref.bool_true;
  31312                     } else {
  31313                         return Air.Inst.Ref.bool_false;
  31314                     }
  31315                 }
  31316                 if (try Value.compareHeteroAdvanced(lhs_val, op, rhs_val, mod, sema)) {
  31317                     return Air.Inst.Ref.bool_true;
  31318                 } else {
  31319                     return Air.Inst.Ref.bool_false;
  31320                 }
  31321             } else {
  31322                 if (!lhs_val.isUndef(mod) and (lhs_ty.isInt(mod) or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt(mod)) {
  31323                     // Compare ints: const vs. var
  31324                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
  31325                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
  31326                     }
  31327                 }
  31328                 break :src rhs_src;
  31329             }
  31330         } else {
  31331             if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| {
  31332                 if (!rhs_val.isUndef(mod) and (rhs_ty.isInt(mod) or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt(mod)) {
  31333                     // Compare ints: var vs. const
  31334                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
  31335                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
  31336                     }
  31337                 }
  31338             }
  31339             break :src lhs_src;
  31340         }
  31341     };
  31342 
  31343     // TODO handle comparisons against lazy zero values
  31344     // Some values can be compared against zero without being runtime-known or without forcing
  31345     // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
  31346     // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
  31347     // of this function if we don't need to.
  31348     try sema.requireRuntimeBlock(block, src, runtime_src);
  31349 
  31350     // For floats, emit a float comparison instruction.
  31351     const lhs_is_float = switch (lhs_ty_tag) {
  31352         .Float, .ComptimeFloat => true,
  31353         else => false,
  31354     };
  31355     const rhs_is_float = switch (rhs_ty_tag) {
  31356         .Float, .ComptimeFloat => true,
  31357         else => false,
  31358     };
  31359 
  31360     if (lhs_is_float and rhs_is_float) {
  31361         // Smaller fixed-width floats coerce to larger fixed-width floats.
  31362         // comptime_float coerces to fixed-width float.
  31363         const dest_ty = x: {
  31364             if (lhs_ty_tag == .ComptimeFloat) {
  31365                 break :x rhs_ty;
  31366             } else if (rhs_ty_tag == .ComptimeFloat) {
  31367                 break :x lhs_ty;
  31368             }
  31369             if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) {
  31370                 break :x lhs_ty;
  31371             } else {
  31372                 break :x rhs_ty;
  31373             }
  31374         };
  31375         const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
  31376         const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
  31377         return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs);
  31378     }
  31379     // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
  31380     // For mixed signed and unsigned integers, implicit cast both operands to a signed
  31381     // integer with + 1 bit.
  31382     // For mixed floats and integers, extract the integer part from the float, cast that to
  31383     // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
  31384     // add/subtract 1.
  31385     const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|
  31386         !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema))
  31387     else
  31388         (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(mod));
  31389     const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
  31390         !(try rhs_val.compareAllWithZeroAdvanced(.gte, sema))
  31391     else
  31392         (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(mod));
  31393     const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
  31394 
  31395     var dest_float_type: ?Type = null;
  31396 
  31397     var lhs_bits: usize = undefined;
  31398     if (try sema.resolveMaybeUndefLazyVal(lhs)) |lhs_val| {
  31399         if (lhs_val.isUndef(mod))
  31400             return sema.addConstUndef(Type.bool);
  31401         if (lhs_val.isNan(mod)) switch (op) {
  31402             .neq => return Air.Inst.Ref.bool_true,
  31403             else => return Air.Inst.Ref.bool_false,
  31404         };
  31405         if (lhs_val.isInf(mod)) switch (op) {
  31406             .neq => return Air.Inst.Ref.bool_true,
  31407             .eq => return Air.Inst.Ref.bool_false,
  31408             .gt, .gte => return if (lhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true,
  31409             .lt, .lte => return if (lhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false,
  31410         };
  31411         if (!rhs_is_signed) {
  31412             switch (lhs_val.orderAgainstZero(mod)) {
  31413                 .gt => {},
  31414                 .eq => switch (op) { // LHS = 0, RHS is unsigned
  31415                     .lte => return Air.Inst.Ref.bool_true,
  31416                     .gt => return Air.Inst.Ref.bool_false,
  31417                     else => {},
  31418                 },
  31419                 .lt => switch (op) { // LHS < 0, RHS is unsigned
  31420                     .neq, .lt, .lte => return Air.Inst.Ref.bool_true,
  31421                     .eq, .gt, .gte => return Air.Inst.Ref.bool_false,
  31422                 },
  31423             }
  31424         }
  31425         if (lhs_is_float) {
  31426             if (lhs_val.floatHasFraction(mod)) {
  31427                 switch (op) {
  31428                     .eq => return Air.Inst.Ref.bool_false,
  31429                     .neq => return Air.Inst.Ref.bool_true,
  31430                     else => {},
  31431                 }
  31432             }
  31433 
  31434             var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128, mod));
  31435             defer bigint.deinit();
  31436             if (lhs_val.floatHasFraction(mod)) {
  31437                 if (lhs_is_signed) {
  31438                     try bigint.addScalar(&bigint, -1);
  31439                 } else {
  31440                     try bigint.addScalar(&bigint, 1);
  31441                 }
  31442             }
  31443             lhs_bits = bigint.toConst().bitCountTwosComp();
  31444         } else {
  31445             lhs_bits = lhs_val.intBitCountTwosComp(mod);
  31446         }
  31447         lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed);
  31448     } else if (lhs_is_float) {
  31449         dest_float_type = lhs_ty;
  31450     } else {
  31451         const int_info = lhs_ty.intInfo(mod);
  31452         lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
  31453     }
  31454 
  31455     var rhs_bits: usize = undefined;
  31456     if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| {
  31457         if (rhs_val.isUndef(mod))
  31458             return sema.addConstUndef(Type.bool);
  31459         if (rhs_val.isNan(mod)) switch (op) {
  31460             .neq => return Air.Inst.Ref.bool_true,
  31461             else => return Air.Inst.Ref.bool_false,
  31462         };
  31463         if (rhs_val.isInf(mod)) switch (op) {
  31464             .neq => return Air.Inst.Ref.bool_true,
  31465             .eq => return Air.Inst.Ref.bool_false,
  31466             .gt, .gte => return if (rhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false,
  31467             .lt, .lte => return if (rhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true,
  31468         };
  31469         if (!lhs_is_signed) {
  31470             switch (rhs_val.orderAgainstZero(mod)) {
  31471                 .gt => {},
  31472                 .eq => switch (op) { // RHS = 0, LHS is unsigned
  31473                     .gte => return Air.Inst.Ref.bool_true,
  31474                     .lt => return Air.Inst.Ref.bool_false,
  31475                     else => {},
  31476                 },
  31477                 .lt => switch (op) { // RHS < 0, LHS is unsigned
  31478                     .neq, .gt, .gte => return Air.Inst.Ref.bool_true,
  31479                     .eq, .lt, .lte => return Air.Inst.Ref.bool_false,
  31480                 },
  31481             }
  31482         }
  31483         if (rhs_is_float) {
  31484             if (rhs_val.floatHasFraction(mod)) {
  31485                 switch (op) {
  31486                     .eq => return Air.Inst.Ref.bool_false,
  31487                     .neq => return Air.Inst.Ref.bool_true,
  31488                     else => {},
  31489                 }
  31490             }
  31491 
  31492             var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128, mod));
  31493             defer bigint.deinit();
  31494             if (rhs_val.floatHasFraction(mod)) {
  31495                 if (rhs_is_signed) {
  31496                     try bigint.addScalar(&bigint, -1);
  31497                 } else {
  31498                     try bigint.addScalar(&bigint, 1);
  31499                 }
  31500             }
  31501             rhs_bits = bigint.toConst().bitCountTwosComp();
  31502         } else {
  31503             rhs_bits = rhs_val.intBitCountTwosComp(mod);
  31504         }
  31505         rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed);
  31506     } else if (rhs_is_float) {
  31507         dest_float_type = rhs_ty;
  31508     } else {
  31509         const int_info = rhs_ty.intInfo(mod);
  31510         rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
  31511     }
  31512 
  31513     const dest_ty = if (dest_float_type) |ft| ft else blk: {
  31514         const max_bits = @max(lhs_bits, rhs_bits);
  31515         const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits});
  31516         const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
  31517         break :blk try mod.intType(signedness, casted_bits);
  31518     };
  31519     const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
  31520     const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
  31521 
  31522     return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs);
  31523 }
  31524 
  31525 /// Asserts that LHS value is an int or comptime int and not undefined, and
  31526 /// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to
  31527 /// determine whether `op` has a guaranteed result.
  31528 /// If it cannot be determined, returns null.
  31529 /// Otherwise returns a bool for the guaranteed comparison operation.
  31530 fn compareIntsOnlyPossibleResult(
  31531     sema: *Sema,
  31532     lhs_val: Value,
  31533     op: std.math.CompareOperator,
  31534     rhs_ty: Type,
  31535 ) Allocator.Error!?bool {
  31536     const mod = sema.mod;
  31537     const rhs_info = rhs_ty.intInfo(mod);
  31538     const vs_zero = lhs_val.orderAgainstZeroAdvanced(mod, sema) catch unreachable;
  31539     const is_zero = vs_zero == .eq;
  31540     const is_negative = vs_zero == .lt;
  31541     const is_positive = vs_zero == .gt;
  31542 
  31543     // Anything vs. zero-sized type has guaranteed outcome.
  31544     if (rhs_info.bits == 0) return switch (op) {
  31545         .eq, .lte, .gte => is_zero,
  31546         .neq, .lt, .gt => !is_zero,
  31547     };
  31548 
  31549     // Special case for i1, which can only be 0 or -1.
  31550     // Zero and positive ints have guaranteed outcome.
  31551     if (rhs_info.bits == 1 and rhs_info.signedness == .signed) {
  31552         if (is_positive) return switch (op) {
  31553             .gt, .gte, .neq => true,
  31554             .lt, .lte, .eq => false,
  31555         };
  31556         if (is_zero) return switch (op) {
  31557             .gte => true,
  31558             .lt => false,
  31559             .gt, .lte, .eq, .neq => null,
  31560         };
  31561     }
  31562 
  31563     // Negative vs. unsigned has guaranteed outcome.
  31564     if (rhs_info.signedness == .unsigned and is_negative) return switch (op) {
  31565         .eq, .gt, .gte => false,
  31566         .neq, .lt, .lte => true,
  31567     };
  31568 
  31569     const sign_adj = @intFromBool(!is_negative and rhs_info.signedness == .signed);
  31570     const req_bits = lhs_val.intBitCountTwosComp(mod) + sign_adj;
  31571 
  31572     // No sized type can have more than 65535 bits.
  31573     // The RHS type operand is either a runtime value or sized (but undefined) constant.
  31574     if (req_bits > 65535) return switch (op) {
  31575         .lt, .lte => is_negative,
  31576         .gt, .gte => is_positive,
  31577         .eq => false,
  31578         .neq => true,
  31579     };
  31580     const fits = req_bits <= rhs_info.bits;
  31581 
  31582     // Oversized int has guaranteed outcome.
  31583     switch (op) {
  31584         .eq => return if (!fits) false else null,
  31585         .neq => return if (!fits) true else null,
  31586         .lt, .lte => if (!fits) return is_negative,
  31587         .gt, .gte => if (!fits) return !is_negative,
  31588     }
  31589 
  31590     // For any other comparison, we need to know if the LHS value is
  31591     // equal to the maximum or minimum possible value of the RHS type.
  31592     const edge: struct { min: bool, max: bool } = edge: {
  31593         if (is_zero and rhs_info.signedness == .unsigned) break :edge .{
  31594             .min = true,
  31595             .max = false,
  31596         };
  31597 
  31598         if (req_bits != rhs_info.bits) break :edge .{
  31599             .min = false,
  31600             .max = false,
  31601         };
  31602 
  31603         const ty = try mod.intType(
  31604             if (is_negative) .signed else .unsigned,
  31605             @as(u16, @intCast(req_bits)),
  31606         );
  31607         const pop_count = lhs_val.popCount(ty, mod);
  31608 
  31609         if (is_negative) {
  31610             break :edge .{
  31611                 .min = pop_count == 1,
  31612                 .max = false,
  31613             };
  31614         } else {
  31615             break :edge .{
  31616                 .min = false,
  31617                 .max = pop_count == req_bits - sign_adj,
  31618             };
  31619         }
  31620     };
  31621 
  31622     assert(fits);
  31623     return switch (op) {
  31624         .lt => if (edge.max) false else null,
  31625         .lte => if (edge.min) true else null,
  31626         .gt => if (edge.min) false else null,
  31627         .gte => if (edge.max) true else null,
  31628         .eq, .neq => unreachable,
  31629     };
  31630 }
  31631 
  31632 /// Asserts that lhs and rhs types are both vectors.
  31633 fn cmpVector(
  31634     sema: *Sema,
  31635     block: *Block,
  31636     src: LazySrcLoc,
  31637     lhs: Air.Inst.Ref,
  31638     rhs: Air.Inst.Ref,
  31639     op: std.math.CompareOperator,
  31640     lhs_src: LazySrcLoc,
  31641     rhs_src: LazySrcLoc,
  31642 ) CompileError!Air.Inst.Ref {
  31643     const mod = sema.mod;
  31644     const lhs_ty = sema.typeOf(lhs);
  31645     const rhs_ty = sema.typeOf(rhs);
  31646     assert(lhs_ty.zigTypeTag(mod) == .Vector);
  31647     assert(rhs_ty.zigTypeTag(mod) == .Vector);
  31648     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  31649 
  31650     const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } });
  31651     const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src);
  31652     const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src);
  31653 
  31654     const result_ty = try mod.vectorType(.{
  31655         .len = lhs_ty.vectorLen(mod),
  31656         .child = .bool_type,
  31657     });
  31658 
  31659     const runtime_src: LazySrcLoc = src: {
  31660         if (try sema.resolveMaybeUndefVal(casted_lhs)) |lhs_val| {
  31661             if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| {
  31662                 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  31663                     return sema.addConstUndef(result_ty);
  31664                 }
  31665                 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
  31666                 return sema.addConstant(cmp_val);
  31667             } else {
  31668                 break :src rhs_src;
  31669             }
  31670         } else {
  31671             break :src lhs_src;
  31672         }
  31673     };
  31674 
  31675     try sema.requireRuntimeBlock(block, src, runtime_src);
  31676     return block.addCmpVector(casted_lhs, casted_rhs, op);
  31677 }
  31678 
  31679 fn wrapOptional(
  31680     sema: *Sema,
  31681     block: *Block,
  31682     dest_ty: Type,
  31683     inst: Air.Inst.Ref,
  31684     inst_src: LazySrcLoc,
  31685 ) !Air.Inst.Ref {
  31686     if (try sema.resolveMaybeUndefVal(inst)) |val| {
  31687         return sema.addConstant((try sema.mod.intern(.{ .opt = .{
  31688             .ty = dest_ty.toIntern(),
  31689             .val = val.toIntern(),
  31690         } })).toValue());
  31691     }
  31692 
  31693     try sema.requireRuntimeBlock(block, inst_src, null);
  31694     return block.addTyOp(.wrap_optional, dest_ty, inst);
  31695 }
  31696 
  31697 fn wrapErrorUnionPayload(
  31698     sema: *Sema,
  31699     block: *Block,
  31700     dest_ty: Type,
  31701     inst: Air.Inst.Ref,
  31702     inst_src: LazySrcLoc,
  31703 ) !Air.Inst.Ref {
  31704     const mod = sema.mod;
  31705     const dest_payload_ty = dest_ty.errorUnionPayload(mod);
  31706     const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false });
  31707     if (try sema.resolveMaybeUndefVal(coerced)) |val| {
  31708         return sema.addConstant((try mod.intern(.{ .error_union = .{
  31709             .ty = dest_ty.toIntern(),
  31710             .val = .{ .payload = try val.intern(dest_payload_ty, mod) },
  31711         } })).toValue());
  31712     }
  31713     try sema.requireRuntimeBlock(block, inst_src, null);
  31714     try sema.queueFullTypeResolution(dest_payload_ty);
  31715     return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
  31716 }
  31717 
  31718 fn wrapErrorUnionSet(
  31719     sema: *Sema,
  31720     block: *Block,
  31721     dest_ty: Type,
  31722     inst: Air.Inst.Ref,
  31723     inst_src: LazySrcLoc,
  31724 ) !Air.Inst.Ref {
  31725     const mod = sema.mod;
  31726     const ip = &mod.intern_pool;
  31727     const inst_ty = sema.typeOf(inst);
  31728     const dest_err_set_ty = dest_ty.errorUnionSet(mod);
  31729     if (try sema.resolveMaybeUndefVal(inst)) |val| {
  31730         switch (dest_err_set_ty.toIntern()) {
  31731             .anyerror_type => {},
  31732             else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) {
  31733                 .error_set_type => |error_set_type| ok: {
  31734                     const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name;
  31735                     if (error_set_type.nameIndex(ip, expected_name) != null) break :ok;
  31736                     return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  31737                 },
  31738                 .inferred_error_set_type => |ies_index| ok: {
  31739                     const ies = mod.inferredErrorSetPtr(ies_index);
  31740                     const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name;
  31741 
  31742                     // We carefully do this in an order that avoids unnecessarily
  31743                     // resolving the destination error set type.
  31744                     if (ies.is_anyerror) break :ok;
  31745 
  31746                     if (ies.errors.contains(expected_name)) break :ok;
  31747                     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) break :ok;
  31748 
  31749                     return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  31750                 },
  31751                 else => unreachable,
  31752             },
  31753         }
  31754         return sema.addConstant((try mod.intern(.{ .error_union = .{
  31755             .ty = dest_ty.toIntern(),
  31756             .val = .{
  31757                 .err_name = mod.intern_pool.indexToKey(try val.intern(dest_err_set_ty, mod)).err.name,
  31758             },
  31759         } })).toValue());
  31760     }
  31761 
  31762     try sema.requireRuntimeBlock(block, inst_src, null);
  31763     const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src);
  31764     return block.addTyOp(.wrap_errunion_err, dest_ty, coerced);
  31765 }
  31766 
  31767 fn unionToTag(
  31768     sema: *Sema,
  31769     block: *Block,
  31770     enum_ty: Type,
  31771     un: Air.Inst.Ref,
  31772     un_src: LazySrcLoc,
  31773 ) !Air.Inst.Ref {
  31774     const mod = sema.mod;
  31775     if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| {
  31776         return sema.addConstant(opv);
  31777     }
  31778     if (try sema.resolveMaybeUndefVal(un)) |un_val| {
  31779         return sema.addConstant(un_val.unionTag(mod));
  31780     }
  31781     try sema.requireRuntimeBlock(block, un_src, null);
  31782     return block.addTyOp(.get_union_tag, enum_ty, un);
  31783 }
  31784 
  31785 const PeerResolveStrategy = enum {
  31786     /// The type is not known.
  31787     /// If refined no further, this is equivalent to `exact`.
  31788     unknown,
  31789     /// The type may be an error set or error union.
  31790     /// If refined no further, it is an error set.
  31791     error_set,
  31792     /// The type must be some error union.
  31793     error_union,
  31794     /// The type may be @TypeOf(null), an optional or a C pointer.
  31795     /// If refined no further, it is @TypeOf(null).
  31796     nullable,
  31797     /// The type must be some optional or a C pointer.
  31798     /// If refined no further, it is an optional.
  31799     optional,
  31800     /// The type must be either an array or a vector.
  31801     /// If refined no further, it is an array.
  31802     array,
  31803     /// The type must be a vector.
  31804     vector,
  31805     /// The type must be a C pointer.
  31806     c_ptr,
  31807     /// The type must be a pointer (C or not).
  31808     /// If refined no further, it is a non-C pointer.
  31809     ptr,
  31810     /// The type must be a function or a pointer to a function.
  31811     /// If refined no further, it is a function.
  31812     func,
  31813     /// The type must be an enum literal, or some specific enum or union. Which one is decided
  31814     /// afterwards based on the types in question.
  31815     enum_or_union,
  31816     /// The type must be some integer or float type.
  31817     /// If refined no further, it is `comptime_int`.
  31818     comptime_int,
  31819     /// The type must be some float type.
  31820     /// If refined no further, it is `comptime_float`.
  31821     comptime_float,
  31822     /// The type must be some float or fixed-width integer type.
  31823     /// If refined no further, it is some fixed-width integer type.
  31824     fixed_int,
  31825     /// The type must be some fixed-width float type.
  31826     fixed_float,
  31827     /// The type must be a struct literal or tuple type.
  31828     coercible_struct,
  31829     /// The peers must all be of the same type.
  31830     exact,
  31831 
  31832     /// Given two strategies, find a strategy that satisfies both, if one exists. If no such
  31833     /// strategy exists, any strategy may be returned; an error will be emitted when the caller
  31834     /// attempts to use the strategy to resolve the type.
  31835     /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at
  31836     /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy.
  31837     fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy {
  31838         // Our merging should be order-independent. Thus, even though the union order is arbitrary,
  31839         // by sorting the tags and switching first on the smaller, we have half as many cases to
  31840         // worry about (since we avoid the duplicates).
  31841         const s0_is_a = @intFromEnum(a) <= @intFromEnum(b);
  31842         const s0 = if (s0_is_a) a else b;
  31843         const s1 = if (s0_is_a) b else a;
  31844 
  31845         const ReasonMethod = enum {
  31846             all_s0,
  31847             all_s1,
  31848             either,
  31849         };
  31850 
  31851         const res: struct { ReasonMethod, PeerResolveStrategy } = switch (s0) {
  31852             .unknown => .{ .all_s1, s1 },
  31853             .error_set => switch (s1) {
  31854                 .error_set => .{ .either, .error_set },
  31855                 else => .{ .all_s0, .error_union },
  31856             },
  31857             .error_union => switch (s1) {
  31858                 .error_union => .{ .either, .error_union },
  31859                 else => .{ .all_s0, .error_union },
  31860             },
  31861             .nullable => switch (s1) {
  31862                 .nullable => .{ .either, .nullable },
  31863                 .c_ptr => .{ .all_s1, .c_ptr },
  31864                 else => .{ .all_s0, .optional },
  31865             },
  31866             .optional => switch (s1) {
  31867                 .optional => .{ .either, .optional },
  31868                 .c_ptr => .{ .all_s1, .c_ptr },
  31869                 else => .{ .all_s0, .optional },
  31870             },
  31871             .array => switch (s1) {
  31872                 .array => .{ .either, .array },
  31873                 .vector => .{ .all_s1, .vector },
  31874                 else => .{ .all_s0, .array },
  31875             },
  31876             .vector => switch (s1) {
  31877                 .vector => .{ .either, .vector },
  31878                 else => .{ .all_s0, .vector },
  31879             },
  31880             .c_ptr => switch (s1) {
  31881                 .c_ptr => .{ .either, .c_ptr },
  31882                 else => .{ .all_s0, .c_ptr },
  31883             },
  31884             .ptr => switch (s1) {
  31885                 .ptr => .{ .either, .ptr },
  31886                 else => .{ .all_s0, .ptr },
  31887             },
  31888             .func => switch (s1) {
  31889                 .func => .{ .either, .func },
  31890                 else => .{ .all_s1, s1 }, // doesn't override anything later
  31891             },
  31892             .enum_or_union => switch (s1) {
  31893                 .enum_or_union => .{ .either, .enum_or_union },
  31894                 else => .{ .all_s0, .enum_or_union },
  31895             },
  31896             .comptime_int => switch (s1) {
  31897                 .comptime_int => .{ .either, .comptime_int },
  31898                 else => .{ .all_s1, s1 }, // doesn't override anything later
  31899             },
  31900             .comptime_float => switch (s1) {
  31901                 .comptime_float => .{ .either, .comptime_float },
  31902                 else => .{ .all_s1, s1 }, // doesn't override anything later
  31903             },
  31904             .fixed_int => switch (s1) {
  31905                 .fixed_int => .{ .either, .fixed_int },
  31906                 else => .{ .all_s1, s1 }, // doesn't override anything later
  31907             },
  31908             .fixed_float => switch (s1) {
  31909                 .fixed_float => .{ .either, .fixed_float },
  31910                 else => .{ .all_s1, s1 }, // doesn't override anything later
  31911             },
  31912             .coercible_struct => switch (s1) {
  31913                 .exact => .{ .all_s1, .exact },
  31914                 else => .{ .all_s0, .coercible_struct },
  31915             },
  31916             .exact => .{ .all_s0, .exact },
  31917         };
  31918 
  31919         switch (res[0]) {
  31920             .all_s0 => {
  31921                 if (!s0_is_a) {
  31922                     reason_peer.* = b_peer_idx;
  31923                 }
  31924             },
  31925             .all_s1 => {
  31926                 if (s0_is_a) {
  31927                     reason_peer.* = b_peer_idx;
  31928                 }
  31929             },
  31930             .either => {
  31931                 // Prefer the earliest peer
  31932                 reason_peer.* = @min(reason_peer.*, b_peer_idx);
  31933             },
  31934         }
  31935 
  31936         return res[1];
  31937     }
  31938 
  31939     fn select(ty: Type, mod: *Module) PeerResolveStrategy {
  31940         return switch (ty.zigTypeTag(mod)) {
  31941             .Type, .Void, .Bool, .Opaque, .Frame, .AnyFrame => .exact,
  31942             .NoReturn, .Undefined => .unknown,
  31943             .Null => .nullable,
  31944             .ComptimeInt => .comptime_int,
  31945             .Int => .fixed_int,
  31946             .ComptimeFloat => .comptime_float,
  31947             .Float => .fixed_float,
  31948             .Pointer => if (ty.ptrInfo(mod).flags.size == .C) .c_ptr else .ptr,
  31949             .Array => .array,
  31950             .Vector => .vector,
  31951             .Optional => .optional,
  31952             .ErrorSet => .error_set,
  31953             .ErrorUnion => .error_union,
  31954             .EnumLiteral, .Enum, .Union => .enum_or_union,
  31955             .Struct => if (ty.isTupleOrAnonStruct(mod)) .coercible_struct else .exact,
  31956             .Fn => .func,
  31957         };
  31958     }
  31959 };
  31960 
  31961 const PeerResolveResult = union(enum) {
  31962     /// The peer type resolution was successful, and resulted in the given type.
  31963     success: Type,
  31964     /// There was some generic conflict between two peers.
  31965     conflict: struct {
  31966         peer_idx_a: usize,
  31967         peer_idx_b: usize,
  31968     },
  31969     /// There was an error when resolving the type of a struct or tuple field.
  31970     field_error: struct {
  31971         /// The name of the field which caused the failure.
  31972         field_name: []const u8,
  31973         /// The type of this field in each peer.
  31974         field_types: []Type,
  31975         /// The error from resolving the field type. Guaranteed not to be `success`.
  31976         sub_result: *PeerResolveResult,
  31977     },
  31978 
  31979     fn report(
  31980         result: PeerResolveResult,
  31981         sema: *Sema,
  31982         block: *Block,
  31983         src: LazySrcLoc,
  31984         instructions: []const Air.Inst.Ref,
  31985         candidate_srcs: Module.PeerTypeCandidateSrc,
  31986     ) !*Module.ErrorMsg {
  31987         const mod = sema.mod;
  31988         const decl_ptr = mod.declPtr(block.src_decl);
  31989 
  31990         var opt_msg: ?*Module.ErrorMsg = null;
  31991         errdefer if (opt_msg) |msg| msg.destroy(sema.gpa);
  31992 
  31993         // If we mention fields we'll want to include field types, so put peer types in a buffer
  31994         var peer_tys = try sema.arena.alloc(Type, instructions.len);
  31995         for (peer_tys, instructions) |*ty, inst| {
  31996             ty.* = sema.typeOf(inst);
  31997         }
  31998 
  31999         var cur = result;
  32000         while (true) {
  32001             var conflict_idx: [2]usize = undefined;
  32002 
  32003             switch (cur) {
  32004                 .success => unreachable,
  32005                 .conflict => |conflict| {
  32006                     // Fall through to two-peer conflict handling below
  32007                     conflict_idx = .{
  32008                         conflict.peer_idx_a,
  32009                         conflict.peer_idx_b,
  32010                     };
  32011                 },
  32012                 .field_error => |field_error| {
  32013                     const fmt = "struct field '{s}' has conflicting types";
  32014                     const args = .{field_error.field_name};
  32015                     if (opt_msg) |msg| {
  32016                         try sema.errNote(block, src, msg, fmt, args);
  32017                     } else {
  32018                         opt_msg = try sema.errMsg(block, src, fmt, args);
  32019                     }
  32020 
  32021                     // Continue on to child error
  32022                     cur = field_error.sub_result.*;
  32023                     peer_tys = field_error.field_types;
  32024                     continue;
  32025                 },
  32026             }
  32027 
  32028             // This is the path for reporting a generic conflict between two peers.
  32029 
  32030             if (conflict_idx[1] < conflict_idx[0]) {
  32031                 // b comes first in source, so it's better if it comes first in the error
  32032                 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]);
  32033             }
  32034 
  32035             const conflict_tys: [2]Type = .{
  32036                 peer_tys[conflict_idx[0]],
  32037                 peer_tys[conflict_idx[1]],
  32038             };
  32039             const conflict_srcs: [2]?LazySrcLoc = .{
  32040                 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[0]),
  32041                 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[1]),
  32042             };
  32043 
  32044             const fmt = "incompatible types: '{}' and '{}'";
  32045             const args = .{
  32046                 conflict_tys[0].fmt(mod),
  32047                 conflict_tys[1].fmt(mod),
  32048             };
  32049             const msg = if (opt_msg) |msg| msg: {
  32050                 try sema.errNote(block, src, msg, fmt, args);
  32051                 break :msg msg;
  32052             } else msg: {
  32053                 const msg = try sema.errMsg(block, src, fmt, args);
  32054                 opt_msg = msg;
  32055                 break :msg msg;
  32056             };
  32057 
  32058             if (conflict_srcs[0]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[0].fmt(mod)});
  32059             if (conflict_srcs[1]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[1].fmt(mod)});
  32060 
  32061             // No child error
  32062             break;
  32063         }
  32064 
  32065         return opt_msg.?;
  32066     }
  32067 };
  32068 
  32069 fn resolvePeerTypes(
  32070     sema: *Sema,
  32071     block: *Block,
  32072     src: LazySrcLoc,
  32073     instructions: []const Air.Inst.Ref,
  32074     candidate_srcs: Module.PeerTypeCandidateSrc,
  32075 ) !Type {
  32076     switch (instructions.len) {
  32077         0 => return Type.noreturn,
  32078         1 => return sema.typeOf(instructions[0]),
  32079         else => {},
  32080     }
  32081 
  32082     var peer_tys = try sema.arena.alloc(?Type, instructions.len);
  32083     var peer_vals = try sema.arena.alloc(?Value, instructions.len);
  32084 
  32085     for (instructions, peer_tys, peer_vals) |inst, *ty, *val| {
  32086         ty.* = sema.typeOf(inst);
  32087         val.* = try sema.resolveMaybeUndefVal(inst);
  32088     }
  32089 
  32090     switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) {
  32091         .success => |ty| return ty,
  32092         else => |result| {
  32093             const msg = try result.report(sema, block, src, instructions, candidate_srcs);
  32094             return sema.failWithOwnedErrorMsg(msg);
  32095         },
  32096     }
  32097 }
  32098 
  32099 fn resolvePeerTypesInner(
  32100     sema: *Sema,
  32101     block: *Block,
  32102     src: LazySrcLoc,
  32103     peer_tys: []?Type,
  32104     peer_vals: []?Value,
  32105 ) !PeerResolveResult {
  32106     const mod = sema.mod;
  32107 
  32108     var strat_reason: usize = 0;
  32109     var s: PeerResolveStrategy = .unknown;
  32110     for (peer_tys, 0..) |opt_ty, i| {
  32111         const ty = opt_ty orelse continue;
  32112         s = s.merge(PeerResolveStrategy.select(ty, mod), &strat_reason, i);
  32113     }
  32114 
  32115     if (s == .unknown) {
  32116         // The whole thing was noreturn or undefined - try to do an exact match
  32117         s = .exact;
  32118     } else {
  32119         // There was something other than noreturn and undefined, so we can ignore those peers
  32120         for (peer_tys) |*ty_ptr| {
  32121             const ty = ty_ptr.* orelse continue;
  32122             switch (ty.zigTypeTag(mod)) {
  32123                 .NoReturn, .Undefined => ty_ptr.* = null,
  32124                 else => {},
  32125             }
  32126         }
  32127     }
  32128 
  32129     const target = mod.getTarget();
  32130 
  32131     switch (s) {
  32132         .unknown => unreachable,
  32133 
  32134         .error_set => {
  32135             var final_set: ?Type = null;
  32136             for (peer_tys, 0..) |opt_ty, i| {
  32137                 const ty = opt_ty orelse continue;
  32138                 if (ty.zigTypeTag(mod) != .ErrorSet) return .{ .conflict = .{
  32139                     .peer_idx_a = strat_reason,
  32140                     .peer_idx_b = i,
  32141                 } };
  32142                 if (final_set) |cur_set| {
  32143                     final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty);
  32144                 } else {
  32145                     final_set = ty;
  32146                 }
  32147             }
  32148             return .{ .success = final_set.? };
  32149         },
  32150 
  32151         .error_union => {
  32152             var final_set: ?Type = null;
  32153             for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
  32154                 const ty = ty_ptr.* orelse continue;
  32155                 const set_ty = switch (ty.zigTypeTag(mod)) {
  32156                     .ErrorSet => blk: {
  32157                         ty_ptr.* = null; // no payload to decide on
  32158                         val_ptr.* = null;
  32159                         break :blk ty;
  32160                     },
  32161                     .ErrorUnion => blk: {
  32162                         const set_ty = ty.errorUnionSet(mod);
  32163                         ty_ptr.* = ty.errorUnionPayload(mod);
  32164                         if (val_ptr.*) |eu_val| switch (mod.intern_pool.indexToKey(eu_val.toIntern())) {
  32165                             .error_union => |eu| switch (eu.val) {
  32166                                 .payload => |payload_ip| val_ptr.* = payload_ip.toValue(),
  32167                                 .err_name => val_ptr.* = null,
  32168                             },
  32169                             .undef => val_ptr.* = (try sema.mod.intern(.{ .undef = ty_ptr.*.?.toIntern() })).toValue(),
  32170                             else => unreachable,
  32171                         };
  32172                         break :blk set_ty;
  32173                     },
  32174                     else => continue, // whole type is the payload
  32175                 };
  32176                 if (final_set) |cur_set| {
  32177                     final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty);
  32178                 } else {
  32179                     final_set = set_ty;
  32180                 }
  32181             }
  32182             assert(final_set != null);
  32183             const final_payload = switch (try sema.resolvePeerTypesInner(
  32184                 block,
  32185                 src,
  32186                 peer_tys,
  32187                 peer_vals,
  32188             )) {
  32189                 .success => |ty| ty,
  32190                 else => |result| return result,
  32191             };
  32192             return .{ .success = try mod.errorUnionType(final_set.?, final_payload) };
  32193         },
  32194 
  32195         .nullable => {
  32196             for (peer_tys, 0..) |opt_ty, i| {
  32197                 const ty = opt_ty orelse continue;
  32198                 if (!ty.eql(Type.null, mod)) return .{ .conflict = .{
  32199                     .peer_idx_a = strat_reason,
  32200                     .peer_idx_b = i,
  32201                 } };
  32202             }
  32203             return .{ .success = Type.null };
  32204         },
  32205 
  32206         .optional => {
  32207             for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
  32208                 const ty = ty_ptr.* orelse continue;
  32209                 switch (ty.zigTypeTag(mod)) {
  32210                     .Null => {
  32211                         ty_ptr.* = null;
  32212                         val_ptr.* = null;
  32213                     },
  32214                     .Optional => {
  32215                         ty_ptr.* = ty.optionalChild(mod);
  32216                         if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(mod)) opt_val.optionalValue(mod) else null;
  32217                     },
  32218                     else => {},
  32219                 }
  32220             }
  32221             const child_ty = switch (try sema.resolvePeerTypesInner(
  32222                 block,
  32223                 src,
  32224                 peer_tys,
  32225                 peer_vals,
  32226             )) {
  32227                 .success => |ty| ty,
  32228                 else => |result| return result,
  32229             };
  32230             return .{ .success = try mod.optionalType(child_ty.toIntern()) };
  32231         },
  32232 
  32233         .array => {
  32234             // Index of the first non-null peer
  32235             var opt_first_idx: ?usize = null;
  32236             // Index of the first array or vector peer (i.e. not a tuple)
  32237             var opt_first_arr_idx: ?usize = null;
  32238             // Set to non-null once we see any peer, even a tuple
  32239             var len: u64 = undefined;
  32240             var sentinel: ?Value = undefined;
  32241             // Only set once we see a non-tuple peer
  32242             var elem_ty: Type = undefined;
  32243 
  32244             for (peer_tys, 0..) |*ty_ptr, i| {
  32245                 const ty = ty_ptr.* orelse continue;
  32246 
  32247                 if (!ty.isArrayOrVector(mod)) {
  32248                     // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced.
  32249                     const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
  32250                         .peer_idx_a = strat_reason,
  32251                         .peer_idx_b = i,
  32252                     } };
  32253 
  32254                     if (opt_first_idx) |first_idx| {
  32255                         if (arr_like.len != len) return .{ .conflict = .{
  32256                             .peer_idx_a = first_idx,
  32257                             .peer_idx_b = i,
  32258                         } };
  32259                     } else {
  32260                         opt_first_idx = i;
  32261                         len = arr_like.len;
  32262                     }
  32263 
  32264                     sentinel = null;
  32265 
  32266                     continue;
  32267                 }
  32268 
  32269                 const first_arr_idx = opt_first_arr_idx orelse {
  32270                     if (opt_first_idx == null) {
  32271                         opt_first_idx = i;
  32272                         len = ty.arrayLen(mod);
  32273                         sentinel = ty.sentinel(mod);
  32274                     }
  32275                     opt_first_arr_idx = i;
  32276                     elem_ty = ty.childType(mod);
  32277                     continue;
  32278                 };
  32279 
  32280                 if (ty.arrayLen(mod) != len) return .{ .conflict = .{
  32281                     .peer_idx_a = first_arr_idx,
  32282                     .peer_idx_b = i,
  32283                 } };
  32284 
  32285                 if (!ty.childType(mod).eql(elem_ty, mod)) {
  32286                     return .{ .conflict = .{
  32287                         .peer_idx_a = first_arr_idx,
  32288                         .peer_idx_b = i,
  32289                     } };
  32290                 }
  32291 
  32292                 if (sentinel) |cur_sent| {
  32293                     if (ty.sentinel(mod)) |peer_sent| {
  32294                         if (!peer_sent.eql(cur_sent, elem_ty, mod)) sentinel = null;
  32295                     } else {
  32296                         sentinel = null;
  32297                     }
  32298                 }
  32299             }
  32300 
  32301             // There should always be at least one array or vector peer
  32302             assert(opt_first_arr_idx != null);
  32303 
  32304             return .{ .success = try mod.arrayType(.{
  32305                 .len = len,
  32306                 .child = elem_ty.toIntern(),
  32307                 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none,
  32308             }) };
  32309         },
  32310 
  32311         .vector => {
  32312             var len: ?u64 = null;
  32313             var first_idx: usize = undefined;
  32314             for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| {
  32315                 const ty = ty_ptr.* orelse continue;
  32316 
  32317                 if (!ty.isArrayOrVector(mod)) {
  32318                     // Allow tuples of the correct length
  32319                     const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
  32320                         .peer_idx_a = strat_reason,
  32321                         .peer_idx_b = i,
  32322                     } };
  32323 
  32324                     if (len) |expect_len| {
  32325                         if (arr_like.len != expect_len) return .{ .conflict = .{
  32326                             .peer_idx_a = first_idx,
  32327                             .peer_idx_b = i,
  32328                         } };
  32329                     } else {
  32330                         len = arr_like.len;
  32331                         first_idx = i;
  32332                     }
  32333 
  32334                     // Tuples won't participate in the child type resolution. We'll resolve without
  32335                     // them, and if the tuples have a bad type, we'll get a coercion error later.
  32336                     ty_ptr.* = null;
  32337                     val_ptr.* = null;
  32338 
  32339                     continue;
  32340                 }
  32341 
  32342                 if (len) |expect_len| {
  32343                     if (ty.arrayLen(mod) != expect_len) return .{ .conflict = .{
  32344                         .peer_idx_a = first_idx,
  32345                         .peer_idx_b = i,
  32346                     } };
  32347                 } else {
  32348                     len = ty.arrayLen(mod);
  32349                     first_idx = i;
  32350                 }
  32351 
  32352                 ty_ptr.* = ty.childType(mod);
  32353                 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR
  32354             }
  32355 
  32356             const child_ty = switch (try sema.resolvePeerTypesInner(
  32357                 block,
  32358                 src,
  32359                 peer_tys,
  32360                 peer_vals,
  32361             )) {
  32362                 .success => |ty| ty,
  32363                 else => |result| return result,
  32364             };
  32365 
  32366             return .{ .success = try mod.vectorType(.{
  32367                 .len = @as(u32, @intCast(len.?)),
  32368                 .child = child_ty.toIntern(),
  32369             }) };
  32370         },
  32371 
  32372         .c_ptr => {
  32373             var opt_ptr_info: ?InternPool.Key.PtrType = null;
  32374             var first_idx: usize = undefined;
  32375             for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
  32376                 const ty = opt_ty orelse continue;
  32377                 switch (ty.zigTypeTag(mod)) {
  32378                     .ComptimeInt => continue, // comptime-known integers can always coerce to C pointers
  32379                     .Int => {
  32380                         if (opt_val != null) {
  32381                             // Always allow the coercion for comptime-known ints
  32382                             continue;
  32383                         } else {
  32384                             // Runtime-known, so check if the type is no bigger than a usize
  32385                             const ptr_bits = target.ptrBitWidth();
  32386                             const bits = ty.intInfo(mod).bits;
  32387                             if (bits <= ptr_bits) continue;
  32388                         }
  32389                     },
  32390                     .Null => continue,
  32391                     else => {},
  32392                 }
  32393 
  32394                 if (!ty.isPtrAtRuntime(mod)) return .{ .conflict = .{
  32395                     .peer_idx_a = strat_reason,
  32396                     .peer_idx_b = i,
  32397                 } };
  32398 
  32399                 // Goes through optionals
  32400                 const peer_info = ty.ptrInfo(mod);
  32401 
  32402                 var ptr_info = opt_ptr_info orelse {
  32403                     opt_ptr_info = peer_info;
  32404                     opt_ptr_info.?.flags.size = .C;
  32405                     first_idx = i;
  32406                     continue;
  32407                 };
  32408 
  32409                 // Try peer -> cur, then cur -> peer
  32410                 ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) orelse {
  32411                     return .{ .conflict = .{
  32412                         .peer_idx_a = first_idx,
  32413                         .peer_idx_b = i,
  32414                     } };
  32415                 }).toIntern();
  32416 
  32417                 if (ptr_info.sentinel != .none and peer_info.sentinel != .none) {
  32418                     const peer_sent = try mod.intern_pool.getCoerced(sema.gpa, ptr_info.sentinel, ptr_info.child);
  32419                     const ptr_sent = try mod.intern_pool.getCoerced(sema.gpa, peer_info.sentinel, ptr_info.child);
  32420                     if (ptr_sent == peer_sent) {
  32421                         ptr_info.sentinel = ptr_sent;
  32422                     } else {
  32423                         ptr_info.sentinel = .none;
  32424                     }
  32425                 } else {
  32426                     ptr_info.sentinel = .none;
  32427                 }
  32428 
  32429                 // Note that the align can be always non-zero; Module.ptrType will canonicalize it
  32430                 ptr_info.flags.alignment = Alignment.fromByteUnits(@min(
  32431                     ptr_info.flags.alignment.toByteUnitsOptional() orelse
  32432                         ptr_info.child.toType().abiAlignment(mod),
  32433                     peer_info.flags.alignment.toByteUnitsOptional() orelse
  32434                         peer_info.child.toType().abiAlignment(mod),
  32435                 ));
  32436                 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
  32437                     return .{ .conflict = .{
  32438                         .peer_idx_a = first_idx,
  32439                         .peer_idx_b = i,
  32440                     } };
  32441                 }
  32442 
  32443                 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
  32444                     ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
  32445                 {
  32446                     return .{ .conflict = .{
  32447                         .peer_idx_a = first_idx,
  32448                         .peer_idx_b = i,
  32449                     } };
  32450                 }
  32451 
  32452                 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
  32453                 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
  32454 
  32455                 opt_ptr_info = ptr_info;
  32456             }
  32457             return .{ .success = try mod.ptrType(opt_ptr_info.?) };
  32458         },
  32459 
  32460         .ptr => {
  32461             // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only
  32462             // if there were no actual slices. Else, we want the slice index to report a conflict.
  32463             var opt_slice_idx: ?usize = null;
  32464 
  32465             var opt_ptr_info: ?InternPool.Key.PtrType = null;
  32466             var first_idx: usize = undefined;
  32467             var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error
  32468 
  32469             for (peer_tys, 0..) |opt_ty, i| {
  32470                 const ty = opt_ty orelse continue;
  32471                 const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(mod)) {
  32472                     .Pointer => ty.ptrInfo(mod),
  32473                     .Fn => .{
  32474                         .child = ty.toIntern(),
  32475                         .flags = .{
  32476                             .address_space = target_util.defaultAddressSpace(target, .global_constant),
  32477                         },
  32478                     },
  32479                     else => return .{ .conflict = .{
  32480                         .peer_idx_a = strat_reason,
  32481                         .peer_idx_b = i,
  32482                     } },
  32483                 };
  32484 
  32485                 switch (peer_info.flags.size) {
  32486                     .One, .Many => {},
  32487                     .Slice => opt_slice_idx = i,
  32488                     .C => return .{ .conflict = .{
  32489                         .peer_idx_a = strat_reason,
  32490                         .peer_idx_b = i,
  32491                     } },
  32492                 }
  32493 
  32494                 var ptr_info = opt_ptr_info orelse {
  32495                     opt_ptr_info = peer_info;
  32496                     first_idx = i;
  32497                     continue;
  32498                 };
  32499 
  32500                 other_idx = i;
  32501 
  32502                 // We want to return this in a lot of cases, so alias it here for convenience
  32503                 const generic_err: PeerResolveResult = .{ .conflict = .{
  32504                     .peer_idx_a = first_idx,
  32505                     .peer_idx_b = i,
  32506                 } };
  32507 
  32508                 // Note that the align can be always non-zero; Type.ptr will canonicalize it
  32509                 ptr_info.flags.alignment = Alignment.fromByteUnits(@min(
  32510                     ptr_info.flags.alignment.toByteUnitsOptional() orelse
  32511                         ptr_info.child.toType().abiAlignment(mod),
  32512                     peer_info.flags.alignment.toByteUnitsOptional() orelse
  32513                         peer_info.child.toType().abiAlignment(mod),
  32514                 ));
  32515 
  32516                 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
  32517                     return generic_err;
  32518                 }
  32519 
  32520                 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
  32521                     ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
  32522                 {
  32523                     return generic_err;
  32524                 }
  32525 
  32526                 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
  32527                 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
  32528 
  32529                 const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) {
  32530                     .One => switch (mod.intern_pool.indexToKey(peer_info.child)) {
  32531                         .array_type => |array_type| array_type.sentinel,
  32532                         else => .none,
  32533                     },
  32534                     .Many, .Slice => peer_info.sentinel,
  32535                     .C => unreachable,
  32536                 };
  32537 
  32538                 const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) {
  32539                     .One => switch (mod.intern_pool.indexToKey(ptr_info.child)) {
  32540                         .array_type => |array_type| array_type.sentinel,
  32541                         else => .none,
  32542                     },
  32543                     .Many, .Slice => ptr_info.sentinel,
  32544                     .C => unreachable,
  32545                 };
  32546 
  32547                 // We abstract array handling slightly so that tuple pointers can work like array pointers
  32548                 const peer_pointee_array = sema.typeIsArrayLike(peer_info.child.toType());
  32549                 const cur_pointee_array = sema.typeIsArrayLike(ptr_info.child.toType());
  32550 
  32551                 // This switch is just responsible for deciding the size and pointee (not including
  32552                 // single-pointer array sentinel).
  32553                 good: {
  32554                     switch (peer_info.flags.size) {
  32555                         .One => switch (ptr_info.flags.size) {
  32556                             .One => {
  32557                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| {
  32558                                     ptr_info.child = pointee.toIntern();
  32559                                     break :good;
  32560                                 }
  32561 
  32562                                 const cur_arr = cur_pointee_array orelse return generic_err;
  32563                                 const peer_arr = peer_pointee_array orelse return generic_err;
  32564 
  32565                                 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| {
  32566                                     // *[n:x]T + *[n:y]T = *[n]T
  32567                                     if (cur_arr.len == peer_arr.len) {
  32568                                         ptr_info.child = (try mod.arrayType(.{
  32569                                             .len = cur_arr.len,
  32570                                             .child = elem_ty.toIntern(),
  32571                                         })).toIntern();
  32572                                         break :good;
  32573                                     }
  32574                                     // *[a]T + *[b]T = []T
  32575                                     ptr_info.flags.size = .Slice;
  32576                                     ptr_info.child = elem_ty.toIntern();
  32577                                     break :good;
  32578                                 }
  32579 
  32580                                 if (peer_arr.elem_ty.toIntern() == .noreturn_type) {
  32581                                     // *struct{} + *[a]T = []T
  32582                                     ptr_info.flags.size = .Slice;
  32583                                     ptr_info.child = cur_arr.elem_ty.toIntern();
  32584                                     break :good;
  32585                                 }
  32586 
  32587                                 if (cur_arr.elem_ty.toIntern() == .noreturn_type) {
  32588                                     // *[a]T + *struct{} = []T
  32589                                     ptr_info.flags.size = .Slice;
  32590                                     ptr_info.child = peer_arr.elem_ty.toIntern();
  32591                                     break :good;
  32592                                 }
  32593 
  32594                                 return generic_err;
  32595                             },
  32596                             .Many => {
  32597                                 // Only works for *[n]T + [*]T -> [*]T
  32598                                 const arr = peer_pointee_array orelse return generic_err;
  32599                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), arr.elem_ty)) |pointee| {
  32600                                     ptr_info.child = pointee.toIntern();
  32601                                     break :good;
  32602                                 }
  32603                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  32604                                     // *struct{} + [*]T -> [*]T
  32605                                     break :good;
  32606                                 }
  32607                                 return generic_err;
  32608                             },
  32609                             .Slice => {
  32610                                 // Only works for *[n]T + []T -> []T
  32611                                 const arr = peer_pointee_array orelse return generic_err;
  32612                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), arr.elem_ty)) |pointee| {
  32613                                     ptr_info.child = pointee.toIntern();
  32614                                     break :good;
  32615                                 }
  32616                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  32617                                     // *struct{} + []T -> []T
  32618                                     break :good;
  32619                                 }
  32620                                 return generic_err;
  32621                             },
  32622                             .C => unreachable,
  32623                         },
  32624                         .Many => switch (ptr_info.flags.size) {
  32625                             .One => {
  32626                                 // Only works for [*]T + *[n]T -> [*]T
  32627                                 const arr = cur_pointee_array orelse return generic_err;
  32628                                 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.child.toType())) |pointee| {
  32629                                     ptr_info.flags.size = .Many;
  32630                                     ptr_info.child = pointee.toIntern();
  32631                                     break :good;
  32632                                 }
  32633                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  32634                                     // [*]T + *struct{} -> [*]T
  32635                                     ptr_info.flags.size = .Many;
  32636                                     ptr_info.child = peer_info.child;
  32637                                     break :good;
  32638                                 }
  32639                                 return generic_err;
  32640                             },
  32641                             .Many => {
  32642                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| {
  32643                                     ptr_info.child = pointee.toIntern();
  32644                                     break :good;
  32645                                 }
  32646                                 return generic_err;
  32647                             },
  32648                             .Slice => {
  32649                                 // Only works if no peers are actually slices
  32650                                 if (opt_slice_idx) |slice_idx| {
  32651                                     return .{ .conflict = .{
  32652                                         .peer_idx_a = slice_idx,
  32653                                         .peer_idx_b = i,
  32654                                     } };
  32655                                 }
  32656                                 // Okay, then works for [*]T + "[]T" -> [*]T
  32657                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| {
  32658                                     ptr_info.flags.size = .Many;
  32659                                     ptr_info.child = pointee.toIntern();
  32660                                     break :good;
  32661                                 }
  32662                                 return generic_err;
  32663                             },
  32664                             .C => unreachable,
  32665                         },
  32666                         .Slice => switch (ptr_info.flags.size) {
  32667                             .One => {
  32668                                 // Only works for []T + *[n]T -> []T
  32669                                 const arr = cur_pointee_array orelse return generic_err;
  32670                                 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.child.toType())) |pointee| {
  32671                                     ptr_info.flags.size = .Slice;
  32672                                     ptr_info.child = pointee.toIntern();
  32673                                     break :good;
  32674                                 }
  32675                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  32676                                     // []T + *struct{} -> []T
  32677                                     ptr_info.flags.size = .Slice;
  32678                                     ptr_info.child = peer_info.child;
  32679                                     break :good;
  32680                                 }
  32681                                 return generic_err;
  32682                             },
  32683                             .Many => {
  32684                                 // Impossible! (current peer is an actual slice)
  32685                                 return generic_err;
  32686                             },
  32687                             .Slice => {
  32688                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| {
  32689                                     ptr_info.child = pointee.toIntern();
  32690                                     break :good;
  32691                                 }
  32692                                 return generic_err;
  32693                             },
  32694                             .C => unreachable,
  32695                         },
  32696                         .C => unreachable,
  32697                     }
  32698                 }
  32699 
  32700                 const sentinel_ty = switch (ptr_info.flags.size) {
  32701                     .One => switch (mod.intern_pool.indexToKey(ptr_info.child)) {
  32702                         .array_type => |array_type| array_type.child,
  32703                         else => ptr_info.child,
  32704                     },
  32705                     .Many, .Slice, .C => ptr_info.child,
  32706                 };
  32707 
  32708                 sentinel: {
  32709                     no_sentinel: {
  32710                         if (peer_sentinel == .none) break :no_sentinel;
  32711                         if (cur_sentinel == .none) break :no_sentinel;
  32712                         const peer_sent_coerced = try mod.intern_pool.getCoerced(sema.gpa, peer_sentinel, sentinel_ty);
  32713                         const cur_sent_coerced = try mod.intern_pool.getCoerced(sema.gpa, cur_sentinel, sentinel_ty);
  32714                         if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel;
  32715                         // Sentinels match
  32716                         if (ptr_info.flags.size == .One) switch (mod.intern_pool.indexToKey(ptr_info.child)) {
  32717                             .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{
  32718                                 .len = array_type.len,
  32719                                 .child = array_type.child,
  32720                                 .sentinel = cur_sent_coerced,
  32721                             })).toIntern(),
  32722                             else => unreachable,
  32723                         } else {
  32724                             ptr_info.sentinel = cur_sent_coerced;
  32725                         }
  32726                         break :sentinel;
  32727                     }
  32728                     // Clear existing sentinel
  32729                     ptr_info.sentinel = .none;
  32730                     switch (mod.intern_pool.indexToKey(ptr_info.child)) {
  32731                         .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{
  32732                             .len = array_type.len,
  32733                             .child = array_type.child,
  32734                             .sentinel = .none,
  32735                         })).toIntern(),
  32736                         else => {},
  32737                     }
  32738                 }
  32739 
  32740                 opt_ptr_info = ptr_info;
  32741             }
  32742 
  32743             // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance)
  32744             // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to
  32745             // coerce the empty struct to a specific type, but no peer provided one. We need to
  32746             // detect this case and emit an error.
  32747             const pointee = opt_ptr_info.?.child;
  32748             switch (pointee) {
  32749                 .noreturn_type => return .{ .conflict = .{
  32750                     .peer_idx_a = first_idx,
  32751                     .peer_idx_b = other_idx,
  32752                 } },
  32753                 else => switch (mod.intern_pool.indexToKey(pointee)) {
  32754                     .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{
  32755                         .peer_idx_a = first_idx,
  32756                         .peer_idx_b = other_idx,
  32757                     } },
  32758                     else => {},
  32759                 },
  32760             }
  32761 
  32762             return .{ .success = try mod.ptrType(opt_ptr_info.?) };
  32763         },
  32764 
  32765         .func => {
  32766             var opt_cur_ty: ?Type = null;
  32767             var first_idx: usize = undefined;
  32768             for (peer_tys, 0..) |opt_ty, i| {
  32769                 const ty = opt_ty orelse continue;
  32770                 const cur_ty = opt_cur_ty orelse {
  32771                     opt_cur_ty = ty;
  32772                     first_idx = i;
  32773                     continue;
  32774                 };
  32775                 if (ty.zigTypeTag(mod) != .Fn) return .{ .conflict = .{
  32776                     .peer_idx_a = strat_reason,
  32777                     .peer_idx_b = i,
  32778                 } };
  32779                 // ty -> cur_ty
  32780                 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, target, src, src)) {
  32781                     continue;
  32782                 }
  32783                 // cur_ty -> ty
  32784                 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, target, src, src)) {
  32785                     opt_cur_ty = ty;
  32786                     continue;
  32787                 }
  32788                 return .{ .conflict = .{
  32789                     .peer_idx_a = first_idx,
  32790                     .peer_idx_b = i,
  32791                 } };
  32792             }
  32793             return .{ .success = opt_cur_ty.? };
  32794         },
  32795 
  32796         .enum_or_union => {
  32797             var opt_cur_ty: ?Type = null;
  32798             // The peer index which gave the current type
  32799             var cur_ty_idx: usize = undefined;
  32800 
  32801             for (peer_tys, 0..) |opt_ty, i| {
  32802                 const ty = opt_ty orelse continue;
  32803                 switch (ty.zigTypeTag(mod)) {
  32804                     .EnumLiteral, .Enum, .Union => {},
  32805                     else => return .{ .conflict = .{
  32806                         .peer_idx_a = strat_reason,
  32807                         .peer_idx_b = i,
  32808                     } },
  32809                 }
  32810                 const cur_ty = opt_cur_ty orelse {
  32811                     opt_cur_ty = ty;
  32812                     cur_ty_idx = i;
  32813                     continue;
  32814                 };
  32815 
  32816                 // We want to return this in a lot of cases, so alias it here for convenience
  32817                 const generic_err: PeerResolveResult = .{ .conflict = .{
  32818                     .peer_idx_a = cur_ty_idx,
  32819                     .peer_idx_b = i,
  32820                 } };
  32821 
  32822                 switch (cur_ty.zigTypeTag(mod)) {
  32823                     .EnumLiteral => {
  32824                         opt_cur_ty = ty;
  32825                         cur_ty_idx = i;
  32826                     },
  32827                     .Enum => switch (ty.zigTypeTag(mod)) {
  32828                         .EnumLiteral => {},
  32829                         .Enum => {
  32830                             if (!ty.eql(cur_ty, mod)) return generic_err;
  32831                         },
  32832                         .Union => {
  32833                             const tag_ty = ty.unionTagTypeHypothetical(mod);
  32834                             if (!tag_ty.eql(cur_ty, mod)) return generic_err;
  32835                             opt_cur_ty = ty;
  32836                             cur_ty_idx = i;
  32837                         },
  32838                         else => unreachable,
  32839                     },
  32840                     .Union => switch (ty.zigTypeTag(mod)) {
  32841                         .EnumLiteral => {},
  32842                         .Enum => {
  32843                             const cur_tag_ty = cur_ty.unionTagTypeHypothetical(mod);
  32844                             if (!ty.eql(cur_tag_ty, mod)) return generic_err;
  32845                         },
  32846                         .Union => {
  32847                             if (!ty.eql(cur_ty, mod)) return generic_err;
  32848                         },
  32849                         else => unreachable,
  32850                     },
  32851                     else => unreachable,
  32852                 }
  32853             }
  32854             return .{ .success = opt_cur_ty.? };
  32855         },
  32856 
  32857         .comptime_int => {
  32858             for (peer_tys, 0..) |opt_ty, i| {
  32859                 const ty = opt_ty orelse continue;
  32860                 switch (ty.zigTypeTag(mod)) {
  32861                     .ComptimeInt => {},
  32862                     else => return .{ .conflict = .{
  32863                         .peer_idx_a = strat_reason,
  32864                         .peer_idx_b = i,
  32865                     } },
  32866                 }
  32867             }
  32868             return .{ .success = Type.comptime_int };
  32869         },
  32870 
  32871         .comptime_float => {
  32872             for (peer_tys, 0..) |opt_ty, i| {
  32873                 const ty = opt_ty orelse continue;
  32874                 switch (ty.zigTypeTag(mod)) {
  32875                     .ComptimeInt, .ComptimeFloat => {},
  32876                     else => return .{ .conflict = .{
  32877                         .peer_idx_a = strat_reason,
  32878                         .peer_idx_b = i,
  32879                     } },
  32880                 }
  32881             }
  32882             return .{ .success = Type.comptime_float };
  32883         },
  32884 
  32885         .fixed_int => {
  32886             var idx_unsigned: ?usize = null;
  32887             var idx_signed: ?usize = null;
  32888 
  32889             // TODO: this is for compatibility with legacy behavior. See beneath the loop.
  32890             var any_comptime_known = false;
  32891 
  32892             for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| {
  32893                 const ty = opt_ty orelse continue;
  32894                 const opt_val = ptr_opt_val.*;
  32895 
  32896                 const peer_tag = ty.zigTypeTag(mod);
  32897                 switch (peer_tag) {
  32898                     .ComptimeInt => {
  32899                         // If the value is undefined, we can't refine to a fixed-width int
  32900                         if (opt_val == null or opt_val.?.isUndef(mod)) return .{ .conflict = .{
  32901                             .peer_idx_a = strat_reason,
  32902                             .peer_idx_b = i,
  32903                         } };
  32904                         any_comptime_known = true;
  32905                         ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?);
  32906                         continue;
  32907                     },
  32908                     .Int => {},
  32909                     else => return .{ .conflict = .{
  32910                         .peer_idx_a = strat_reason,
  32911                         .peer_idx_b = i,
  32912                     } },
  32913                 }
  32914 
  32915                 if (opt_val != null) any_comptime_known = true;
  32916 
  32917                 const info = ty.intInfo(mod);
  32918 
  32919                 const idx_ptr = switch (info.signedness) {
  32920                     .unsigned => &idx_unsigned,
  32921                     .signed => &idx_signed,
  32922                 };
  32923 
  32924                 const largest_idx = idx_ptr.* orelse {
  32925                     idx_ptr.* = i;
  32926                     continue;
  32927                 };
  32928 
  32929                 const cur_info = peer_tys[largest_idx].?.intInfo(mod);
  32930                 if (info.bits > cur_info.bits) {
  32931                     idx_ptr.* = i;
  32932                 }
  32933             }
  32934 
  32935             if (idx_signed == null) {
  32936                 return .{ .success = peer_tys[idx_unsigned.?].? };
  32937             }
  32938 
  32939             if (idx_unsigned == null) {
  32940                 return .{ .success = peer_tys[idx_signed.?].? };
  32941             }
  32942 
  32943             const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(mod);
  32944             const signed_info = peer_tys[idx_signed.?].?.intInfo(mod);
  32945             if (signed_info.bits > unsigned_info.bits) {
  32946                 return .{ .success = peer_tys[idx_signed.?].? };
  32947             }
  32948 
  32949             // TODO: this is for compatibility with legacy behavior. Before this version of PTR was
  32950             // implemented, the algorithm very often returned false positives, with the expectation
  32951             // that you'd just hit a coercion error later. One of these was that for integers, the
  32952             // largest type would always be returned, even if it couldn't fit everything. This had
  32953             // an unintentional consequence to semantics, which is that if values were known at
  32954             // comptime, they would be coerced down to the smallest type where possible. This
  32955             // behavior is unintuitive and order-dependent, so in my opinion should be eliminated,
  32956             // but for now we'll retain compatibility.
  32957             if (any_comptime_known) {
  32958                 if (unsigned_info.bits > signed_info.bits) {
  32959                     return .{ .success = peer_tys[idx_unsigned.?].? };
  32960                 }
  32961                 const idx = @min(idx_unsigned.?, idx_signed.?);
  32962                 return .{ .success = peer_tys[idx].? };
  32963             }
  32964 
  32965             return .{ .conflict = .{
  32966                 .peer_idx_a = idx_unsigned.?,
  32967                 .peer_idx_b = idx_signed.?,
  32968             } };
  32969         },
  32970 
  32971         .fixed_float => {
  32972             var opt_cur_ty: ?Type = null;
  32973 
  32974             for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
  32975                 const ty = opt_ty orelse continue;
  32976                 switch (ty.zigTypeTag(mod)) {
  32977                     .ComptimeFloat, .ComptimeInt => {},
  32978                     .Int => {
  32979                         if (opt_val == null) return .{ .conflict = .{
  32980                             .peer_idx_a = strat_reason,
  32981                             .peer_idx_b = i,
  32982                         } };
  32983                     },
  32984                     .Float => {
  32985                         if (opt_cur_ty) |cur_ty| {
  32986                             if (cur_ty.eql(ty, mod)) continue;
  32987                             // Recreate the type so we eliminate any c_longdouble
  32988                             const bits = @max(cur_ty.floatBits(target), ty.floatBits(target));
  32989                             opt_cur_ty = switch (bits) {
  32990                                 16 => Type.f16,
  32991                                 32 => Type.f32,
  32992                                 64 => Type.f64,
  32993                                 80 => Type.f80,
  32994                                 128 => Type.f128,
  32995                                 else => unreachable,
  32996                             };
  32997                         } else {
  32998                             opt_cur_ty = ty;
  32999                         }
  33000                     },
  33001                     else => return .{ .conflict = .{
  33002                         .peer_idx_a = strat_reason,
  33003                         .peer_idx_b = i,
  33004                     } },
  33005                 }
  33006             }
  33007 
  33008             // Note that fixed_float is only chosen if there is at least one fixed-width float peer,
  33009             // so opt_cur_ty must be non-null.
  33010             return .{ .success = opt_cur_ty.? };
  33011         },
  33012 
  33013         .coercible_struct => {
  33014             // First, check that every peer has the same approximate structure (field count and names)
  33015 
  33016             var opt_first_idx: ?usize = null;
  33017             var is_tuple: bool = undefined;
  33018             var field_count: usize = undefined;
  33019             // Only defined for non-tuples.
  33020             var field_names: []InternPool.NullTerminatedString = undefined;
  33021 
  33022             for (peer_tys, 0..) |opt_ty, i| {
  33023                 const ty = opt_ty orelse continue;
  33024 
  33025                 if (!ty.isTupleOrAnonStruct(mod)) {
  33026                     return .{ .conflict = .{
  33027                         .peer_idx_a = strat_reason,
  33028                         .peer_idx_b = i,
  33029                     } };
  33030                 }
  33031 
  33032                 const first_idx = opt_first_idx orelse {
  33033                     opt_first_idx = i;
  33034                     is_tuple = ty.isTuple(mod);
  33035                     field_count = ty.structFieldCount(mod);
  33036                     if (!is_tuple) {
  33037                         const names = mod.intern_pool.indexToKey(ty.toIntern()).anon_struct_type.names;
  33038                         field_names = try sema.arena.dupe(InternPool.NullTerminatedString, names);
  33039                     }
  33040                     continue;
  33041                 };
  33042 
  33043                 if (ty.isTuple(mod) != is_tuple or ty.structFieldCount(mod) != field_count) {
  33044                     return .{ .conflict = .{
  33045                         .peer_idx_a = first_idx,
  33046                         .peer_idx_b = i,
  33047                     } };
  33048                 }
  33049 
  33050                 if (!is_tuple) {
  33051                     for (field_names, 0..) |expected, field_idx| {
  33052                         const actual = ty.structFieldName(field_idx, mod);
  33053                         if (actual == expected) continue;
  33054                         return .{ .conflict = .{
  33055                             .peer_idx_a = first_idx,
  33056                             .peer_idx_b = i,
  33057                         } };
  33058                     }
  33059                 }
  33060             }
  33061 
  33062             assert(opt_first_idx != null);
  33063 
  33064             // Now, we'll recursively resolve the field types
  33065             const field_types = try sema.arena.alloc(InternPool.Index, field_count);
  33066             // Values for `comptime` fields - `.none` used for non-comptime fields
  33067             const field_vals = try sema.arena.alloc(InternPool.Index, field_count);
  33068             const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len);
  33069             const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len);
  33070 
  33071             for (field_types, field_vals, 0..) |*field_ty, *field_val, field_idx| {
  33072                 // Fill buffers with types and values of the field
  33073                 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| {
  33074                     const ty = opt_ty orelse {
  33075                         peer_field_ty.* = null;
  33076                         peer_field_val.* = null;
  33077                         continue;
  33078                     };
  33079                     peer_field_ty.* = ty.structFieldType(field_idx, mod);
  33080                     peer_field_val.* = if (opt_val) |val| try val.fieldValue(mod, field_idx) else null;
  33081                 }
  33082 
  33083                 // Resolve field type recursively
  33084                 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) {
  33085                     .success => |ty| ty.toIntern(),
  33086                     else => |result| {
  33087                         const result_buf = try sema.arena.create(PeerResolveResult);
  33088                         result_buf.* = result;
  33089                         const field_name = if (is_tuple) name: {
  33090                             break :name try std.fmt.allocPrint(sema.arena, "{d}", .{field_idx});
  33091                         } else try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(field_names[field_idx]));
  33092 
  33093                         // The error info needs the field types, but we can't reuse sub_peer_tys
  33094                         // since the recursive call may have clobbered it.
  33095                         const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len);
  33096                         for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| {
  33097                             // Already-resolved types won't be referenced by the error so it's fine
  33098                             // to leave them undefined.
  33099                             const ty = opt_ty orelse continue;
  33100                             peer_field_ty.* = ty.structFieldType(field_idx, mod);
  33101                         }
  33102 
  33103                         return .{ .field_error = .{
  33104                             .field_name = field_name,
  33105                             .field_types = peer_field_tys,
  33106                             .sub_result = result_buf,
  33107                         } };
  33108                     },
  33109                 };
  33110 
  33111                 // Decide if this is a comptime field. If it is comptime in all peers, and the
  33112                 // coerced comptime values are all the same, we say it is comptime, else not.
  33113 
  33114                 var comptime_val: ?Value = null;
  33115                 for (peer_tys) |opt_ty| {
  33116                     const struct_ty = opt_ty orelse continue;
  33117                     const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse {
  33118                         comptime_val = null;
  33119                         break;
  33120                     };
  33121                     const uncoerced_field = try sema.addConstant(uncoerced_field_val);
  33122                     const coerced_inst = sema.coerceExtra(block, field_ty.toType(), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) {
  33123                         // 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
  33124                         error.NotCoercible => {
  33125                             comptime_val = null;
  33126                             break;
  33127                         },
  33128                         else => |e| return e,
  33129                     };
  33130                     const coerced_val = (try sema.resolveMaybeUndefVal(coerced_inst)) orelse continue;
  33131                     const existing = comptime_val orelse {
  33132                         comptime_val = coerced_val;
  33133                         continue;
  33134                     };
  33135                     if (!coerced_val.eql(existing, field_ty.toType(), mod)) {
  33136                         comptime_val = null;
  33137                         break;
  33138                     }
  33139                 }
  33140 
  33141                 field_val.* = if (comptime_val) |v| v.toIntern() else .none;
  33142             }
  33143 
  33144             const final_ty = try mod.intern(.{ .anon_struct_type = .{
  33145                 .types = field_types,
  33146                 .names = if (is_tuple) &.{} else field_names,
  33147                 .values = field_vals,
  33148             } });
  33149 
  33150             return .{ .success = final_ty.toType() };
  33151         },
  33152 
  33153         .exact => {
  33154             var expect_ty: ?Type = null;
  33155             var first_idx: usize = undefined;
  33156             for (peer_tys, 0..) |opt_ty, i| {
  33157                 const ty = opt_ty orelse continue;
  33158                 if (expect_ty) |expect| {
  33159                     if (!ty.eql(expect, mod)) return .{ .conflict = .{
  33160                         .peer_idx_a = first_idx,
  33161                         .peer_idx_b = i,
  33162                     } };
  33163                 } else {
  33164                     expect_ty = ty;
  33165                     first_idx = i;
  33166                 }
  33167             }
  33168             return .{ .success = expect_ty.? };
  33169         },
  33170     }
  33171 }
  33172 
  33173 fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type {
  33174     // e0 -> e1
  33175     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) {
  33176         return e1;
  33177     }
  33178 
  33179     // e1 -> e0
  33180     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) {
  33181         return e0;
  33182     }
  33183 
  33184     return sema.errorSetMerge(e0, e1);
  33185 }
  33186 
  33187 fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type {
  33188     // ty_b -> ty_a
  33189     if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, sema.mod.getTarget(), src, src)) {
  33190         return ty_a;
  33191     }
  33192 
  33193     // ty_a -> ty_b
  33194     if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, sema.mod.getTarget(), src, src)) {
  33195         return ty_b;
  33196     }
  33197 
  33198     return null;
  33199 }
  33200 
  33201 const ArrayLike = struct {
  33202     len: u64,
  33203     /// `noreturn` indicates that this type is `struct{}` so can coerce to anything
  33204     elem_ty: Type,
  33205 };
  33206 fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike {
  33207     const mod = sema.mod;
  33208     return switch (ty.zigTypeTag(mod)) {
  33209         .Array => .{
  33210             .len = ty.arrayLen(mod),
  33211             .elem_ty = ty.childType(mod),
  33212         },
  33213         .Struct => {
  33214             const field_count = ty.structFieldCount(mod);
  33215             if (field_count == 0) return .{
  33216                 .len = 0,
  33217                 .elem_ty = Type.noreturn,
  33218             };
  33219             if (!ty.isTuple(mod)) return null;
  33220             const elem_ty = ty.structFieldType(0, mod);
  33221             for (1..field_count) |i| {
  33222                 if (!ty.structFieldType(i, mod).eql(elem_ty, mod)) {
  33223                     return null;
  33224                 }
  33225             }
  33226             return .{
  33227                 .len = field_count,
  33228                 .elem_ty = elem_ty,
  33229             };
  33230         },
  33231         else => null,
  33232     };
  33233 }
  33234 
  33235 pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void {
  33236     const mod = sema.mod;
  33237     try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.return_type.toType());
  33238 
  33239     if (mod.comp.bin_file.options.error_return_tracing and mod.typeToFunc(fn_ty).?.return_type.toType().isError(mod)) {
  33240         // Ensure the type exists so that backends can assume that.
  33241         _ = try sema.getBuiltinType("StackTrace");
  33242     }
  33243 
  33244     for (0..mod.typeToFunc(fn_ty).?.param_types.len) |i| {
  33245         try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.param_types[i].toType());
  33246     }
  33247 }
  33248 
  33249 /// Make it so that calling hash() and eql() on `val` will not assert due
  33250 /// to a type not having its layout resolved.
  33251 fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value {
  33252     const mod = sema.mod;
  33253     switch (mod.intern_pool.indexToKey(val.toIntern())) {
  33254         .int => |int| switch (int.storage) {
  33255             .u64, .i64, .big_int => return val,
  33256             .lazy_align, .lazy_size => return (try mod.intern(.{ .int = .{
  33257                 .ty = int.ty,
  33258                 .storage = .{ .u64 = (try val.getUnsignedIntAdvanced(mod, sema)).? },
  33259             } })).toValue(),
  33260         },
  33261         .ptr => |ptr| {
  33262             const resolved_len = switch (ptr.len) {
  33263                 .none => .none,
  33264                 else => (try sema.resolveLazyValue(ptr.len.toValue())).toIntern(),
  33265             };
  33266             switch (ptr.addr) {
  33267                 .decl, .mut_decl => return if (resolved_len == ptr.len)
  33268                     val
  33269                 else
  33270                     (try mod.intern(.{ .ptr = .{
  33271                         .ty = ptr.ty,
  33272                         .addr = switch (ptr.addr) {
  33273                             .decl => |decl| .{ .decl = decl },
  33274                             .mut_decl => |mut_decl| .{ .mut_decl = mut_decl },
  33275                             else => unreachable,
  33276                         },
  33277                         .len = resolved_len,
  33278                     } })).toValue(),
  33279                 .comptime_field => |field_val| {
  33280                     const resolved_field_val =
  33281                         (try sema.resolveLazyValue(field_val.toValue())).toIntern();
  33282                     return if (resolved_field_val == field_val and resolved_len == ptr.len)
  33283                         val
  33284                     else
  33285                         (try mod.intern(.{ .ptr = .{
  33286                             .ty = ptr.ty,
  33287                             .addr = .{ .comptime_field = resolved_field_val },
  33288                             .len = resolved_len,
  33289                         } })).toValue();
  33290                 },
  33291                 .int => |int| {
  33292                     const resolved_int = (try sema.resolveLazyValue(int.toValue())).toIntern();
  33293                     return if (resolved_int == int and resolved_len == ptr.len)
  33294                         val
  33295                     else
  33296                         (try mod.intern(.{ .ptr = .{
  33297                             .ty = ptr.ty,
  33298                             .addr = .{ .int = resolved_int },
  33299                             .len = resolved_len,
  33300                         } })).toValue();
  33301                 },
  33302                 .eu_payload, .opt_payload => |base| {
  33303                     const resolved_base = (try sema.resolveLazyValue(base.toValue())).toIntern();
  33304                     return if (resolved_base == base and resolved_len == ptr.len)
  33305                         val
  33306                     else
  33307                         (try mod.intern(.{ .ptr = .{
  33308                             .ty = ptr.ty,
  33309                             .addr = switch (ptr.addr) {
  33310                                 .eu_payload => .{ .eu_payload = resolved_base },
  33311                                 .opt_payload => .{ .opt_payload = resolved_base },
  33312                                 else => unreachable,
  33313                             },
  33314                             .len = ptr.len,
  33315                         } })).toValue();
  33316                 },
  33317                 .elem, .field => |base_index| {
  33318                     const resolved_base = (try sema.resolveLazyValue(base_index.base.toValue())).toIntern();
  33319                     return if (resolved_base == base_index.base and resolved_len == ptr.len)
  33320                         val
  33321                     else
  33322                         (try mod.intern(.{ .ptr = .{
  33323                             .ty = ptr.ty,
  33324                             .addr = switch (ptr.addr) {
  33325                                 .elem => .{ .elem = .{
  33326                                     .base = resolved_base,
  33327                                     .index = base_index.index,
  33328                                 } },
  33329                                 .field => .{ .field = .{
  33330                                     .base = resolved_base,
  33331                                     .index = base_index.index,
  33332                                 } },
  33333                                 else => unreachable,
  33334                             },
  33335                             .len = ptr.len,
  33336                         } })).toValue();
  33337                 },
  33338             }
  33339         },
  33340         .aggregate => |aggregate| switch (aggregate.storage) {
  33341             .bytes => return val,
  33342             .elems => |elems| {
  33343                 var resolved_elems: []InternPool.Index = &.{};
  33344                 for (elems, 0..) |elem, i| {
  33345                     const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern();
  33346                     if (resolved_elems.len == 0 and resolved_elem != elem) {
  33347                         resolved_elems = try sema.arena.alloc(InternPool.Index, elems.len);
  33348                         @memcpy(resolved_elems[0..i], elems[0..i]);
  33349                     }
  33350                     if (resolved_elems.len > 0) resolved_elems[i] = resolved_elem;
  33351                 }
  33352                 return if (resolved_elems.len == 0) val else (try mod.intern(.{ .aggregate = .{
  33353                     .ty = aggregate.ty,
  33354                     .storage = .{ .elems = resolved_elems },
  33355                 } })).toValue();
  33356             },
  33357             .repeated_elem => |elem| {
  33358                 const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern();
  33359                 return if (resolved_elem == elem) val else (try mod.intern(.{ .aggregate = .{
  33360                     .ty = aggregate.ty,
  33361                     .storage = .{ .repeated_elem = resolved_elem },
  33362                 } })).toValue();
  33363             },
  33364         },
  33365         .un => |un| {
  33366             const resolved_tag = (try sema.resolveLazyValue(un.tag.toValue())).toIntern();
  33367             const resolved_val = (try sema.resolveLazyValue(un.val.toValue())).toIntern();
  33368             return if (resolved_tag == un.tag and resolved_val == un.val)
  33369                 val
  33370             else
  33371                 (try mod.intern(.{ .un = .{
  33372                     .ty = un.ty,
  33373                     .tag = resolved_tag,
  33374                     .val = resolved_val,
  33375                 } })).toValue();
  33376         },
  33377         else => return val,
  33378     }
  33379 }
  33380 
  33381 pub fn resolveTypeLayout(sema: *Sema, ty: Type) CompileError!void {
  33382     const mod = sema.mod;
  33383     switch (ty.zigTypeTag(mod)) {
  33384         .Struct => return sema.resolveStructLayout(ty),
  33385         .Union => return sema.resolveUnionLayout(ty),
  33386         .Array => {
  33387             if (ty.arrayLenIncludingSentinel(mod) == 0) return;
  33388             const elem_ty = ty.childType(mod);
  33389             return sema.resolveTypeLayout(elem_ty);
  33390         },
  33391         .Optional => {
  33392             const payload_ty = ty.optionalChild(mod);
  33393             // In case of querying the ABI alignment of this optional, we will ask
  33394             // for hasRuntimeBits() of the payload type, so we need "requires comptime"
  33395             // to be known already before this function returns.
  33396             _ = try sema.typeRequiresComptime(payload_ty);
  33397             return sema.resolveTypeLayout(payload_ty);
  33398         },
  33399         .ErrorUnion => {
  33400             const payload_ty = ty.errorUnionPayload(mod);
  33401             return sema.resolveTypeLayout(payload_ty);
  33402         },
  33403         .Fn => {
  33404             const info = mod.typeToFunc(ty).?;
  33405             if (info.is_generic) {
  33406                 // Resolving of generic function types is deferred to when
  33407                 // the function is instantiated.
  33408                 return;
  33409             }
  33410             for (info.param_types) |param_ty| {
  33411                 try sema.resolveTypeLayout(param_ty.toType());
  33412             }
  33413             try sema.resolveTypeLayout(info.return_type.toType());
  33414         },
  33415         else => {},
  33416     }
  33417 }
  33418 
  33419 fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
  33420     const mod = sema.mod;
  33421     const resolved_ty = try sema.resolveTypeFields(ty);
  33422     if (mod.typeToStruct(resolved_ty)) |struct_obj| {
  33423         switch (struct_obj.status) {
  33424             .none, .have_field_types => {},
  33425             .field_types_wip, .layout_wip => {
  33426                 const msg = try Module.ErrorMsg.create(
  33427                     sema.gpa,
  33428                     struct_obj.srcLoc(mod),
  33429                     "struct '{}' depends on itself",
  33430                     .{ty.fmt(mod)},
  33431                 );
  33432                 return sema.failWithOwnedErrorMsg(msg);
  33433             },
  33434             .have_layout, .fully_resolved_wip, .fully_resolved => return,
  33435         }
  33436         const prev_status = struct_obj.status;
  33437         errdefer if (struct_obj.status == .layout_wip) {
  33438             struct_obj.status = prev_status;
  33439         };
  33440 
  33441         struct_obj.status = .layout_wip;
  33442         for (struct_obj.fields.values(), 0..) |field, i| {
  33443             sema.resolveTypeLayout(field.ty) catch |err| switch (err) {
  33444                 error.AnalysisFail => {
  33445                     const msg = sema.err orelse return err;
  33446                     try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{});
  33447                     return err;
  33448                 },
  33449                 else => return err,
  33450             };
  33451         }
  33452 
  33453         if (struct_obj.layout == .Packed) {
  33454             try semaBackingIntType(mod, struct_obj);
  33455         }
  33456 
  33457         struct_obj.status = .have_layout;
  33458         _ = try sema.resolveTypeRequiresComptime(resolved_ty);
  33459 
  33460         if (struct_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) {
  33461             const msg = try Module.ErrorMsg.create(
  33462                 sema.gpa,
  33463                 struct_obj.srcLoc(mod),
  33464                 "struct layout depends on it having runtime bits",
  33465                 .{},
  33466             );
  33467             return sema.failWithOwnedErrorMsg(msg);
  33468         }
  33469 
  33470         if (struct_obj.layout == .Auto and mod.backendSupportsFeature(.field_reordering)) {
  33471             const optimized_order = try mod.tmp_hack_arena.allocator().alloc(u32, struct_obj.fields.count());
  33472 
  33473             for (struct_obj.fields.values(), 0..) |field, i| {
  33474                 optimized_order[i] = if (try sema.typeHasRuntimeBits(field.ty))
  33475                     @as(u32, @intCast(i))
  33476                 else
  33477                     Module.Struct.omitted_field;
  33478             }
  33479 
  33480             const AlignSortContext = struct {
  33481                 struct_obj: *Module.Struct,
  33482                 sema: *Sema,
  33483 
  33484                 fn lessThan(ctx: @This(), a: u32, b: u32) bool {
  33485                     const m = ctx.sema.mod;
  33486                     if (a == Module.Struct.omitted_field) return false;
  33487                     if (b == Module.Struct.omitted_field) return true;
  33488                     return ctx.struct_obj.fields.values()[a].ty.abiAlignment(m) >
  33489                         ctx.struct_obj.fields.values()[b].ty.abiAlignment(m);
  33490                 }
  33491             };
  33492             mem.sort(u32, optimized_order, AlignSortContext{
  33493                 .struct_obj = struct_obj,
  33494                 .sema = sema,
  33495             }, AlignSortContext.lessThan);
  33496             struct_obj.optimized_order = optimized_order.ptr;
  33497         }
  33498     }
  33499     // otherwise it's a tuple; no need to resolve anything
  33500 }
  33501 
  33502 fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!void {
  33503     const gpa = mod.gpa;
  33504 
  33505     var fields_bit_sum: u64 = 0;
  33506     for (struct_obj.fields.values()) |field| {
  33507         fields_bit_sum += field.ty.bitSize(mod);
  33508     }
  33509 
  33510     const decl_index = struct_obj.owner_decl;
  33511     const decl = mod.declPtr(decl_index);
  33512 
  33513     const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir;
  33514     const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
  33515     assert(extended.opcode == .struct_decl);
  33516     const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small));
  33517 
  33518     if (small.has_backing_int) {
  33519         var extra_index: usize = extended.operand;
  33520         extra_index += @intFromBool(small.has_src_node);
  33521         extra_index += @intFromBool(small.has_fields_len);
  33522         extra_index += @intFromBool(small.has_decls_len);
  33523 
  33524         const backing_int_body_len = zir.extra[extra_index];
  33525         extra_index += 1;
  33526 
  33527         var analysis_arena = std.heap.ArenaAllocator.init(gpa);
  33528         defer analysis_arena.deinit();
  33529 
  33530         var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
  33531         defer comptime_mutable_decls.deinit();
  33532 
  33533         var sema: Sema = .{
  33534             .mod = mod,
  33535             .gpa = gpa,
  33536             .arena = analysis_arena.allocator(),
  33537             .code = zir,
  33538             .owner_decl = decl,
  33539             .owner_decl_index = decl_index,
  33540             .func = null,
  33541             .func_index = .none,
  33542             .fn_ret_ty = Type.void,
  33543             .owner_func = null,
  33544             .owner_func_index = .none,
  33545             .comptime_mutable_decls = &comptime_mutable_decls,
  33546         };
  33547         defer sema.deinit();
  33548 
  33549         var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope);
  33550         defer wip_captures.deinit();
  33551 
  33552         var block: Block = .{
  33553             .parent = null,
  33554             .sema = &sema,
  33555             .src_decl = decl_index,
  33556             .namespace = struct_obj.namespace,
  33557             .wip_capture_scope = wip_captures.scope,
  33558             .instructions = .{},
  33559             .inlining = null,
  33560             .is_comptime = true,
  33561         };
  33562         defer {
  33563             assert(block.instructions.items.len == 0);
  33564             block.params.deinit(gpa);
  33565         }
  33566 
  33567         const backing_int_src: LazySrcLoc = .{ .node_offset_container_tag = 0 };
  33568         const backing_int_ty = blk: {
  33569             if (backing_int_body_len == 0) {
  33570                 const backing_int_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  33571                 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref);
  33572             } else {
  33573                 const body = zir.extra[extra_index..][0..backing_int_body_len];
  33574                 const ty_ref = try sema.resolveBody(&block, body, struct_obj.zir_index);
  33575                 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref);
  33576             }
  33577         };
  33578 
  33579         try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum);
  33580         struct_obj.backing_int_ty = backing_int_ty;
  33581         try wip_captures.finalize();
  33582         for (comptime_mutable_decls.items) |ct_decl_index| {
  33583             const ct_decl = mod.declPtr(ct_decl_index);
  33584             try ct_decl.intern(mod);
  33585         }
  33586     } else {
  33587         if (fields_bit_sum > std.math.maxInt(u16)) {
  33588             var sema: Sema = .{
  33589                 .mod = mod,
  33590                 .gpa = gpa,
  33591                 .arena = undefined,
  33592                 .code = zir,
  33593                 .owner_decl = decl,
  33594                 .owner_decl_index = decl_index,
  33595                 .func = null,
  33596                 .func_index = .none,
  33597                 .fn_ret_ty = Type.void,
  33598                 .owner_func = null,
  33599                 .owner_func_index = .none,
  33600                 .comptime_mutable_decls = undefined,
  33601             };
  33602             defer sema.deinit();
  33603 
  33604             var block: Block = .{
  33605                 .parent = null,
  33606                 .sema = &sema,
  33607                 .src_decl = decl_index,
  33608                 .namespace = struct_obj.namespace,
  33609                 .wip_capture_scope = undefined,
  33610                 .instructions = .{},
  33611                 .inlining = null,
  33612                 .is_comptime = true,
  33613             };
  33614             return sema.fail(&block, LazySrcLoc.nodeOffset(0), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum});
  33615         }
  33616         struct_obj.backing_int_ty = try mod.intType(.unsigned, @as(u16, @intCast(fields_bit_sum)));
  33617     }
  33618 }
  33619 
  33620 fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void {
  33621     const mod = sema.mod;
  33622 
  33623     if (!backing_int_ty.isInt(mod)) {
  33624         return sema.fail(block, src, "expected backing integer type, found '{}'", .{backing_int_ty.fmt(sema.mod)});
  33625     }
  33626     if (backing_int_ty.bitSize(mod) != fields_bit_sum) {
  33627         return sema.fail(
  33628             block,
  33629             src,
  33630             "backing integer type '{}' has bit size {} but the struct fields have a total bit size of {}",
  33631             .{ backing_int_ty.fmt(sema.mod), backing_int_ty.bitSize(mod), fields_bit_sum },
  33632         );
  33633     }
  33634 }
  33635 
  33636 fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  33637     const mod = sema.mod;
  33638     if (!ty.isIndexable(mod)) {
  33639         const msg = msg: {
  33640             const msg = try sema.errMsg(block, src, "type '{}' does not support indexing", .{ty.fmt(sema.mod)});
  33641             errdefer msg.destroy(sema.gpa);
  33642             try sema.errNote(block, src, msg, "operand must be an array, slice, tuple, or vector", .{});
  33643             break :msg msg;
  33644         };
  33645         return sema.failWithOwnedErrorMsg(msg);
  33646     }
  33647 }
  33648 
  33649 fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  33650     const mod = sema.mod;
  33651     if (ty.zigTypeTag(mod) == .Pointer) {
  33652         switch (ty.ptrSize(mod)) {
  33653             .Slice, .Many, .C => return,
  33654             .One => {
  33655                 const elem_ty = ty.childType(mod);
  33656                 if (elem_ty.zigTypeTag(mod) == .Array) return;
  33657                 // TODO https://github.com/ziglang/zig/issues/15479
  33658                 // if (elem_ty.isTuple()) return;
  33659             },
  33660         }
  33661     }
  33662     const msg = msg: {
  33663         const msg = try sema.errMsg(block, src, "type '{}' is not an indexable pointer", .{ty.fmt(sema.mod)});
  33664         errdefer msg.destroy(sema.gpa);
  33665         try sema.errNote(block, src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{});
  33666         break :msg msg;
  33667     };
  33668     return sema.failWithOwnedErrorMsg(msg);
  33669 }
  33670 
  33671 fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
  33672     const mod = sema.mod;
  33673     const resolved_ty = try sema.resolveTypeFields(ty);
  33674     const union_obj = mod.typeToUnion(resolved_ty).?;
  33675     switch (union_obj.status) {
  33676         .none, .have_field_types => {},
  33677         .field_types_wip, .layout_wip => {
  33678             const msg = try Module.ErrorMsg.create(
  33679                 sema.gpa,
  33680                 union_obj.srcLoc(sema.mod),
  33681                 "union '{}' depends on itself",
  33682                 .{ty.fmt(sema.mod)},
  33683             );
  33684             return sema.failWithOwnedErrorMsg(msg);
  33685         },
  33686         .have_layout, .fully_resolved_wip, .fully_resolved => return,
  33687     }
  33688     const prev_status = union_obj.status;
  33689     errdefer if (union_obj.status == .layout_wip) {
  33690         union_obj.status = prev_status;
  33691     };
  33692 
  33693     union_obj.status = .layout_wip;
  33694     for (union_obj.fields.values(), 0..) |field, i| {
  33695         sema.resolveTypeLayout(field.ty) catch |err| switch (err) {
  33696             error.AnalysisFail => {
  33697                 const msg = sema.err orelse return err;
  33698                 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{});
  33699                 return err;
  33700             },
  33701             else => return err,
  33702         };
  33703     }
  33704     union_obj.status = .have_layout;
  33705     _ = try sema.resolveTypeRequiresComptime(resolved_ty);
  33706 
  33707     if (union_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) {
  33708         const msg = try Module.ErrorMsg.create(
  33709             sema.gpa,
  33710             union_obj.srcLoc(sema.mod),
  33711             "union layout depends on it having runtime bits",
  33712             .{},
  33713         );
  33714         return sema.failWithOwnedErrorMsg(msg);
  33715     }
  33716 }
  33717 
  33718 // In case of querying the ABI alignment of this struct, we will ask
  33719 // for hasRuntimeBits() of each field, so we need "requires comptime"
  33720 // to be known already before this function returns.
  33721 pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
  33722     const mod = sema.mod;
  33723 
  33724     return switch (ty.toIntern()) {
  33725         .empty_struct_type => false,
  33726         else => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  33727             .int_type => false,
  33728             .ptr_type => |ptr_type| {
  33729                 const child_ty = ptr_type.child.toType();
  33730                 if (child_ty.zigTypeTag(mod) == .Fn) {
  33731                     return mod.typeToFunc(child_ty).?.is_generic;
  33732                 } else {
  33733                     return sema.resolveTypeRequiresComptime(child_ty);
  33734                 }
  33735             },
  33736             .anyframe_type => |child| {
  33737                 if (child == .none) return false;
  33738                 return sema.resolveTypeRequiresComptime(child.toType());
  33739             },
  33740             .array_type => |array_type| return sema.resolveTypeRequiresComptime(array_type.child.toType()),
  33741             .vector_type => |vector_type| return sema.resolveTypeRequiresComptime(vector_type.child.toType()),
  33742             .opt_type => |child| return sema.resolveTypeRequiresComptime(child.toType()),
  33743             .error_union_type => |error_union_type| return sema.resolveTypeRequiresComptime(error_union_type.payload_type.toType()),
  33744             .error_set_type, .inferred_error_set_type => false,
  33745 
  33746             .func_type => true,
  33747 
  33748             .simple_type => |t| switch (t) {
  33749                 .f16,
  33750                 .f32,
  33751                 .f64,
  33752                 .f80,
  33753                 .f128,
  33754                 .usize,
  33755                 .isize,
  33756                 .c_char,
  33757                 .c_short,
  33758                 .c_ushort,
  33759                 .c_int,
  33760                 .c_uint,
  33761                 .c_long,
  33762                 .c_ulong,
  33763                 .c_longlong,
  33764                 .c_ulonglong,
  33765                 .c_longdouble,
  33766                 .anyopaque,
  33767                 .bool,
  33768                 .void,
  33769                 .anyerror,
  33770                 .noreturn,
  33771                 .generic_poison,
  33772                 .atomic_order,
  33773                 .atomic_rmw_op,
  33774                 .calling_convention,
  33775                 .address_space,
  33776                 .float_mode,
  33777                 .reduce_op,
  33778                 .call_modifier,
  33779                 .prefetch_options,
  33780                 .export_options,
  33781                 .extern_options,
  33782                 => false,
  33783 
  33784                 .type,
  33785                 .comptime_int,
  33786                 .comptime_float,
  33787                 .null,
  33788                 .undefined,
  33789                 .enum_literal,
  33790                 .type_info,
  33791                 => true,
  33792             },
  33793             .struct_type => |struct_type| {
  33794                 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false;
  33795                 switch (struct_obj.requires_comptime) {
  33796                     .no, .wip => return false,
  33797                     .yes => return true,
  33798                     .unknown => {
  33799                         var requires_comptime = false;
  33800                         struct_obj.requires_comptime = .wip;
  33801                         for (struct_obj.fields.values()) |field| {
  33802                             if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
  33803                         }
  33804                         if (requires_comptime) {
  33805                             struct_obj.requires_comptime = .yes;
  33806                         } else {
  33807                             struct_obj.requires_comptime = .no;
  33808                         }
  33809                         return requires_comptime;
  33810                     },
  33811                 }
  33812             },
  33813 
  33814             .anon_struct_type => |tuple| {
  33815                 for (tuple.types, tuple.values) |field_ty, field_val| {
  33816                     const have_comptime_val = field_val != .none;
  33817                     if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty.toType())) {
  33818                         return true;
  33819                     }
  33820                 }
  33821                 return false;
  33822             },
  33823 
  33824             .union_type => |union_type| {
  33825                 const union_obj = mod.unionPtr(union_type.index);
  33826                 switch (union_obj.requires_comptime) {
  33827                     .no, .wip => return false,
  33828                     .yes => return true,
  33829                     .unknown => {
  33830                         var requires_comptime = false;
  33831                         union_obj.requires_comptime = .wip;
  33832                         for (union_obj.fields.values()) |field| {
  33833                             if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
  33834                         }
  33835                         if (requires_comptime) {
  33836                             union_obj.requires_comptime = .yes;
  33837                         } else {
  33838                             union_obj.requires_comptime = .no;
  33839                         }
  33840                         return requires_comptime;
  33841                     },
  33842                 }
  33843             },
  33844 
  33845             .opaque_type => false,
  33846 
  33847             .enum_type => |enum_type| try sema.resolveTypeRequiresComptime(enum_type.tag_ty.toType()),
  33848 
  33849             // values, not types
  33850             .undef,
  33851             .runtime_value,
  33852             .simple_value,
  33853             .variable,
  33854             .extern_func,
  33855             .func,
  33856             .int,
  33857             .err,
  33858             .error_union,
  33859             .enum_literal,
  33860             .enum_tag,
  33861             .empty_enum_value,
  33862             .float,
  33863             .ptr,
  33864             .opt,
  33865             .aggregate,
  33866             .un,
  33867             // memoization, not types
  33868             .memoized_call,
  33869             => unreachable,
  33870         },
  33871     };
  33872 }
  33873 
  33874 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to
  33875 /// be resolved.
  33876 pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void {
  33877     const mod = sema.mod;
  33878     switch (ty.zigTypeTag(mod)) {
  33879         .Pointer => {
  33880             const child_ty = try sema.resolveTypeFields(ty.childType(mod));
  33881             return sema.resolveTypeFully(child_ty);
  33882         },
  33883         .Struct => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  33884             .struct_type => return sema.resolveStructFully(ty),
  33885             .anon_struct_type => |tuple| {
  33886                 for (tuple.types) |field_ty| {
  33887                     try sema.resolveTypeFully(field_ty.toType());
  33888                 }
  33889             },
  33890             else => {},
  33891         },
  33892         .Union => return sema.resolveUnionFully(ty),
  33893         .Array => return sema.resolveTypeFully(ty.childType(mod)),
  33894         .Optional => {
  33895             return sema.resolveTypeFully(ty.optionalChild(mod));
  33896         },
  33897         .ErrorUnion => return sema.resolveTypeFully(ty.errorUnionPayload(mod)),
  33898         .Fn => {
  33899             const info = mod.typeToFunc(ty).?;
  33900             if (info.is_generic) {
  33901                 // Resolving of generic function types is deferred to when
  33902                 // the function is instantiated.
  33903                 return;
  33904             }
  33905             for (info.param_types) |param_ty| {
  33906                 try sema.resolveTypeFully(param_ty.toType());
  33907             }
  33908             try sema.resolveTypeFully(info.return_type.toType());
  33909         },
  33910         else => {},
  33911     }
  33912 }
  33913 
  33914 fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void {
  33915     try sema.resolveStructLayout(ty);
  33916 
  33917     const mod = sema.mod;
  33918     const resolved_ty = try sema.resolveTypeFields(ty);
  33919     const struct_obj = mod.typeToStruct(resolved_ty).?;
  33920 
  33921     switch (struct_obj.status) {
  33922         .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
  33923         .fully_resolved_wip, .fully_resolved => return,
  33924     }
  33925 
  33926     {
  33927         // After we have resolve struct layout we have to go over the fields again to
  33928         // make sure pointer fields get their child types resolved as well.
  33929         // See also similar code for unions.
  33930         const prev_status = struct_obj.status;
  33931         errdefer struct_obj.status = prev_status;
  33932 
  33933         struct_obj.status = .fully_resolved_wip;
  33934         for (struct_obj.fields.values()) |field| {
  33935             try sema.resolveTypeFully(field.ty);
  33936         }
  33937         struct_obj.status = .fully_resolved;
  33938     }
  33939 
  33940     // And let's not forget comptime-only status.
  33941     _ = try sema.typeRequiresComptime(ty);
  33942 }
  33943 
  33944 fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void {
  33945     try sema.resolveUnionLayout(ty);
  33946 
  33947     const mod = sema.mod;
  33948     const resolved_ty = try sema.resolveTypeFields(ty);
  33949     const union_obj = mod.typeToUnion(resolved_ty).?;
  33950     switch (union_obj.status) {
  33951         .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
  33952         .fully_resolved_wip, .fully_resolved => return,
  33953     }
  33954 
  33955     {
  33956         // After we have resolve union layout we have to go over the fields again to
  33957         // make sure pointer fields get their child types resolved as well.
  33958         // See also similar code for structs.
  33959         const prev_status = union_obj.status;
  33960         errdefer union_obj.status = prev_status;
  33961 
  33962         union_obj.status = .fully_resolved_wip;
  33963         for (union_obj.fields.values()) |field| {
  33964             try sema.resolveTypeFully(field.ty);
  33965         }
  33966         union_obj.status = .fully_resolved;
  33967     }
  33968 
  33969     // And let's not forget comptime-only status.
  33970     _ = try sema.typeRequiresComptime(ty);
  33971 }
  33972 
  33973 pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type {
  33974     const mod = sema.mod;
  33975 
  33976     switch (ty.toIntern()) {
  33977         .var_args_param_type => unreachable,
  33978 
  33979         .none => unreachable,
  33980 
  33981         .u0_type,
  33982         .i0_type,
  33983         .u1_type,
  33984         .u8_type,
  33985         .i8_type,
  33986         .u16_type,
  33987         .i16_type,
  33988         .u29_type,
  33989         .u32_type,
  33990         .i32_type,
  33991         .u64_type,
  33992         .i64_type,
  33993         .u80_type,
  33994         .u128_type,
  33995         .i128_type,
  33996         .usize_type,
  33997         .isize_type,
  33998         .c_char_type,
  33999         .c_short_type,
  34000         .c_ushort_type,
  34001         .c_int_type,
  34002         .c_uint_type,
  34003         .c_long_type,
  34004         .c_ulong_type,
  34005         .c_longlong_type,
  34006         .c_ulonglong_type,
  34007         .c_longdouble_type,
  34008         .f16_type,
  34009         .f32_type,
  34010         .f64_type,
  34011         .f80_type,
  34012         .f128_type,
  34013         .anyopaque_type,
  34014         .bool_type,
  34015         .void_type,
  34016         .type_type,
  34017         .anyerror_type,
  34018         .comptime_int_type,
  34019         .comptime_float_type,
  34020         .noreturn_type,
  34021         .anyframe_type,
  34022         .null_type,
  34023         .undefined_type,
  34024         .enum_literal_type,
  34025         .manyptr_u8_type,
  34026         .manyptr_const_u8_type,
  34027         .manyptr_const_u8_sentinel_0_type,
  34028         .single_const_pointer_to_comptime_int_type,
  34029         .slice_const_u8_type,
  34030         .slice_const_u8_sentinel_0_type,
  34031         .optional_noreturn_type,
  34032         .anyerror_void_error_union_type,
  34033         .generic_poison_type,
  34034         .empty_struct_type,
  34035         => return ty,
  34036 
  34037         .undef => unreachable,
  34038         .zero => unreachable,
  34039         .zero_usize => unreachable,
  34040         .zero_u8 => unreachable,
  34041         .one => unreachable,
  34042         .one_usize => unreachable,
  34043         .one_u8 => unreachable,
  34044         .four_u8 => unreachable,
  34045         .negative_one => unreachable,
  34046         .calling_convention_c => unreachable,
  34047         .calling_convention_inline => unreachable,
  34048         .void_value => unreachable,
  34049         .unreachable_value => unreachable,
  34050         .null_value => unreachable,
  34051         .bool_true => unreachable,
  34052         .bool_false => unreachable,
  34053         .empty_struct => unreachable,
  34054         .generic_poison => unreachable,
  34055 
  34056         .type_info_type => return sema.getBuiltinType("Type"),
  34057         .extern_options_type => return sema.getBuiltinType("ExternOptions"),
  34058         .export_options_type => return sema.getBuiltinType("ExportOptions"),
  34059         .atomic_order_type => return sema.getBuiltinType("AtomicOrder"),
  34060         .atomic_rmw_op_type => return sema.getBuiltinType("AtomicRmwOp"),
  34061         .calling_convention_type => return sema.getBuiltinType("CallingConvention"),
  34062         .address_space_type => return sema.getBuiltinType("AddressSpace"),
  34063         .float_mode_type => return sema.getBuiltinType("FloatMode"),
  34064         .reduce_op_type => return sema.getBuiltinType("ReduceOp"),
  34065         .call_modifier_type => return sema.getBuiltinType("CallModifier"),
  34066         .prefetch_options_type => return sema.getBuiltinType("PrefetchOptions"),
  34067 
  34068         _ => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) {
  34069             .type_struct,
  34070             .type_struct_ns,
  34071             .type_union_tagged,
  34072             .type_union_untagged,
  34073             .type_union_safety,
  34074             => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  34075                 .struct_type => |struct_type| {
  34076                     const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return ty;
  34077                     try sema.resolveTypeFieldsStruct(ty, struct_obj);
  34078                     return ty;
  34079                 },
  34080                 .union_type => |union_type| {
  34081                     const union_obj = mod.unionPtr(union_type.index);
  34082                     try sema.resolveTypeFieldsUnion(ty, union_obj);
  34083                     return ty;
  34084                 },
  34085                 else => unreachable,
  34086             },
  34087             else => return ty,
  34088         },
  34089     }
  34090 }
  34091 
  34092 fn resolveTypeFieldsStruct(
  34093     sema: *Sema,
  34094     ty: Type,
  34095     struct_obj: *Module.Struct,
  34096 ) CompileError!void {
  34097     switch (sema.mod.declPtr(struct_obj.owner_decl).analysis) {
  34098         .file_failure,
  34099         .dependency_failure,
  34100         .sema_failure,
  34101         .sema_failure_retryable,
  34102         => {
  34103             sema.owner_decl.analysis = .dependency_failure;
  34104             sema.owner_decl.generation = sema.mod.generation;
  34105             return error.AnalysisFail;
  34106         },
  34107         else => {},
  34108     }
  34109     switch (struct_obj.status) {
  34110         .none => {},
  34111         .field_types_wip => {
  34112             const msg = try Module.ErrorMsg.create(
  34113                 sema.gpa,
  34114                 struct_obj.srcLoc(sema.mod),
  34115                 "struct '{}' depends on itself",
  34116                 .{ty.fmt(sema.mod)},
  34117             );
  34118             return sema.failWithOwnedErrorMsg(msg);
  34119         },
  34120         .have_field_types,
  34121         .have_layout,
  34122         .layout_wip,
  34123         .fully_resolved_wip,
  34124         .fully_resolved,
  34125         => return,
  34126     }
  34127 
  34128     struct_obj.status = .field_types_wip;
  34129     errdefer struct_obj.status = .none;
  34130     try semaStructFields(sema.mod, struct_obj);
  34131 }
  34132 
  34133 fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_obj: *Module.Union) CompileError!void {
  34134     switch (sema.mod.declPtr(union_obj.owner_decl).analysis) {
  34135         .file_failure,
  34136         .dependency_failure,
  34137         .sema_failure,
  34138         .sema_failure_retryable,
  34139         => {
  34140             sema.owner_decl.analysis = .dependency_failure;
  34141             sema.owner_decl.generation = sema.mod.generation;
  34142             return error.AnalysisFail;
  34143         },
  34144         else => {},
  34145     }
  34146     switch (union_obj.status) {
  34147         .none => {},
  34148         .field_types_wip => {
  34149             const msg = try Module.ErrorMsg.create(
  34150                 sema.gpa,
  34151                 union_obj.srcLoc(sema.mod),
  34152                 "union '{}' depends on itself",
  34153                 .{ty.fmt(sema.mod)},
  34154             );
  34155             return sema.failWithOwnedErrorMsg(msg);
  34156         },
  34157         .have_field_types,
  34158         .have_layout,
  34159         .layout_wip,
  34160         .fully_resolved_wip,
  34161         .fully_resolved,
  34162         => return,
  34163     }
  34164 
  34165     union_obj.status = .field_types_wip;
  34166     errdefer union_obj.status = .none;
  34167     try semaUnionFields(sema.mod, union_obj);
  34168     union_obj.status = .have_field_types;
  34169 }
  34170 
  34171 fn resolveInferredErrorSet(
  34172     sema: *Sema,
  34173     block: *Block,
  34174     src: LazySrcLoc,
  34175     ies_index: Module.Fn.InferredErrorSet.Index,
  34176 ) CompileError!void {
  34177     const mod = sema.mod;
  34178     const ies = mod.inferredErrorSetPtr(ies_index);
  34179 
  34180     if (ies.is_resolved) return;
  34181 
  34182     const func = mod.funcPtr(ies.func);
  34183     if (func.state == .in_progress) {
  34184         return sema.fail(block, src, "unable to resolve inferred error set", .{});
  34185     }
  34186 
  34187     // In order to ensure that all dependencies are properly added to the set, we
  34188     // need to ensure the function body is analyzed of the inferred error set.
  34189     // However, in the case of comptime/inline function calls with inferred error sets,
  34190     // each call gets a new InferredErrorSet object, which contains the same
  34191     // `Module.Fn.Index`. Not only is the function not relevant to the inferred error set
  34192     // in this case, it may be a generic function which would cause an assertion failure
  34193     // if we called `ensureFuncBodyAnalyzed` on it here.
  34194     const ies_func_owner_decl = mod.declPtr(func.owner_decl);
  34195     const ies_func_info = mod.typeToFunc(ies_func_owner_decl.ty).?;
  34196     // if ies declared by a inline function with generic return type, the return_type should be generic_poison,
  34197     // because inline function does not create a new declaration, and the ies has been filled with analyzeCall,
  34198     // so here we can simply skip this case.
  34199     if (ies_func_info.return_type == .generic_poison_type) {
  34200         assert(ies_func_info.cc == .Inline);
  34201     } else if (mod.typeToInferredErrorSet(ies_func_info.return_type.toType().errorUnionSet(mod)).? == ies) {
  34202         if (ies_func_info.is_generic) {
  34203             const msg = msg: {
  34204                 const msg = try sema.errMsg(block, src, "unable to resolve inferred error set of generic function", .{});
  34205                 errdefer msg.destroy(sema.gpa);
  34206 
  34207                 try sema.mod.errNoteNonLazy(ies_func_owner_decl.srcLoc(mod), msg, "generic function declared here", .{});
  34208                 break :msg msg;
  34209             };
  34210             return sema.failWithOwnedErrorMsg(msg);
  34211         }
  34212         // In this case we are dealing with the actual InferredErrorSet object that
  34213         // corresponds to the function, not one created to track an inline/comptime call.
  34214         try sema.ensureFuncBodyAnalyzed(ies.func);
  34215     }
  34216 
  34217     ies.is_resolved = true;
  34218 
  34219     for (ies.inferred_error_sets.keys()) |other_ies_index| {
  34220         if (ies_index == other_ies_index) continue;
  34221         try sema.resolveInferredErrorSet(block, src, other_ies_index);
  34222 
  34223         const other_ies = mod.inferredErrorSetPtr(other_ies_index);
  34224         for (other_ies.errors.keys()) |key| {
  34225             try ies.errors.put(sema.gpa, key, {});
  34226         }
  34227         if (other_ies.is_anyerror)
  34228             ies.is_anyerror = true;
  34229     }
  34230 }
  34231 
  34232 fn resolveInferredErrorSetTy(
  34233     sema: *Sema,
  34234     block: *Block,
  34235     src: LazySrcLoc,
  34236     ty: Type,
  34237 ) CompileError!void {
  34238     const mod = sema.mod;
  34239     if (mod.typeToInferredErrorSetIndex(ty).unwrap()) |ies_index| {
  34240         try sema.resolveInferredErrorSet(block, src, ies_index);
  34241     }
  34242 }
  34243 
  34244 fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void {
  34245     const gpa = mod.gpa;
  34246     const ip = &mod.intern_pool;
  34247     const decl_index = struct_obj.owner_decl;
  34248     const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir;
  34249     const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
  34250     assert(extended.opcode == .struct_decl);
  34251     const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small));
  34252     var extra_index: usize = extended.operand;
  34253 
  34254     const src = LazySrcLoc.nodeOffset(0);
  34255     extra_index += @intFromBool(small.has_src_node);
  34256 
  34257     const fields_len = if (small.has_fields_len) blk: {
  34258         const fields_len = zir.extra[extra_index];
  34259         extra_index += 1;
  34260         break :blk fields_len;
  34261     } else 0;
  34262 
  34263     const decls_len = if (small.has_decls_len) decls_len: {
  34264         const decls_len = zir.extra[extra_index];
  34265         extra_index += 1;
  34266         break :decls_len decls_len;
  34267     } else 0;
  34268 
  34269     // The backing integer cannot be handled until `resolveStructLayout()`.
  34270     if (small.has_backing_int) {
  34271         const backing_int_body_len = zir.extra[extra_index];
  34272         extra_index += 1; // backing_int_body_len
  34273         if (backing_int_body_len == 0) {
  34274             extra_index += 1; // backing_int_ref
  34275         } else {
  34276             extra_index += backing_int_body_len; // backing_int_body_inst
  34277         }
  34278     }
  34279 
  34280     // Skip over decls.
  34281     var decls_it = zir.declIteratorInner(extra_index, decls_len);
  34282     while (decls_it.next()) |_| {}
  34283     extra_index = decls_it.extra_index;
  34284 
  34285     if (fields_len == 0) {
  34286         if (struct_obj.layout == .Packed) {
  34287             try semaBackingIntType(mod, struct_obj);
  34288         }
  34289         struct_obj.status = .have_layout;
  34290         return;
  34291     }
  34292 
  34293     const decl = mod.declPtr(decl_index);
  34294 
  34295     var analysis_arena = std.heap.ArenaAllocator.init(gpa);
  34296     defer analysis_arena.deinit();
  34297 
  34298     var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
  34299     defer comptime_mutable_decls.deinit();
  34300 
  34301     var sema: Sema = .{
  34302         .mod = mod,
  34303         .gpa = gpa,
  34304         .arena = analysis_arena.allocator(),
  34305         .code = zir,
  34306         .owner_decl = decl,
  34307         .owner_decl_index = decl_index,
  34308         .func = null,
  34309         .func_index = .none,
  34310         .fn_ret_ty = Type.void,
  34311         .owner_func = null,
  34312         .owner_func_index = .none,
  34313         .comptime_mutable_decls = &comptime_mutable_decls,
  34314     };
  34315     defer sema.deinit();
  34316 
  34317     var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope);
  34318     defer wip_captures.deinit();
  34319 
  34320     var block_scope: Block = .{
  34321         .parent = null,
  34322         .sema = &sema,
  34323         .src_decl = decl_index,
  34324         .namespace = struct_obj.namespace,
  34325         .wip_capture_scope = wip_captures.scope,
  34326         .instructions = .{},
  34327         .inlining = null,
  34328         .is_comptime = true,
  34329     };
  34330     defer {
  34331         assert(block_scope.instructions.items.len == 0);
  34332         block_scope.params.deinit(gpa);
  34333     }
  34334 
  34335     struct_obj.fields = .{};
  34336     try struct_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len);
  34337 
  34338     const Field = struct {
  34339         type_body_len: u32 = 0,
  34340         align_body_len: u32 = 0,
  34341         init_body_len: u32 = 0,
  34342         type_ref: Zir.Inst.Ref = .none,
  34343     };
  34344     const fields = try sema.arena.alloc(Field, fields_len);
  34345     var any_inits = false;
  34346 
  34347     {
  34348         const bits_per_field = 4;
  34349         const fields_per_u32 = 32 / bits_per_field;
  34350         const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  34351         const flags_index = extra_index;
  34352         var bit_bag_index: usize = flags_index;
  34353         extra_index += bit_bags_count;
  34354         var cur_bit_bag: u32 = undefined;
  34355         var field_i: u32 = 0;
  34356         while (field_i < fields_len) : (field_i += 1) {
  34357             if (field_i % fields_per_u32 == 0) {
  34358                 cur_bit_bag = zir.extra[bit_bag_index];
  34359                 bit_bag_index += 1;
  34360             }
  34361             const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  34362             cur_bit_bag >>= 1;
  34363             const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
  34364             cur_bit_bag >>= 1;
  34365             const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0;
  34366             cur_bit_bag >>= 1;
  34367             const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
  34368             cur_bit_bag >>= 1;
  34369 
  34370             var field_name_zir: ?[:0]const u8 = null;
  34371             if (!small.is_tuple) {
  34372                 field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
  34373                 extra_index += 1;
  34374             }
  34375             extra_index += 1; // doc_comment
  34376 
  34377             fields[field_i] = .{};
  34378 
  34379             if (has_type_body) {
  34380                 fields[field_i].type_body_len = zir.extra[extra_index];
  34381             } else {
  34382                 fields[field_i].type_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  34383             }
  34384             extra_index += 1;
  34385 
  34386             // This string needs to outlive the ZIR code.
  34387             const field_name = try ip.getOrPutString(gpa, if (field_name_zir) |s|
  34388                 s
  34389             else
  34390                 try std.fmt.allocPrint(sema.arena, "{d}", .{field_i}));
  34391 
  34392             const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
  34393             if (gop.found_existing) {
  34394                 const msg = msg: {
  34395                     const field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i }).lazy;
  34396                     const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{}'", .{field_name.fmt(ip)});
  34397                     errdefer msg.destroy(gpa);
  34398 
  34399                     const prev_field_index = struct_obj.fields.getIndex(field_name).?;
  34400                     const prev_field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = prev_field_index });
  34401                     try mod.errNoteNonLazy(prev_field_src, msg, "other field here", .{});
  34402                     try sema.errNote(&block_scope, src, msg, "struct declared here", .{});
  34403                     break :msg msg;
  34404                 };
  34405                 return sema.failWithOwnedErrorMsg(msg);
  34406             }
  34407             gop.value_ptr.* = .{
  34408                 .ty = Type.noreturn,
  34409                 .abi_align = .none,
  34410                 .default_val = .none,
  34411                 .is_comptime = is_comptime,
  34412                 .offset = undefined,
  34413             };
  34414 
  34415             if (has_align) {
  34416                 fields[field_i].align_body_len = zir.extra[extra_index];
  34417                 extra_index += 1;
  34418             }
  34419             if (has_init) {
  34420                 fields[field_i].init_body_len = zir.extra[extra_index];
  34421                 extra_index += 1;
  34422                 any_inits = true;
  34423             }
  34424         }
  34425     }
  34426 
  34427     // Next we do only types and alignments, saving the inits for a second pass,
  34428     // so that init values may depend on type layout.
  34429     const bodies_index = extra_index;
  34430 
  34431     for (fields, 0..) |zir_field, field_i| {
  34432         const field_ty: Type = ty: {
  34433             if (zir_field.type_ref != .none) {
  34434                 break :ty sema.resolveType(&block_scope, .unneeded, zir_field.type_ref) catch |err| switch (err) {
  34435                     error.NeededSourceLocation => {
  34436                         const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34437                             .index = field_i,
  34438                             .range = .type,
  34439                         }).lazy;
  34440                         _ = try sema.resolveType(&block_scope, ty_src, zir_field.type_ref);
  34441                         unreachable;
  34442                     },
  34443                     else => |e| return e,
  34444                 };
  34445             }
  34446             assert(zir_field.type_body_len != 0);
  34447             const body = zir.extra[extra_index..][0..zir_field.type_body_len];
  34448             extra_index += body.len;
  34449             const ty_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
  34450             break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) {
  34451                 error.NeededSourceLocation => {
  34452                     const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34453                         .index = field_i,
  34454                         .range = .type,
  34455                     }).lazy;
  34456                     _ = try sema.analyzeAsType(&block_scope, ty_src, ty_ref);
  34457                     unreachable;
  34458                 },
  34459                 else => |e| return e,
  34460             };
  34461         };
  34462         if (field_ty.isGenericPoison()) {
  34463             return error.GenericPoison;
  34464         }
  34465 
  34466         const field = &struct_obj.fields.values()[field_i];
  34467         field.ty = field_ty;
  34468 
  34469         if (field_ty.zigTypeTag(mod) == .Opaque) {
  34470             const msg = msg: {
  34471                 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34472                     .index = field_i,
  34473                     .range = .type,
  34474                 }).lazy;
  34475                 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  34476                 errdefer msg.destroy(sema.gpa);
  34477 
  34478                 try sema.addDeclaredHereNote(msg, field_ty);
  34479                 break :msg msg;
  34480             };
  34481             return sema.failWithOwnedErrorMsg(msg);
  34482         }
  34483         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  34484             const msg = msg: {
  34485                 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34486                     .index = field_i,
  34487                     .range = .type,
  34488                 }).lazy;
  34489                 const msg = try sema.errMsg(&block_scope, ty_src, "struct fields cannot be 'noreturn'", .{});
  34490                 errdefer msg.destroy(sema.gpa);
  34491 
  34492                 try sema.addDeclaredHereNote(msg, field_ty);
  34493                 break :msg msg;
  34494             };
  34495             return sema.failWithOwnedErrorMsg(msg);
  34496         }
  34497         if (struct_obj.layout == .Extern and !try sema.validateExternType(field.ty, .struct_field)) {
  34498             const msg = msg: {
  34499                 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34500                     .index = field_i,
  34501                     .range = .type,
  34502                 });
  34503                 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(mod)});
  34504                 errdefer msg.destroy(sema.gpa);
  34505 
  34506                 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field.ty, .struct_field);
  34507 
  34508                 try sema.addDeclaredHereNote(msg, field.ty);
  34509                 break :msg msg;
  34510             };
  34511             return sema.failWithOwnedErrorMsg(msg);
  34512         } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty, mod))) {
  34513             const msg = msg: {
  34514                 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34515                     .index = field_i,
  34516                     .range = .type,
  34517                 });
  34518                 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(mod)});
  34519                 errdefer msg.destroy(sema.gpa);
  34520 
  34521                 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field.ty);
  34522 
  34523                 try sema.addDeclaredHereNote(msg, field.ty);
  34524                 break :msg msg;
  34525             };
  34526             return sema.failWithOwnedErrorMsg(msg);
  34527         }
  34528 
  34529         if (zir_field.align_body_len > 0) {
  34530             const body = zir.extra[extra_index..][0..zir_field.align_body_len];
  34531             extra_index += body.len;
  34532             const align_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
  34533             field.abi_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) {
  34534                 error.NeededSourceLocation => {
  34535                     const align_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34536                         .index = field_i,
  34537                         .range = .alignment,
  34538                     }).lazy;
  34539                     _ = try sema.analyzeAsAlign(&block_scope, align_src, align_ref);
  34540                     unreachable;
  34541                 },
  34542                 else => |e| return e,
  34543             };
  34544         }
  34545 
  34546         extra_index += zir_field.init_body_len;
  34547     }
  34548 
  34549     struct_obj.status = .have_field_types;
  34550 
  34551     if (any_inits) {
  34552         extra_index = bodies_index;
  34553         for (fields, 0..) |zir_field, field_i| {
  34554             extra_index += zir_field.type_body_len;
  34555             extra_index += zir_field.align_body_len;
  34556             if (zir_field.init_body_len > 0) {
  34557                 const body = zir.extra[extra_index..][0..zir_field.init_body_len];
  34558                 extra_index += body.len;
  34559                 const init = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
  34560                 const field = &struct_obj.fields.values()[field_i];
  34561                 const coerced = sema.coerce(&block_scope, field.ty, init, .unneeded) catch |err| switch (err) {
  34562                     error.NeededSourceLocation => {
  34563                         const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34564                             .index = field_i,
  34565                             .range = .value,
  34566                         }).lazy;
  34567                         _ = try sema.coerce(&block_scope, field.ty, init, init_src);
  34568                         unreachable;
  34569                     },
  34570                     else => |e| return e,
  34571                 };
  34572                 const default_val = (try sema.resolveMaybeUndefVal(coerced)) orelse {
  34573                     const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34574                         .index = field_i,
  34575                         .range = .value,
  34576                     }).lazy;
  34577                     return sema.failWithNeededComptime(&block_scope, init_src, "struct field default value must be comptime-known");
  34578                 };
  34579                 field.default_val = try default_val.intern(field.ty, mod);
  34580             }
  34581         }
  34582     }
  34583     try wip_captures.finalize();
  34584     for (comptime_mutable_decls.items) |ct_decl_index| {
  34585         const ct_decl = mod.declPtr(ct_decl_index);
  34586         try ct_decl.intern(mod);
  34587     }
  34588 
  34589     struct_obj.have_field_inits = true;
  34590 }
  34591 
  34592 fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
  34593     const tracy = trace(@src());
  34594     defer tracy.end();
  34595 
  34596     const gpa = mod.gpa;
  34597     const ip = &mod.intern_pool;
  34598     const decl_index = union_obj.owner_decl;
  34599     const zir = mod.namespacePtr(union_obj.namespace).file_scope.zir;
  34600     const extended = zir.instructions.items(.data)[union_obj.zir_index].extended;
  34601     assert(extended.opcode == .union_decl);
  34602     const small = @as(Zir.Inst.UnionDecl.Small, @bitCast(extended.small));
  34603     var extra_index: usize = extended.operand;
  34604 
  34605     const src = LazySrcLoc.nodeOffset(0);
  34606     extra_index += @intFromBool(small.has_src_node);
  34607 
  34608     const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: {
  34609         const ty_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  34610         extra_index += 1;
  34611         break :blk ty_ref;
  34612     } else .none;
  34613 
  34614     const body_len = if (small.has_body_len) blk: {
  34615         const body_len = zir.extra[extra_index];
  34616         extra_index += 1;
  34617         break :blk body_len;
  34618     } else 0;
  34619 
  34620     const fields_len = if (small.has_fields_len) blk: {
  34621         const fields_len = zir.extra[extra_index];
  34622         extra_index += 1;
  34623         break :blk fields_len;
  34624     } else 0;
  34625 
  34626     const decls_len = if (small.has_decls_len) decls_len: {
  34627         const decls_len = zir.extra[extra_index];
  34628         extra_index += 1;
  34629         break :decls_len decls_len;
  34630     } else 0;
  34631 
  34632     // Skip over decls.
  34633     var decls_it = zir.declIteratorInner(extra_index, decls_len);
  34634     while (decls_it.next()) |_| {}
  34635     extra_index = decls_it.extra_index;
  34636 
  34637     const body = zir.extra[extra_index..][0..body_len];
  34638     extra_index += body.len;
  34639 
  34640     const decl = mod.declPtr(decl_index);
  34641 
  34642     var analysis_arena = std.heap.ArenaAllocator.init(gpa);
  34643     defer analysis_arena.deinit();
  34644 
  34645     var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
  34646     defer comptime_mutable_decls.deinit();
  34647 
  34648     var sema: Sema = .{
  34649         .mod = mod,
  34650         .gpa = gpa,
  34651         .arena = analysis_arena.allocator(),
  34652         .code = zir,
  34653         .owner_decl = decl,
  34654         .owner_decl_index = decl_index,
  34655         .func = null,
  34656         .func_index = .none,
  34657         .fn_ret_ty = Type.void,
  34658         .owner_func = null,
  34659         .owner_func_index = .none,
  34660         .comptime_mutable_decls = &comptime_mutable_decls,
  34661     };
  34662     defer sema.deinit();
  34663 
  34664     var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope);
  34665     defer wip_captures.deinit();
  34666 
  34667     var block_scope: Block = .{
  34668         .parent = null,
  34669         .sema = &sema,
  34670         .src_decl = decl_index,
  34671         .namespace = union_obj.namespace,
  34672         .wip_capture_scope = wip_captures.scope,
  34673         .instructions = .{},
  34674         .inlining = null,
  34675         .is_comptime = true,
  34676     };
  34677     defer {
  34678         assert(block_scope.instructions.items.len == 0);
  34679         block_scope.params.deinit(gpa);
  34680     }
  34681 
  34682     if (body.len != 0) {
  34683         try sema.analyzeBody(&block_scope, body);
  34684     }
  34685 
  34686     try wip_captures.finalize();
  34687     for (comptime_mutable_decls.items) |ct_decl_index| {
  34688         const ct_decl = mod.declPtr(ct_decl_index);
  34689         try ct_decl.intern(mod);
  34690     }
  34691 
  34692     try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len);
  34693 
  34694     var int_tag_ty: Type = undefined;
  34695     var enum_field_names: []InternPool.NullTerminatedString = &.{};
  34696     var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{};
  34697     var explicit_tags_seen: []bool = &.{};
  34698     if (tag_type_ref != .none) {
  34699         const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x };
  34700         const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref);
  34701         if (small.auto_enum_tag) {
  34702             // The provided type is an integer type and we must construct the enum tag type here.
  34703             int_tag_ty = provided_ty;
  34704             if (int_tag_ty.zigTypeTag(mod) != .Int and int_tag_ty.zigTypeTag(mod) != .ComptimeInt) {
  34705                 return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{}'", .{int_tag_ty.fmt(mod)});
  34706             }
  34707 
  34708             if (fields_len > 0) {
  34709                 const field_count_val = try mod.intValue(Type.comptime_int, fields_len - 1);
  34710                 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) {
  34711                     const msg = msg: {
  34712                         const msg = try sema.errMsg(&block_scope, tag_ty_src, "specified integer tag type cannot represent every field", .{});
  34713                         errdefer msg.destroy(sema.gpa);
  34714                         try sema.errNote(&block_scope, tag_ty_src, msg, "type '{}' cannot fit values in range 0...{d}", .{
  34715                             int_tag_ty.fmt(mod),
  34716                             fields_len - 1,
  34717                         });
  34718                         break :msg msg;
  34719                     };
  34720                     return sema.failWithOwnedErrorMsg(msg);
  34721                 }
  34722                 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  34723                 try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len);
  34724             }
  34725         } else {
  34726             // The provided type is the enum tag type.
  34727             union_obj.tag_ty = provided_ty;
  34728             const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) {
  34729                 .enum_type => |x| x,
  34730                 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(mod)}),
  34731             };
  34732             // The fields of the union must match the enum exactly.
  34733             // A flag per field is used to check for missing and extraneous fields.
  34734             explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
  34735             @memset(explicit_tags_seen, false);
  34736         }
  34737     } else {
  34738         // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis
  34739         // purposes, we still auto-generate an enum tag type the same way. That the union is
  34740         // untagged is represented by the Type tag (union vs union_tagged).
  34741         enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  34742     }
  34743 
  34744     const bits_per_field = 4;
  34745     const fields_per_u32 = 32 / bits_per_field;
  34746     const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  34747     var bit_bag_index: usize = extra_index;
  34748     extra_index += bit_bags_count;
  34749     var cur_bit_bag: u32 = undefined;
  34750     var field_i: u32 = 0;
  34751     var last_tag_val: ?Value = null;
  34752     while (field_i < fields_len) : (field_i += 1) {
  34753         if (field_i % fields_per_u32 == 0) {
  34754             cur_bit_bag = zir.extra[bit_bag_index];
  34755             bit_bag_index += 1;
  34756         }
  34757         const has_type = @as(u1, @truncate(cur_bit_bag)) != 0;
  34758         cur_bit_bag >>= 1;
  34759         const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  34760         cur_bit_bag >>= 1;
  34761         const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0;
  34762         cur_bit_bag >>= 1;
  34763         const unused = @as(u1, @truncate(cur_bit_bag)) != 0;
  34764         cur_bit_bag >>= 1;
  34765         _ = unused;
  34766 
  34767         const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
  34768         extra_index += 1;
  34769 
  34770         // doc_comment
  34771         extra_index += 1;
  34772 
  34773         const field_type_ref: Zir.Inst.Ref = if (has_type) blk: {
  34774             const field_type_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  34775             extra_index += 1;
  34776             break :blk field_type_ref;
  34777         } else .none;
  34778 
  34779         const align_ref: Zir.Inst.Ref = if (has_align) blk: {
  34780             const align_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  34781             extra_index += 1;
  34782             break :blk align_ref;
  34783         } else .none;
  34784 
  34785         const tag_ref: Air.Inst.Ref = if (has_tag) blk: {
  34786             const tag_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  34787             extra_index += 1;
  34788             break :blk try sema.resolveInst(tag_ref);
  34789         } else .none;
  34790 
  34791         if (enum_field_vals.capacity() > 0) {
  34792             const enum_tag_val = if (tag_ref != .none) blk: {
  34793                 const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) {
  34794                     error.NeededSourceLocation => {
  34795                         const val_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34796                             .index = field_i,
  34797                             .range = .value,
  34798                         }).lazy;
  34799                         _ = try sema.semaUnionFieldVal(&block_scope, val_src, int_tag_ty, tag_ref);
  34800                         unreachable;
  34801                     },
  34802                     else => |e| return e,
  34803                 };
  34804                 last_tag_val = val;
  34805 
  34806                 break :blk val;
  34807             } else blk: {
  34808                 const val = if (last_tag_val) |val|
  34809                     try sema.intAdd(val, Value.one_comptime_int, int_tag_ty, undefined)
  34810                 else
  34811                     try mod.intValue(int_tag_ty, 0);
  34812                 last_tag_val = val;
  34813 
  34814                 break :blk val;
  34815             };
  34816             const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern());
  34817             if (gop.found_existing) {
  34818                 const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy;
  34819                 const other_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = gop.index }).lazy;
  34820                 const msg = msg: {
  34821                     const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{enum_tag_val.fmtValue(int_tag_ty, mod)});
  34822                     errdefer msg.destroy(gpa);
  34823                     try sema.errNote(&block_scope, other_field_src, msg, "other occurrence here", .{});
  34824                     break :msg msg;
  34825                 };
  34826                 return sema.failWithOwnedErrorMsg(msg);
  34827             }
  34828         }
  34829 
  34830         // This string needs to outlive the ZIR code.
  34831         const field_name = try ip.getOrPutString(gpa, field_name_zir);
  34832         if (enum_field_names.len != 0) {
  34833             enum_field_names[field_i] = field_name;
  34834         }
  34835 
  34836         const field_ty: Type = if (!has_type)
  34837             Type.void
  34838         else if (field_type_ref == .none)
  34839             Type.noreturn
  34840         else
  34841             sema.resolveType(&block_scope, .unneeded, field_type_ref) catch |err| switch (err) {
  34842                 error.NeededSourceLocation => {
  34843                     const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34844                         .index = field_i,
  34845                         .range = .type,
  34846                     }).lazy;
  34847                     _ = try sema.resolveType(&block_scope, ty_src, field_type_ref);
  34848                     unreachable;
  34849                 },
  34850                 else => |e| return e,
  34851             };
  34852 
  34853         if (field_ty.isGenericPoison()) {
  34854             return error.GenericPoison;
  34855         }
  34856 
  34857         const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
  34858         if (gop.found_existing) {
  34859             const msg = msg: {
  34860                 const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy;
  34861                 const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{}'", .{
  34862                     field_name.fmt(ip),
  34863                 });
  34864                 errdefer msg.destroy(gpa);
  34865 
  34866                 const prev_field_index = union_obj.fields.getIndex(field_name).?;
  34867                 const prev_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = prev_field_index }).lazy;
  34868                 try mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl, mod), msg, "other field here", .{});
  34869                 try sema.errNote(&block_scope, src, msg, "union declared here", .{});
  34870                 break :msg msg;
  34871             };
  34872             return sema.failWithOwnedErrorMsg(msg);
  34873         }
  34874 
  34875         if (explicit_tags_seen.len > 0) {
  34876             const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
  34877             const enum_index = tag_info.nameIndex(ip, field_name) orelse {
  34878                 const msg = msg: {
  34879                     const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34880                         .index = field_i,
  34881                         .range = .type,
  34882                     }).lazy;
  34883                     const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{}' in enum '{}'", .{
  34884                         field_name.fmt(ip), union_obj.tag_ty.fmt(mod),
  34885                     });
  34886                     errdefer msg.destroy(sema.gpa);
  34887                     try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
  34888                     break :msg msg;
  34889                 };
  34890                 return sema.failWithOwnedErrorMsg(msg);
  34891             };
  34892             // No check for duplicate because the check already happened in order
  34893             // to create the enum type in the first place.
  34894             assert(!explicit_tags_seen[enum_index]);
  34895             explicit_tags_seen[enum_index] = true;
  34896         }
  34897 
  34898         if (field_ty.zigTypeTag(mod) == .Opaque) {
  34899             const msg = msg: {
  34900                 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34901                     .index = field_i,
  34902                     .range = .type,
  34903                 }).lazy;
  34904                 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
  34905                 errdefer msg.destroy(sema.gpa);
  34906 
  34907                 try sema.addDeclaredHereNote(msg, field_ty);
  34908                 break :msg msg;
  34909             };
  34910             return sema.failWithOwnedErrorMsg(msg);
  34911         }
  34912         if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) {
  34913             const msg = msg: {
  34914                 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34915                     .index = field_i,
  34916                     .range = .type,
  34917                 });
  34918                 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  34919                 errdefer msg.destroy(sema.gpa);
  34920 
  34921                 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .union_field);
  34922 
  34923                 try sema.addDeclaredHereNote(msg, field_ty);
  34924                 break :msg msg;
  34925             };
  34926             return sema.failWithOwnedErrorMsg(msg);
  34927         } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) {
  34928             const msg = msg: {
  34929                 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34930                     .index = field_i,
  34931                     .range = .type,
  34932                 });
  34933                 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  34934                 errdefer msg.destroy(sema.gpa);
  34935 
  34936                 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty);
  34937 
  34938                 try sema.addDeclaredHereNote(msg, field_ty);
  34939                 break :msg msg;
  34940             };
  34941             return sema.failWithOwnedErrorMsg(msg);
  34942         }
  34943 
  34944         gop.value_ptr.* = .{
  34945             .ty = field_ty,
  34946             .abi_align = .none,
  34947         };
  34948 
  34949         if (align_ref != .none) {
  34950             gop.value_ptr.abi_align = sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) {
  34951                 error.NeededSourceLocation => {
  34952                     const align_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34953                         .index = field_i,
  34954                         .range = .alignment,
  34955                     }).lazy;
  34956                     _ = try sema.resolveAlign(&block_scope, align_src, align_ref);
  34957                     unreachable;
  34958                 },
  34959                 else => |e| return e,
  34960             };
  34961         } else {
  34962             gop.value_ptr.abi_align = .none;
  34963         }
  34964     }
  34965 
  34966     if (explicit_tags_seen.len > 0) {
  34967         const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
  34968         if (tag_info.names.len > fields_len) {
  34969             const msg = msg: {
  34970                 const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{});
  34971                 errdefer msg.destroy(sema.gpa);
  34972 
  34973                 const enum_ty = union_obj.tag_ty;
  34974                 for (tag_info.names, 0..) |field_name, field_index| {
  34975                     if (explicit_tags_seen[field_index]) continue;
  34976                     try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{
  34977                         field_name.fmt(ip),
  34978                     });
  34979                 }
  34980                 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
  34981                 break :msg msg;
  34982             };
  34983             return sema.failWithOwnedErrorMsg(msg);
  34984         }
  34985     } else if (enum_field_vals.count() > 0) {
  34986         union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_obj);
  34987     } else {
  34988         union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_obj);
  34989     }
  34990 }
  34991 
  34992 fn semaUnionFieldVal(sema: *Sema, block: *Block, src: LazySrcLoc, int_tag_ty: Type, tag_ref: Air.Inst.Ref) CompileError!Value {
  34993     const coerced = try sema.coerce(block, int_tag_ty, tag_ref, src);
  34994     return sema.resolveConstValue(block, src, coerced, "enum tag value must be comptime-known");
  34995 }
  34996 
  34997 fn generateUnionTagTypeNumbered(
  34998     sema: *Sema,
  34999     block: *Block,
  35000     enum_field_names: []const InternPool.NullTerminatedString,
  35001     enum_field_vals: []const InternPool.Index,
  35002     union_obj: *Module.Union,
  35003 ) !Type {
  35004     const mod = sema.mod;
  35005     const gpa = sema.gpa;
  35006 
  35007     const src_decl = mod.declPtr(block.src_decl);
  35008     const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope);
  35009     errdefer mod.destroyDecl(new_decl_index);
  35010     const fqn = try union_obj.getFullyQualifiedName(mod);
  35011     const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)});
  35012     try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{
  35013         .ty = Type.noreturn,
  35014         .val = Value.@"unreachable",
  35015     }, name);
  35016     errdefer mod.abortAnonDecl(new_decl_index);
  35017 
  35018     const new_decl = mod.declPtr(new_decl_index);
  35019     new_decl.name_fully_qualified = true;
  35020     new_decl.owns_tv = true;
  35021     new_decl.name_fully_qualified = true;
  35022 
  35023     const enum_ty = try mod.intern(.{ .enum_type = .{
  35024         .decl = new_decl_index,
  35025         .namespace = .none,
  35026         .tag_ty = if (enum_field_vals.len == 0)
  35027             (try mod.intType(.unsigned, 0)).toIntern()
  35028         else
  35029             mod.intern_pool.typeOf(enum_field_vals[0]),
  35030         .names = enum_field_names,
  35031         .values = enum_field_vals,
  35032         .tag_mode = .explicit,
  35033     } });
  35034 
  35035     new_decl.ty = Type.type;
  35036     new_decl.val = enum_ty.toValue();
  35037 
  35038     try mod.finalizeAnonDecl(new_decl_index);
  35039     return enum_ty.toType();
  35040 }
  35041 
  35042 fn generateUnionTagTypeSimple(
  35043     sema: *Sema,
  35044     block: *Block,
  35045     enum_field_names: []const InternPool.NullTerminatedString,
  35046     maybe_union_obj: ?*Module.Union,
  35047 ) !Type {
  35048     const mod = sema.mod;
  35049     const gpa = sema.gpa;
  35050 
  35051     const new_decl_index = new_decl_index: {
  35052         const union_obj = maybe_union_obj orelse {
  35053             break :new_decl_index try mod.createAnonymousDecl(block, .{
  35054                 .ty = Type.noreturn,
  35055                 .val = Value.@"unreachable",
  35056             });
  35057         };
  35058         const src_decl = mod.declPtr(block.src_decl);
  35059         const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope);
  35060         errdefer mod.destroyDecl(new_decl_index);
  35061         const fqn = try union_obj.getFullyQualifiedName(mod);
  35062         const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)});
  35063         try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{
  35064             .ty = Type.noreturn,
  35065             .val = Value.@"unreachable",
  35066         }, name);
  35067         mod.declPtr(new_decl_index).name_fully_qualified = true;
  35068         break :new_decl_index new_decl_index;
  35069     };
  35070     errdefer mod.abortAnonDecl(new_decl_index);
  35071 
  35072     const enum_ty = try mod.intern(.{ .enum_type = .{
  35073         .decl = new_decl_index,
  35074         .namespace = .none,
  35075         .tag_ty = if (enum_field_names.len == 0)
  35076             (try mod.intType(.unsigned, 0)).toIntern()
  35077         else
  35078             (try mod.smallestUnsignedInt(enum_field_names.len - 1)).toIntern(),
  35079         .names = enum_field_names,
  35080         .values = &.{},
  35081         .tag_mode = .auto,
  35082     } });
  35083 
  35084     const new_decl = mod.declPtr(new_decl_index);
  35085     new_decl.owns_tv = true;
  35086     new_decl.ty = Type.type;
  35087     new_decl.val = enum_ty.toValue();
  35088 
  35089     try mod.finalizeAnonDecl(new_decl_index);
  35090     return enum_ty.toType();
  35091 }
  35092 
  35093 fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref {
  35094     const gpa = sema.gpa;
  35095     const src = LazySrcLoc.nodeOffset(0);
  35096 
  35097     var wip_captures = try WipCaptureScope.init(gpa, sema.owner_decl.src_scope);
  35098     defer wip_captures.deinit();
  35099 
  35100     var block: Block = .{
  35101         .parent = null,
  35102         .sema = sema,
  35103         .src_decl = sema.owner_decl_index,
  35104         .namespace = sema.owner_decl.src_namespace,
  35105         .wip_capture_scope = wip_captures.scope,
  35106         .instructions = .{},
  35107         .inlining = null,
  35108         .is_comptime = true,
  35109     };
  35110     defer {
  35111         block.instructions.deinit(gpa);
  35112         block.params.deinit(gpa);
  35113     }
  35114 
  35115     const decl_index = try getBuiltinDecl(sema, &block, name);
  35116     return sema.analyzeDeclVal(&block, src, decl_index);
  35117 }
  35118 
  35119 fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!Module.Decl.Index {
  35120     const gpa = sema.gpa;
  35121 
  35122     const src = LazySrcLoc.nodeOffset(0);
  35123 
  35124     const mod = sema.mod;
  35125     const ip = &mod.intern_pool;
  35126     const std_pkg = mod.main_pkg.table.get("std").?;
  35127     const std_file = (mod.importPkg(std_pkg) catch unreachable).file;
  35128     const opt_builtin_inst = (try sema.namespaceLookupRef(
  35129         block,
  35130         src,
  35131         mod.declPtr(std_file.root_decl.unwrap().?).src_namespace,
  35132         try ip.getOrPutString(gpa, "builtin"),
  35133     )) orelse @panic("lib/std.zig is corrupt and missing 'builtin'");
  35134     const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst, src);
  35135     const builtin_ty = sema.analyzeAsType(block, src, builtin_inst) catch |err| switch (err) {
  35136         error.AnalysisFail => std.debug.panic("std.builtin is corrupt", .{}),
  35137         else => |e| return e,
  35138     };
  35139     const decl_index = (try sema.namespaceLookup(
  35140         block,
  35141         src,
  35142         builtin_ty.getNamespaceIndex(mod).unwrap().?,
  35143         try ip.getOrPutString(gpa, name),
  35144     )) orelse std.debug.panic("lib/std/builtin.zig is corrupt and missing '{s}'", .{name});
  35145     return decl_index;
  35146 }
  35147 
  35148 fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type {
  35149     const ty_inst = try sema.getBuiltin(name);
  35150 
  35151     var wip_captures = try WipCaptureScope.init(sema.gpa, sema.owner_decl.src_scope);
  35152     defer wip_captures.deinit();
  35153 
  35154     var block: Block = .{
  35155         .parent = null,
  35156         .sema = sema,
  35157         .src_decl = sema.owner_decl_index,
  35158         .namespace = sema.owner_decl.src_namespace,
  35159         .wip_capture_scope = wip_captures.scope,
  35160         .instructions = .{},
  35161         .inlining = null,
  35162         .is_comptime = true,
  35163     };
  35164     defer {
  35165         block.instructions.deinit(sema.gpa);
  35166         block.params.deinit(sema.gpa);
  35167     }
  35168     const src = LazySrcLoc.nodeOffset(0);
  35169 
  35170     const result_ty = sema.analyzeAsType(&block, src, ty_inst) catch |err| switch (err) {
  35171         error.AnalysisFail => std.debug.panic("std.builtin.{s} is corrupt", .{name}),
  35172         else => |e| return e,
  35173     };
  35174     try sema.resolveTypeFully(result_ty); // Should not fail
  35175     return result_ty;
  35176 }
  35177 
  35178 /// There is another implementation of this in `Type.onePossibleValue`. This one
  35179 /// in `Sema` is for calling during semantic analysis, and performs field resolution
  35180 /// to get the answer. The one in `Type` is for calling during codegen and asserts
  35181 /// that the types are already resolved.
  35182 /// TODO assert the return value matches `ty.onePossibleValue`
  35183 pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
  35184     const mod = sema.mod;
  35185     return switch (ty.toIntern()) {
  35186         .u0_type,
  35187         .i0_type,
  35188         => try mod.intValue(ty, 0),
  35189         .u1_type,
  35190         .u8_type,
  35191         .i8_type,
  35192         .u16_type,
  35193         .i16_type,
  35194         .u29_type,
  35195         .u32_type,
  35196         .i32_type,
  35197         .u64_type,
  35198         .i64_type,
  35199         .u80_type,
  35200         .u128_type,
  35201         .i128_type,
  35202         .usize_type,
  35203         .isize_type,
  35204         .c_char_type,
  35205         .c_short_type,
  35206         .c_ushort_type,
  35207         .c_int_type,
  35208         .c_uint_type,
  35209         .c_long_type,
  35210         .c_ulong_type,
  35211         .c_longlong_type,
  35212         .c_ulonglong_type,
  35213         .c_longdouble_type,
  35214         .f16_type,
  35215         .f32_type,
  35216         .f64_type,
  35217         .f80_type,
  35218         .f128_type,
  35219         .anyopaque_type,
  35220         .bool_type,
  35221         .type_type,
  35222         .anyerror_type,
  35223         .comptime_int_type,
  35224         .comptime_float_type,
  35225         .enum_literal_type,
  35226         .atomic_order_type,
  35227         .atomic_rmw_op_type,
  35228         .calling_convention_type,
  35229         .address_space_type,
  35230         .float_mode_type,
  35231         .reduce_op_type,
  35232         .call_modifier_type,
  35233         .prefetch_options_type,
  35234         .export_options_type,
  35235         .extern_options_type,
  35236         .type_info_type,
  35237         .manyptr_u8_type,
  35238         .manyptr_const_u8_type,
  35239         .manyptr_const_u8_sentinel_0_type,
  35240         .single_const_pointer_to_comptime_int_type,
  35241         .slice_const_u8_type,
  35242         .slice_const_u8_sentinel_0_type,
  35243         .anyerror_void_error_union_type,
  35244         => null,
  35245         .void_type => Value.void,
  35246         .noreturn_type => Value.@"unreachable",
  35247         .anyframe_type => unreachable,
  35248         .null_type => Value.null,
  35249         .undefined_type => Value.undef,
  35250         .optional_noreturn_type => try mod.nullValue(ty),
  35251         .generic_poison_type => error.GenericPoison,
  35252         .empty_struct_type => Value.empty_struct,
  35253         // values, not types
  35254         .undef,
  35255         .zero,
  35256         .zero_usize,
  35257         .zero_u8,
  35258         .one,
  35259         .one_usize,
  35260         .one_u8,
  35261         .four_u8,
  35262         .negative_one,
  35263         .calling_convention_c,
  35264         .calling_convention_inline,
  35265         .void_value,
  35266         .unreachable_value,
  35267         .null_value,
  35268         .bool_true,
  35269         .bool_false,
  35270         .empty_struct,
  35271         .generic_poison,
  35272         // invalid
  35273         .var_args_param_type,
  35274         .none,
  35275         => unreachable,
  35276         _ => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) {
  35277             .type_int_signed, // i0 handled above
  35278             .type_int_unsigned, // u0 handled above
  35279             .type_pointer,
  35280             .type_slice,
  35281             .type_optional, // ?noreturn handled above
  35282             .type_anyframe,
  35283             .type_error_union,
  35284             .type_error_set,
  35285             .type_inferred_error_set,
  35286             .type_opaque,
  35287             .type_function,
  35288             => null,
  35289             .simple_type, // handled above
  35290             // values, not types
  35291             .undef,
  35292             .runtime_value,
  35293             .simple_value,
  35294             .ptr_decl,
  35295             .ptr_mut_decl,
  35296             .ptr_comptime_field,
  35297             .ptr_int,
  35298             .ptr_eu_payload,
  35299             .ptr_opt_payload,
  35300             .ptr_elem,
  35301             .ptr_field,
  35302             .ptr_slice,
  35303             .opt_payload,
  35304             .opt_null,
  35305             .int_u8,
  35306             .int_u16,
  35307             .int_u32,
  35308             .int_i32,
  35309             .int_usize,
  35310             .int_comptime_int_u32,
  35311             .int_comptime_int_i32,
  35312             .int_small,
  35313             .int_positive,
  35314             .int_negative,
  35315             .int_lazy_align,
  35316             .int_lazy_size,
  35317             .error_set_error,
  35318             .error_union_error,
  35319             .error_union_payload,
  35320             .enum_literal,
  35321             .enum_tag,
  35322             .float_f16,
  35323             .float_f32,
  35324             .float_f64,
  35325             .float_f80,
  35326             .float_f128,
  35327             .float_c_longdouble_f80,
  35328             .float_c_longdouble_f128,
  35329             .float_comptime_float,
  35330             .variable,
  35331             .extern_func,
  35332             .func,
  35333             .only_possible_value,
  35334             .union_value,
  35335             .bytes,
  35336             .aggregate,
  35337             .repeated,
  35338             // memoized value, not types
  35339             .memoized_call,
  35340             => unreachable,
  35341             .type_array_big,
  35342             .type_array_small,
  35343             .type_vector,
  35344             .type_enum_auto,
  35345             .type_enum_explicit,
  35346             .type_enum_nonexhaustive,
  35347             .type_struct,
  35348             .type_struct_ns,
  35349             .type_struct_anon,
  35350             .type_tuple_anon,
  35351             .type_union_tagged,
  35352             .type_union_untagged,
  35353             .type_union_safety,
  35354             => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  35355                 inline .array_type, .vector_type => |seq_type, seq_tag| {
  35356                     const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none;
  35357                     if (seq_type.len + @intFromBool(has_sentinel) == 0) return (try mod.intern(.{ .aggregate = .{
  35358                         .ty = ty.toIntern(),
  35359                         .storage = .{ .elems = &.{} },
  35360                     } })).toValue();
  35361 
  35362                     if (try sema.typeHasOnePossibleValue(seq_type.child.toType())) |opv| {
  35363                         return (try mod.intern(.{ .aggregate = .{
  35364                             .ty = ty.toIntern(),
  35365                             .storage = .{ .repeated_elem = opv.toIntern() },
  35366                         } })).toValue();
  35367                     }
  35368                     return null;
  35369                 },
  35370 
  35371                 .struct_type => |struct_type| {
  35372                     const resolved_ty = try sema.resolveTypeFields(ty);
  35373                     if (mod.structPtrUnwrap(struct_type.index)) |s| {
  35374                         const field_vals = try sema.arena.alloc(InternPool.Index, s.fields.count());
  35375                         for (field_vals, s.fields.values(), 0..) |*field_val, field, i| {
  35376                             if (field.is_comptime) {
  35377                                 field_val.* = field.default_val;
  35378                                 continue;
  35379                             }
  35380                             if (field.ty.eql(resolved_ty, sema.mod)) {
  35381                                 const msg = try Module.ErrorMsg.create(
  35382                                     sema.gpa,
  35383                                     s.srcLoc(sema.mod),
  35384                                     "struct '{}' depends on itself",
  35385                                     .{ty.fmt(sema.mod)},
  35386                                 );
  35387                                 try sema.addFieldErrNote(resolved_ty, i, msg, "while checking this field", .{});
  35388                                 return sema.failWithOwnedErrorMsg(msg);
  35389                             }
  35390                             if (try sema.typeHasOnePossibleValue(field.ty)) |field_opv| {
  35391                                 field_val.* = try field_opv.intern(field.ty, mod);
  35392                             } else return null;
  35393                         }
  35394 
  35395                         // In this case the struct has no runtime-known fields and
  35396                         // therefore has one possible value.
  35397                         return (try mod.intern(.{ .aggregate = .{
  35398                             .ty = ty.toIntern(),
  35399                             .storage = .{ .elems = field_vals },
  35400                         } })).toValue();
  35401                     }
  35402 
  35403                     // In this case the struct has no fields at all and
  35404                     // therefore has one possible value.
  35405                     return (try mod.intern(.{ .aggregate = .{
  35406                         .ty = ty.toIntern(),
  35407                         .storage = .{ .elems = &.{} },
  35408                     } })).toValue();
  35409                 },
  35410 
  35411                 .anon_struct_type => |tuple| {
  35412                     for (tuple.values) |val| {
  35413                         if (val == .none) return null;
  35414                     }
  35415                     // In this case the struct has all comptime-known fields and
  35416                     // therefore has one possible value.
  35417                     // TODO: write something like getCoercedInts to avoid needing to dupe
  35418                     return (try mod.intern(.{ .aggregate = .{
  35419                         .ty = ty.toIntern(),
  35420                         .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values) },
  35421                     } })).toValue();
  35422                 },
  35423 
  35424                 .union_type => |union_type| {
  35425                     const resolved_ty = try sema.resolveTypeFields(ty);
  35426                     const union_obj = mod.unionPtr(union_type.index);
  35427                     const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse
  35428                         return null;
  35429                     const fields = union_obj.fields.values();
  35430                     if (fields.len == 0) {
  35431                         const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
  35432                         return only.toValue();
  35433                     }
  35434                     const only_field = fields[0];
  35435                     if (only_field.ty.eql(resolved_ty, sema.mod)) {
  35436                         const msg = try Module.ErrorMsg.create(
  35437                             sema.gpa,
  35438                             union_obj.srcLoc(sema.mod),
  35439                             "union '{}' depends on itself",
  35440                             .{ty.fmt(sema.mod)},
  35441                         );
  35442                         try sema.addFieldErrNote(resolved_ty, 0, msg, "while checking this field", .{});
  35443                         return sema.failWithOwnedErrorMsg(msg);
  35444                     }
  35445                     const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse
  35446                         return null;
  35447                     const only = try mod.intern(.{ .un = .{
  35448                         .ty = resolved_ty.toIntern(),
  35449                         .tag = tag_val.toIntern(),
  35450                         .val = val_val.toIntern(),
  35451                     } });
  35452                     return only.toValue();
  35453                 },
  35454 
  35455                 .enum_type => |enum_type| switch (enum_type.tag_mode) {
  35456                     .nonexhaustive => {
  35457                         if (enum_type.tag_ty == .comptime_int_type) return null;
  35458 
  35459                         if (try sema.typeHasOnePossibleValue(enum_type.tag_ty.toType())) |int_opv| {
  35460                             const only = try mod.intern(.{ .enum_tag = .{
  35461                                 .ty = ty.toIntern(),
  35462                                 .int = int_opv.toIntern(),
  35463                             } });
  35464                             return only.toValue();
  35465                         }
  35466 
  35467                         return null;
  35468                     },
  35469                     .auto, .explicit => {
  35470                         if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null;
  35471 
  35472                         switch (enum_type.names.len) {
  35473                             0 => {
  35474                                 const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
  35475                                 return only.toValue();
  35476                             },
  35477                             1 => return try mod.getCoerced((if (enum_type.values.len == 0)
  35478                                 try mod.intern(.{ .int = .{
  35479                                     .ty = enum_type.tag_ty,
  35480                                     .storage = .{ .u64 = 0 },
  35481                                 } })
  35482                             else
  35483                                 enum_type.values[0]).toValue(), ty),
  35484                             else => return null,
  35485                         }
  35486                     },
  35487                 },
  35488 
  35489                 else => unreachable,
  35490             },
  35491         },
  35492     };
  35493 }
  35494 
  35495 /// Returns the type of the AIR instruction.
  35496 fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
  35497     return sema.getTmpAir().typeOf(inst, &sema.mod.intern_pool);
  35498 }
  35499 
  35500 pub fn getTmpAir(sema: Sema) Air {
  35501     return .{
  35502         .instructions = sema.air_instructions.slice(),
  35503         .extra = sema.air_extra.items,
  35504     };
  35505 }
  35506 
  35507 // TODO: make this non-fallible or remove it entirely
  35508 pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
  35509     _ = sema;
  35510     return Air.internedToRef(ty.toIntern());
  35511 }
  35512 
  35513 fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
  35514     const mod = sema.mod;
  35515     return sema.addConstant(try mod.intValue(ty, int));
  35516 }
  35517 
  35518 fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
  35519     return sema.addConstant((try sema.mod.intern(.{ .undef = ty.toIntern() })).toValue());
  35520 }
  35521 
  35522 // TODO: make this non-fallible or remove it entirely
  35523 pub fn addConstant(sema: *Sema, val: Value) !Air.Inst.Ref {
  35524     _ = sema;
  35525     return Air.internedToRef(val.toIntern());
  35526 }
  35527 
  35528 pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {
  35529     const fields = std.meta.fields(@TypeOf(extra));
  35530     try sema.air_extra.ensureUnusedCapacity(sema.gpa, fields.len);
  35531     return sema.addExtraAssumeCapacity(extra);
  35532 }
  35533 
  35534 pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {
  35535     const fields = std.meta.fields(@TypeOf(extra));
  35536     const result = @as(u32, @intCast(sema.air_extra.items.len));
  35537     inline for (fields) |field| {
  35538         sema.air_extra.appendAssumeCapacity(switch (field.type) {
  35539             u32 => @field(extra, field.name),
  35540             Air.Inst.Ref => @intFromEnum(@field(extra, field.name)),
  35541             i32 => @as(u32, @bitCast(@field(extra, field.name))),
  35542             InternPool.Index => @intFromEnum(@field(extra, field.name)),
  35543             else => @compileError("bad field type: " ++ @typeName(field.type)),
  35544         });
  35545     }
  35546     return result;
  35547 }
  35548 
  35549 fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void {
  35550     const coerced = @as([]const u32, @ptrCast(refs));
  35551     sema.air_extra.appendSliceAssumeCapacity(coerced);
  35552 }
  35553 
  35554 fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index {
  35555     const air_datas = sema.air_instructions.items(.data);
  35556     const air_tags = sema.air_instructions.items(.tag);
  35557     switch (air_tags[inst_index]) {
  35558         .br => return air_datas[inst_index].br.block_inst,
  35559         else => return null,
  35560     }
  35561 }
  35562 
  35563 fn isComptimeKnown(
  35564     sema: *Sema,
  35565     inst: Air.Inst.Ref,
  35566 ) !bool {
  35567     return (try sema.resolveMaybeUndefVal(inst)) != null;
  35568 }
  35569 
  35570 fn analyzeComptimeAlloc(
  35571     sema: *Sema,
  35572     block: *Block,
  35573     var_type: Type,
  35574     alignment: Alignment,
  35575 ) CompileError!Air.Inst.Ref {
  35576     const mod = sema.mod;
  35577 
  35578     // Needed to make an anon decl with type `var_type` (the `finish()` call below).
  35579     _ = try sema.typeHasOnePossibleValue(var_type);
  35580 
  35581     const ptr_type = try mod.ptrType(.{
  35582         .child = var_type.toIntern(),
  35583         .flags = .{
  35584             .alignment = alignment,
  35585             .address_space = target_util.defaultAddressSpace(mod.getTarget(), .global_constant),
  35586         },
  35587     });
  35588 
  35589     var anon_decl = try block.startAnonDecl();
  35590     defer anon_decl.deinit();
  35591 
  35592     const decl_index = try anon_decl.finish(
  35593         var_type,
  35594         // There will be stores before the first load, but they may be to sub-elements or
  35595         // sub-fields. So we need to initialize with undef to allow the mechanism to expand
  35596         // into fields/elements and have those overridden with stored values.
  35597         (try mod.intern(.{ .undef = var_type.toIntern() })).toValue(),
  35598         alignment,
  35599     );
  35600     const decl = mod.declPtr(decl_index);
  35601     decl.alignment = alignment;
  35602 
  35603     try sema.comptime_mutable_decls.append(decl_index);
  35604     try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
  35605     return sema.addConstant((try mod.intern(.{ .ptr = .{
  35606         .ty = ptr_type.toIntern(),
  35607         .addr = .{ .mut_decl = .{
  35608             .decl = decl_index,
  35609             .runtime_index = block.runtime_index,
  35610         } },
  35611     } })).toValue());
  35612 }
  35613 
  35614 /// The places where a user can specify an address space attribute
  35615 pub const AddressSpaceContext = enum {
  35616     /// A function is specified to be placed in a certain address space.
  35617     function,
  35618 
  35619     /// A (global) variable is specified to be placed in a certain address space.
  35620     /// In contrast to .constant, these values (and thus the address space they will be
  35621     /// placed in) are required to be mutable.
  35622     variable,
  35623 
  35624     /// A (global) constant value is specified to be placed in a certain address space.
  35625     /// In contrast to .variable, values placed in this address space are not required to be mutable.
  35626     constant,
  35627 
  35628     /// A pointer is ascripted to point into a certain address space.
  35629     pointer,
  35630 };
  35631 
  35632 pub fn analyzeAddressSpace(
  35633     sema: *Sema,
  35634     block: *Block,
  35635     src: LazySrcLoc,
  35636     zir_ref: Zir.Inst.Ref,
  35637     ctx: AddressSpaceContext,
  35638 ) !std.builtin.AddressSpace {
  35639     const mod = sema.mod;
  35640     const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref, "addresspace must be comptime-known");
  35641     const address_space = mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val);
  35642     const target = sema.mod.getTarget();
  35643     const arch = target.cpu.arch;
  35644 
  35645     const is_nv = arch == .nvptx or arch == .nvptx64;
  35646     const is_amd = arch == .amdgcn;
  35647     const is_spirv = arch == .spirv32 or arch == .spirv64;
  35648     const is_gpu = is_nv or is_amd or is_spirv;
  35649 
  35650     const supported = switch (address_space) {
  35651         // TODO: on spir-v only when os is opencl.
  35652         .generic => true,
  35653         .gs, .fs, .ss => (arch == .x86 or arch == .x86_64) and ctx == .pointer,
  35654         // TODO: check that .shared and .local are left uninitialized
  35655         .param => is_nv,
  35656         .global, .shared, .local => is_gpu,
  35657         .constant => is_gpu and (ctx == .constant),
  35658         // TODO this should also check how many flash banks the cpu has
  35659         .flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr,
  35660     };
  35661 
  35662     if (!supported) {
  35663         // TODO error messages could be made more elaborate here
  35664         const entity = switch (ctx) {
  35665             .function => "functions",
  35666             .variable => "mutable values",
  35667             .constant => "constant values",
  35668             .pointer => "pointers",
  35669         };
  35670         return sema.fail(
  35671             block,
  35672             src,
  35673             "{s} with address space '{s}' are not supported on {s}",
  35674             .{ entity, @tagName(address_space), arch.genericName() },
  35675         );
  35676     }
  35677 
  35678     return address_space;
  35679 }
  35680 
  35681 /// Asserts the value is a pointer and dereferences it.
  35682 /// Returns `null` if the pointer contents cannot be loaded at comptime.
  35683 fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
  35684     const mod = sema.mod;
  35685     const load_ty = ptr_ty.childType(mod);
  35686     const res = try sema.pointerDerefExtra(block, src, ptr_val, load_ty);
  35687     switch (res) {
  35688         .runtime_load => return null,
  35689         .val => |v| return v,
  35690         .needed_well_defined => |ty| return sema.fail(
  35691             block,
  35692             src,
  35693             "comptime dereference requires '{}' to have a well-defined layout, but it does not.",
  35694             .{ty.fmt(sema.mod)},
  35695         ),
  35696         .out_of_bounds => |ty| return sema.fail(
  35697             block,
  35698             src,
  35699             "dereference of '{}' exceeds bounds of containing decl of type '{}'",
  35700             .{ ptr_ty.fmt(sema.mod), ty.fmt(sema.mod) },
  35701         ),
  35702     }
  35703 }
  35704 
  35705 const DerefResult = union(enum) {
  35706     runtime_load,
  35707     val: Value,
  35708     needed_well_defined: Type,
  35709     out_of_bounds: Type,
  35710 };
  35711 
  35712 fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, load_ty: Type) CompileError!DerefResult {
  35713     const mod = sema.mod;
  35714     const target = mod.getTarget();
  35715     const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) {
  35716         error.RuntimeLoad => return DerefResult{ .runtime_load = {} },
  35717         else => |e| return e,
  35718     };
  35719 
  35720     if (deref.pointee) |tv| {
  35721         const coerce_in_mem_ok =
  35722             (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or
  35723             (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok;
  35724         if (coerce_in_mem_ok) {
  35725             // We have a Value that lines up in virtual memory exactly with what we want to load,
  35726             // and it is in-memory coercible to load_ty. It may be returned without modifications.
  35727             // Move mutable decl values to the InternPool and assert other decls are already in
  35728             // the InternPool.
  35729             const uncoerced_val = if (deref.is_mutable) try tv.val.intern(tv.ty, mod) else tv.val.toIntern();
  35730             const coerced_val = try mod.getCoerced(uncoerced_val.toValue(), load_ty);
  35731             return .{ .val = coerced_val };
  35732         }
  35733     }
  35734 
  35735     // The type is not in-memory coercible or the direct dereference failed, so it must
  35736     // be bitcast according to the pointer type we are performing the load through.
  35737     if (!load_ty.hasWellDefinedLayout(mod)) {
  35738         return DerefResult{ .needed_well_defined = load_ty };
  35739     }
  35740 
  35741     const load_sz = try sema.typeAbiSize(load_ty);
  35742 
  35743     // Try the smaller bit-cast first, since that's more efficient than using the larger `parent`
  35744     if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(tv.ty))
  35745         return DerefResult{ .val = (try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0)) orelse return .runtime_load };
  35746 
  35747     // If that fails, try to bit-cast from the largest parent value with a well-defined layout
  35748     if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(parent.tv.ty))
  35749         return DerefResult{ .val = (try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset)) orelse return .runtime_load };
  35750 
  35751     if (deref.ty_without_well_defined_layout) |bad_ty| {
  35752         // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
  35753         // is that some type we encountered when de-referencing does not have a well-defined layout.
  35754         return DerefResult{ .needed_well_defined = bad_ty };
  35755     } else {
  35756         // If all encountered types had well-defined layouts, the parent is the root decl and it just
  35757         // wasn't big enough for the load.
  35758         return DerefResult{ .out_of_bounds = deref.parent.?.tv.ty };
  35759     }
  35760 }
  35761 
  35762 /// Used to convert a u64 value to a usize value, emitting a compile error if the number
  35763 /// is too big to fit.
  35764 fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize {
  35765     if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int;
  35766     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});
  35767 }
  35768 
  35769 /// For pointer-like optionals, it returns the pointer type. For pointers,
  35770 /// the type is returned unmodified.
  35771 /// This can return `error.AnalysisFail` because it sometimes requires resolving whether
  35772 /// a type has zero bits, which can cause a "foo depends on itself" compile error.
  35773 /// This logic must be kept in sync with `Type.isPtrLikeOptional`.
  35774 fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type {
  35775     const mod = sema.mod;
  35776     return switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  35777         .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  35778             .One, .Many, .C => ty,
  35779             .Slice => null,
  35780         },
  35781         .opt_type => |opt_child| switch (mod.intern_pool.indexToKey(opt_child)) {
  35782             .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  35783                 .Slice, .C => null,
  35784                 .Many, .One => {
  35785                     if (ptr_type.flags.is_allowzero) return null;
  35786 
  35787                     // optionals of zero sized types behave like bools, not pointers
  35788                     const payload_ty = opt_child.toType();
  35789                     if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) {
  35790                         return null;
  35791                     }
  35792 
  35793                     return payload_ty;
  35794                 },
  35795             },
  35796             else => null,
  35797         },
  35798         else => null,
  35799     };
  35800 }
  35801 
  35802 /// `generic_poison` will return false.
  35803 /// This function returns false negatives when structs and unions are having their
  35804 /// field types resolved.
  35805 /// TODO assert the return value matches `ty.comptimeOnly`
  35806 /// TODO merge these implementations together with the "advanced"/opt_sema pattern seen
  35807 /// elsewhere in value.zig
  35808 pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
  35809     const mod = sema.mod;
  35810     return switch (ty.toIntern()) {
  35811         .empty_struct_type => false,
  35812 
  35813         else => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  35814             .int_type => return false,
  35815             .ptr_type => |ptr_type| {
  35816                 const child_ty = ptr_type.child.toType();
  35817                 if (child_ty.zigTypeTag(mod) == .Fn) {
  35818                     return mod.typeToFunc(child_ty).?.is_generic;
  35819                 } else {
  35820                     return sema.typeRequiresComptime(child_ty);
  35821                 }
  35822             },
  35823             .anyframe_type => |child| {
  35824                 if (child == .none) return false;
  35825                 return sema.typeRequiresComptime(child.toType());
  35826             },
  35827             .array_type => |array_type| return sema.typeRequiresComptime(array_type.child.toType()),
  35828             .vector_type => |vector_type| return sema.typeRequiresComptime(vector_type.child.toType()),
  35829             .opt_type => |child| return sema.typeRequiresComptime(child.toType()),
  35830 
  35831             .error_union_type => |error_union_type| {
  35832                 return sema.typeRequiresComptime(error_union_type.payload_type.toType());
  35833             },
  35834 
  35835             .error_set_type, .inferred_error_set_type => false,
  35836 
  35837             .func_type => true,
  35838 
  35839             .simple_type => |t| return switch (t) {
  35840                 .f16,
  35841                 .f32,
  35842                 .f64,
  35843                 .f80,
  35844                 .f128,
  35845                 .usize,
  35846                 .isize,
  35847                 .c_char,
  35848                 .c_short,
  35849                 .c_ushort,
  35850                 .c_int,
  35851                 .c_uint,
  35852                 .c_long,
  35853                 .c_ulong,
  35854                 .c_longlong,
  35855                 .c_ulonglong,
  35856                 .c_longdouble,
  35857                 .anyopaque,
  35858                 .bool,
  35859                 .void,
  35860                 .anyerror,
  35861                 .noreturn,
  35862                 .generic_poison,
  35863                 .atomic_order,
  35864                 .atomic_rmw_op,
  35865                 .calling_convention,
  35866                 .address_space,
  35867                 .float_mode,
  35868                 .reduce_op,
  35869                 .call_modifier,
  35870                 .prefetch_options,
  35871                 .export_options,
  35872                 .extern_options,
  35873                 => false,
  35874 
  35875                 .type,
  35876                 .comptime_int,
  35877                 .comptime_float,
  35878                 .null,
  35879                 .undefined,
  35880                 .enum_literal,
  35881                 .type_info,
  35882                 => true,
  35883             },
  35884             .struct_type => |struct_type| {
  35885                 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false;
  35886                 switch (struct_obj.requires_comptime) {
  35887                     .no, .wip => return false,
  35888                     .yes => return true,
  35889                     .unknown => {
  35890                         if (struct_obj.status == .field_types_wip)
  35891                             return false;
  35892 
  35893                         try sema.resolveTypeFieldsStruct(ty, struct_obj);
  35894 
  35895                         struct_obj.requires_comptime = .wip;
  35896                         for (struct_obj.fields.values()) |field| {
  35897                             if (field.is_comptime) continue;
  35898                             if (try sema.typeRequiresComptime(field.ty)) {
  35899                                 struct_obj.requires_comptime = .yes;
  35900                                 return true;
  35901                             }
  35902                         }
  35903                         struct_obj.requires_comptime = .no;
  35904                         return false;
  35905                     },
  35906                 }
  35907             },
  35908             .anon_struct_type => |tuple| {
  35909                 for (tuple.types, tuple.values) |field_ty, val| {
  35910                     const have_comptime_val = val != .none;
  35911                     if (!have_comptime_val and try sema.typeRequiresComptime(field_ty.toType())) {
  35912                         return true;
  35913                     }
  35914                 }
  35915                 return false;
  35916             },
  35917 
  35918             .union_type => |union_type| {
  35919                 const union_obj = mod.unionPtr(union_type.index);
  35920                 switch (union_obj.requires_comptime) {
  35921                     .no, .wip => return false,
  35922                     .yes => return true,
  35923                     .unknown => {
  35924                         if (union_obj.status == .field_types_wip)
  35925                             return false;
  35926 
  35927                         try sema.resolveTypeFieldsUnion(ty, union_obj);
  35928 
  35929                         union_obj.requires_comptime = .wip;
  35930                         for (union_obj.fields.values()) |field| {
  35931                             if (try sema.typeRequiresComptime(field.ty)) {
  35932                                 union_obj.requires_comptime = .yes;
  35933                                 return true;
  35934                             }
  35935                         }
  35936                         union_obj.requires_comptime = .no;
  35937                         return false;
  35938                     },
  35939                 }
  35940             },
  35941 
  35942             .opaque_type => false,
  35943             .enum_type => |enum_type| try sema.typeRequiresComptime(enum_type.tag_ty.toType()),
  35944 
  35945             // values, not types
  35946             .undef,
  35947             .runtime_value,
  35948             .simple_value,
  35949             .variable,
  35950             .extern_func,
  35951             .func,
  35952             .int,
  35953             .err,
  35954             .error_union,
  35955             .enum_literal,
  35956             .enum_tag,
  35957             .empty_enum_value,
  35958             .float,
  35959             .ptr,
  35960             .opt,
  35961             .aggregate,
  35962             .un,
  35963             // memoization, not types
  35964             .memoized_call,
  35965             => unreachable,
  35966         },
  35967     };
  35968 }
  35969 
  35970 pub fn typeHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {
  35971     const mod = sema.mod;
  35972     return ty.hasRuntimeBitsAdvanced(mod, false, .{ .sema = sema }) catch |err| switch (err) {
  35973         error.NeedLazy => unreachable,
  35974         else => |e| return e,
  35975     };
  35976 }
  35977 
  35978 fn typeAbiSize(sema: *Sema, ty: Type) !u64 {
  35979     try sema.resolveTypeLayout(ty);
  35980     return ty.abiSize(sema.mod);
  35981 }
  35982 
  35983 fn typeAbiAlignment(sema: *Sema, ty: Type) CompileError!u32 {
  35984     return (try ty.abiAlignmentAdvanced(sema.mod, .{ .sema = sema })).scalar;
  35985 }
  35986 
  35987 /// Not valid to call for packed unions.
  35988 /// Keep implementation in sync with `Module.Union.Field.normalAlignment`.
  35989 fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 {
  35990     return @as(u32, @intCast(if (field.ty.isNoReturn(sema.mod))
  35991         0
  35992     else
  35993         field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty)));
  35994 }
  35995 
  35996 /// Keep implementation in sync with `Module.Struct.Field.alignment`.
  35997 fn structFieldAlignment(sema: *Sema, field: Module.Struct.Field, layout: std.builtin.Type.ContainerLayout) !u32 {
  35998     const mod = sema.mod;
  35999     if (field.abi_align.toByteUnitsOptional()) |a| {
  36000         assert(layout != .Packed);
  36001         return @as(u32, @intCast(a));
  36002     }
  36003     switch (layout) {
  36004         .Packed => return 0,
  36005         .Auto => if (mod.getTarget().ofmt != .c) {
  36006             return sema.typeAbiAlignment(field.ty);
  36007         },
  36008         .Extern => {},
  36009     }
  36010     // extern
  36011     const ty_abi_align = try sema.typeAbiAlignment(field.ty);
  36012     if (field.ty.isAbiInt(mod) and field.ty.intInfo(mod).bits >= 128) {
  36013         return @max(ty_abi_align, 16);
  36014     }
  36015     return ty_abi_align;
  36016 }
  36017 
  36018 /// Synchronize logic with `Type.isFnOrHasRuntimeBits`.
  36019 pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {
  36020     const mod = sema.mod;
  36021     const fn_info = mod.typeToFunc(ty).?;
  36022     if (fn_info.is_generic) return false;
  36023     if (fn_info.is_var_args) return true;
  36024     switch (fn_info.cc) {
  36025         // If there was a comptime calling convention, it should also return false here.
  36026         .Inline => return false,
  36027         else => {},
  36028     }
  36029     if (try sema.typeRequiresComptime(fn_info.return_type.toType())) {
  36030         return false;
  36031     }
  36032     return true;
  36033 }
  36034 
  36035 fn unionFieldIndex(
  36036     sema: *Sema,
  36037     block: *Block,
  36038     unresolved_union_ty: Type,
  36039     field_name: InternPool.NullTerminatedString,
  36040     field_src: LazySrcLoc,
  36041 ) !u32 {
  36042     const mod = sema.mod;
  36043     const union_ty = try sema.resolveTypeFields(unresolved_union_ty);
  36044     const union_obj = mod.typeToUnion(union_ty).?;
  36045     const field_index_usize = union_obj.fields.getIndex(field_name) orelse
  36046         return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
  36047     return @as(u32, @intCast(field_index_usize));
  36048 }
  36049 
  36050 fn structFieldIndex(
  36051     sema: *Sema,
  36052     block: *Block,
  36053     unresolved_struct_ty: Type,
  36054     field_name: InternPool.NullTerminatedString,
  36055     field_src: LazySrcLoc,
  36056 ) !u32 {
  36057     const mod = sema.mod;
  36058     const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty);
  36059     if (struct_ty.isAnonStruct(mod)) {
  36060         return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src);
  36061     } else {
  36062         const struct_obj = mod.typeToStruct(struct_ty).?;
  36063         const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
  36064             return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
  36065         return @as(u32, @intCast(field_index_usize));
  36066     }
  36067 }
  36068 
  36069 fn anonStructFieldIndex(
  36070     sema: *Sema,
  36071     block: *Block,
  36072     struct_ty: Type,
  36073     field_name: InternPool.NullTerminatedString,
  36074     field_src: LazySrcLoc,
  36075 ) !u32 {
  36076     const mod = sema.mod;
  36077     switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) {
  36078         .anon_struct_type => |anon_struct_type| for (anon_struct_type.names, 0..) |name, i| {
  36079             if (name == field_name) return @as(u32, @intCast(i));
  36080         },
  36081         .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| {
  36082             for (struct_obj.fields.keys(), 0..) |name, i| {
  36083                 if (name == field_name) {
  36084                     return @as(u32, @intCast(i));
  36085                 }
  36086             }
  36087         },
  36088         else => unreachable,
  36089     }
  36090     return sema.fail(block, field_src, "no field named '{}' in anonymous struct '{}'", .{
  36091         field_name.fmt(&mod.intern_pool), struct_ty.fmt(sema.mod),
  36092     });
  36093 }
  36094 
  36095 fn queueFullTypeResolution(sema: *Sema, ty: Type) !void {
  36096     try sema.types_to_resolve.put(sema.gpa, ty.toIntern(), {});
  36097 }
  36098 
  36099 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
  36100 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
  36101 fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value {
  36102     var overflow: usize = undefined;
  36103     return sema.intAddInner(lhs, rhs, ty, &overflow) catch |err| switch (err) {
  36104         error.Overflow => {
  36105             const is_vec = ty.isVector(sema.mod);
  36106             overflow_idx.* = if (is_vec) overflow else 0;
  36107             const safe_ty = if (is_vec) try sema.mod.vectorType(.{
  36108                 .len = ty.vectorLen(sema.mod),
  36109                 .child = .comptime_int_type,
  36110             }) else Type.comptime_int;
  36111             return sema.intAddInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) {
  36112                 error.Overflow => unreachable,
  36113                 else => |e| return e,
  36114             };
  36115         },
  36116         else => |e| return e,
  36117     };
  36118 }
  36119 
  36120 fn intAddInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value {
  36121     const mod = sema.mod;
  36122     if (ty.zigTypeTag(mod) == .Vector) {
  36123         const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
  36124         const scalar_ty = ty.scalarType(mod);
  36125         for (result_data, 0..) |*scalar, i| {
  36126             const lhs_elem = try lhs.elemValue(mod, i);
  36127             const rhs_elem = try rhs.elemValue(mod, i);
  36128             const val = sema.intAddScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) {
  36129                 error.Overflow => {
  36130                     overflow_idx.* = i;
  36131                     return error.Overflow;
  36132                 },
  36133                 else => |e| return e,
  36134             };
  36135             scalar.* = try val.intern(scalar_ty, mod);
  36136         }
  36137         return (try mod.intern(.{ .aggregate = .{
  36138             .ty = ty.toIntern(),
  36139             .storage = .{ .elems = result_data },
  36140         } })).toValue();
  36141     }
  36142     return sema.intAddScalar(lhs, rhs, ty);
  36143 }
  36144 
  36145 fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
  36146     const mod = sema.mod;
  36147     if (scalar_ty.toIntern() != .comptime_int_type) {
  36148         const res = try sema.intAddWithOverflowScalar(lhs, rhs, scalar_ty);
  36149         if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
  36150         return res.wrapped_result;
  36151     }
  36152     // TODO is this a performance issue? maybe we should try the operation without
  36153     // resorting to BigInt first.
  36154     var lhs_space: Value.BigIntSpace = undefined;
  36155     var rhs_space: Value.BigIntSpace = undefined;
  36156     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  36157     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  36158     const limbs = try sema.arena.alloc(
  36159         std.math.big.Limb,
  36160         @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
  36161     );
  36162     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  36163     result_bigint.add(lhs_bigint, rhs_bigint);
  36164     return mod.intValue_big(scalar_ty, result_bigint.toConst());
  36165 }
  36166 
  36167 /// Supports both floats and ints; handles undefined.
  36168 fn numberAddWrapScalar(
  36169     sema: *Sema,
  36170     lhs: Value,
  36171     rhs: Value,
  36172     ty: Type,
  36173 ) !Value {
  36174     const mod = sema.mod;
  36175     if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
  36176 
  36177     if (ty.zigTypeTag(mod) == .ComptimeInt) {
  36178         return sema.intAdd(lhs, rhs, ty, undefined);
  36179     }
  36180 
  36181     if (ty.isAnyFloat()) {
  36182         return Value.floatAdd(lhs, rhs, ty, sema.arena, mod);
  36183     }
  36184 
  36185     const overflow_result = try sema.intAddWithOverflow(lhs, rhs, ty);
  36186     return overflow_result.wrapped_result;
  36187 }
  36188 
  36189 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
  36190 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
  36191 fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value {
  36192     var overflow: usize = undefined;
  36193     return sema.intSubInner(lhs, rhs, ty, &overflow) catch |err| switch (err) {
  36194         error.Overflow => {
  36195             const is_vec = ty.isVector(sema.mod);
  36196             overflow_idx.* = if (is_vec) overflow else 0;
  36197             const safe_ty = if (is_vec) try sema.mod.vectorType(.{
  36198                 .len = ty.vectorLen(sema.mod),
  36199                 .child = .comptime_int_type,
  36200             }) else Type.comptime_int;
  36201             return sema.intSubInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) {
  36202                 error.Overflow => unreachable,
  36203                 else => |e| return e,
  36204             };
  36205         },
  36206         else => |e| return e,
  36207     };
  36208 }
  36209 
  36210 fn intSubInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value {
  36211     const mod = sema.mod;
  36212     if (ty.zigTypeTag(mod) == .Vector) {
  36213         const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
  36214         const scalar_ty = ty.scalarType(mod);
  36215         for (result_data, 0..) |*scalar, i| {
  36216             const lhs_elem = try lhs.elemValue(sema.mod, i);
  36217             const rhs_elem = try rhs.elemValue(sema.mod, i);
  36218             const val = sema.intSubScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) {
  36219                 error.Overflow => {
  36220                     overflow_idx.* = i;
  36221                     return error.Overflow;
  36222                 },
  36223                 else => |e| return e,
  36224             };
  36225             scalar.* = try val.intern(scalar_ty, mod);
  36226         }
  36227         return (try mod.intern(.{ .aggregate = .{
  36228             .ty = ty.toIntern(),
  36229             .storage = .{ .elems = result_data },
  36230         } })).toValue();
  36231     }
  36232     return sema.intSubScalar(lhs, rhs, ty);
  36233 }
  36234 
  36235 fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
  36236     const mod = sema.mod;
  36237     if (scalar_ty.toIntern() != .comptime_int_type) {
  36238         const res = try sema.intSubWithOverflowScalar(lhs, rhs, scalar_ty);
  36239         if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
  36240         return res.wrapped_result;
  36241     }
  36242     // TODO is this a performance issue? maybe we should try the operation without
  36243     // resorting to BigInt first.
  36244     var lhs_space: Value.BigIntSpace = undefined;
  36245     var rhs_space: Value.BigIntSpace = undefined;
  36246     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  36247     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  36248     const limbs = try sema.arena.alloc(
  36249         std.math.big.Limb,
  36250         @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
  36251     );
  36252     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  36253     result_bigint.sub(lhs_bigint, rhs_bigint);
  36254     return mod.intValue_big(scalar_ty, result_bigint.toConst());
  36255 }
  36256 
  36257 /// Supports both floats and ints; handles undefined.
  36258 fn numberSubWrapScalar(
  36259     sema: *Sema,
  36260     lhs: Value,
  36261     rhs: Value,
  36262     ty: Type,
  36263 ) !Value {
  36264     const mod = sema.mod;
  36265     if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
  36266 
  36267     if (ty.zigTypeTag(mod) == .ComptimeInt) {
  36268         return sema.intSub(lhs, rhs, ty, undefined);
  36269     }
  36270 
  36271     if (ty.isAnyFloat()) {
  36272         return Value.floatSub(lhs, rhs, ty, sema.arena, mod);
  36273     }
  36274 
  36275     const overflow_result = try sema.intSubWithOverflow(lhs, rhs, ty);
  36276     return overflow_result.wrapped_result;
  36277 }
  36278 
  36279 fn intSubWithOverflow(
  36280     sema: *Sema,
  36281     lhs: Value,
  36282     rhs: Value,
  36283     ty: Type,
  36284 ) !Value.OverflowArithmeticResult {
  36285     const mod = sema.mod;
  36286     if (ty.zigTypeTag(mod) == .Vector) {
  36287         const vec_len = ty.vectorLen(mod);
  36288         const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len);
  36289         const result_data = try sema.arena.alloc(InternPool.Index, vec_len);
  36290         const scalar_ty = ty.scalarType(mod);
  36291         for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
  36292             const lhs_elem = try lhs.elemValue(sema.mod, i);
  36293             const rhs_elem = try rhs.elemValue(sema.mod, i);
  36294             const of_math_result = try sema.intSubWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty);
  36295             of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
  36296             scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
  36297         }
  36298         return Value.OverflowArithmeticResult{
  36299             .overflow_bit = (try mod.intern(.{ .aggregate = .{
  36300                 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
  36301                 .storage = .{ .elems = overflowed_data },
  36302             } })).toValue(),
  36303             .wrapped_result = (try mod.intern(.{ .aggregate = .{
  36304                 .ty = ty.toIntern(),
  36305                 .storage = .{ .elems = result_data },
  36306             } })).toValue(),
  36307         };
  36308     }
  36309     return sema.intSubWithOverflowScalar(lhs, rhs, ty);
  36310 }
  36311 
  36312 fn intSubWithOverflowScalar(
  36313     sema: *Sema,
  36314     lhs: Value,
  36315     rhs: Value,
  36316     ty: Type,
  36317 ) !Value.OverflowArithmeticResult {
  36318     const mod = sema.mod;
  36319     const info = ty.intInfo(mod);
  36320 
  36321     var lhs_space: Value.BigIntSpace = undefined;
  36322     var rhs_space: Value.BigIntSpace = undefined;
  36323     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  36324     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  36325     const limbs = try sema.arena.alloc(
  36326         std.math.big.Limb,
  36327         std.math.big.int.calcTwosCompLimbCount(info.bits),
  36328     );
  36329     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  36330     const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
  36331     const wrapped_result = try mod.intValue_big(ty, result_bigint.toConst());
  36332     return Value.OverflowArithmeticResult{
  36333         .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
  36334         .wrapped_result = wrapped_result,
  36335     };
  36336 }
  36337 
  36338 fn intFromFloat(
  36339     sema: *Sema,
  36340     block: *Block,
  36341     src: LazySrcLoc,
  36342     val: Value,
  36343     float_ty: Type,
  36344     int_ty: Type,
  36345 ) CompileError!Value {
  36346     const mod = sema.mod;
  36347     if (float_ty.zigTypeTag(mod) == .Vector) {
  36348         const elem_ty = float_ty.scalarType(mod);
  36349         const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(mod));
  36350         const scalar_ty = int_ty.scalarType(mod);
  36351         for (result_data, 0..) |*scalar, i| {
  36352             const elem_val = try val.elemValue(sema.mod, i);
  36353             scalar.* = try (try sema.intFromFloatScalar(block, src, elem_val, elem_ty, int_ty.scalarType(mod))).intern(scalar_ty, mod);
  36354         }
  36355         return (try mod.intern(.{ .aggregate = .{
  36356             .ty = int_ty.toIntern(),
  36357             .storage = .{ .elems = result_data },
  36358         } })).toValue();
  36359     }
  36360     return sema.intFromFloatScalar(block, src, val, float_ty, int_ty);
  36361 }
  36362 
  36363 // float is expected to be finite and non-NaN
  36364 fn float128IntPartToBigInt(
  36365     arena: Allocator,
  36366     float: f128,
  36367 ) !std.math.big.int.Managed {
  36368     const is_negative = std.math.signbit(float);
  36369     const floored = @floor(@fabs(float));
  36370 
  36371     var rational = try std.math.big.Rational.init(arena);
  36372     defer rational.q.deinit();
  36373     rational.setFloat(f128, floored) catch |err| switch (err) {
  36374         error.NonFiniteFloat => unreachable,
  36375         error.OutOfMemory => return error.OutOfMemory,
  36376     };
  36377 
  36378     // The float is reduced in rational.setFloat, so we assert that denominator is equal to one
  36379     const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
  36380     assert(rational.q.toConst().eqlAbs(big_one));
  36381 
  36382     if (is_negative) {
  36383         rational.negate();
  36384     }
  36385     return rational.p;
  36386 }
  36387 
  36388 fn intFromFloatScalar(
  36389     sema: *Sema,
  36390     block: *Block,
  36391     src: LazySrcLoc,
  36392     val: Value,
  36393     float_ty: Type,
  36394     int_ty: Type,
  36395 ) CompileError!Value {
  36396     const mod = sema.mod;
  36397 
  36398     const float = val.toFloat(f128, mod);
  36399     if (std.math.isNan(float)) {
  36400         return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{
  36401             int_ty.fmt(sema.mod),
  36402         });
  36403     }
  36404     if (std.math.isInf(float)) {
  36405         return sema.fail(block, src, "float value Inf cannot be stored in integer type '{}'", .{
  36406             int_ty.fmt(sema.mod),
  36407         });
  36408     }
  36409 
  36410     var big_int = try float128IntPartToBigInt(sema.arena, float);
  36411     defer big_int.deinit();
  36412 
  36413     const cti_result = try mod.intValue_big(Type.comptime_int, big_int.toConst());
  36414 
  36415     if (!(try sema.intFitsInType(cti_result, int_ty, null))) {
  36416         return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{
  36417             val.fmtValue(float_ty, sema.mod), int_ty.fmt(sema.mod),
  36418         });
  36419     }
  36420     return mod.getCoerced(cti_result, int_ty);
  36421 }
  36422 
  36423 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
  36424 /// Vectors are also accepted. Vector results are reduced with AND.
  36425 ///
  36426 /// If provided, `vector_index` reports the first element that failed the range check.
  36427 fn intFitsInType(
  36428     sema: *Sema,
  36429     val: Value,
  36430     ty: Type,
  36431     vector_index: ?*usize,
  36432 ) CompileError!bool {
  36433     const mod = sema.mod;
  36434     if (ty.toIntern() == .comptime_int_type) return true;
  36435     const info = ty.intInfo(mod);
  36436     switch (val.toIntern()) {
  36437         .zero_usize, .zero_u8 => return true,
  36438         else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
  36439             .undef => return true,
  36440             .variable, .extern_func, .func, .ptr => {
  36441                 const target = mod.getTarget();
  36442                 const ptr_bits = target.ptrBitWidth();
  36443                 return switch (info.signedness) {
  36444                     .signed => info.bits > ptr_bits,
  36445                     .unsigned => info.bits >= ptr_bits,
  36446                 };
  36447             },
  36448             .int => |int| switch (int.storage) {
  36449                 .u64, .i64, .big_int => {
  36450                     var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
  36451                     const big_int = int.storage.toBigInt(&buffer);
  36452                     return big_int.fitsInTwosComp(info.signedness, info.bits);
  36453                 },
  36454                 .lazy_align => |lazy_ty| {
  36455                     const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed);
  36456                     // If it is u16 or bigger we know the alignment fits without resolving it.
  36457                     if (info.bits >= max_needed_bits) return true;
  36458                     const x = try sema.typeAbiAlignment(lazy_ty.toType());
  36459                     if (x == 0) return true;
  36460                     const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed);
  36461                     return info.bits >= actual_needed_bits;
  36462                 },
  36463                 .lazy_size => |lazy_ty| {
  36464                     const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed);
  36465                     // If it is u64 or bigger we know the size fits without resolving it.
  36466                     if (info.bits >= max_needed_bits) return true;
  36467                     const x = try sema.typeAbiSize(lazy_ty.toType());
  36468                     if (x == 0) return true;
  36469                     const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed);
  36470                     return info.bits >= actual_needed_bits;
  36471                 },
  36472             },
  36473             .aggregate => |aggregate| {
  36474                 assert(ty.zigTypeTag(mod) == .Vector);
  36475                 return switch (aggregate.storage) {
  36476                     .bytes => |bytes| for (bytes, 0..) |byte, i| {
  36477                         if (byte == 0) continue;
  36478                         const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed);
  36479                         if (info.bits >= actual_needed_bits) continue;
  36480                         if (vector_index) |vi| vi.* = i;
  36481                         break false;
  36482                     } else true,
  36483                     .elems, .repeated_elem => for (switch (aggregate.storage) {
  36484                         .bytes => unreachable,
  36485                         .elems => |elems| elems,
  36486                         .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem),
  36487                     }, 0..) |elem, i| {
  36488                         if (try sema.intFitsInType(elem.toValue(), ty.scalarType(mod), null)) continue;
  36489                         if (vector_index) |vi| vi.* = i;
  36490                         break false;
  36491                     } else true,
  36492                 };
  36493             },
  36494             else => unreachable,
  36495         },
  36496     }
  36497 }
  36498 
  36499 fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool {
  36500     const mod = sema.mod;
  36501     if (!(try int_val.compareAllWithZeroAdvanced(.gte, sema))) return false;
  36502     const end_val = try mod.intValue(tag_ty, end);
  36503     if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false;
  36504     return true;
  36505 }
  36506 
  36507 /// Asserts the type is an enum.
  36508 fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
  36509     const mod = sema.mod;
  36510     const enum_type = mod.intern_pool.indexToKey(ty.toIntern()).enum_type;
  36511     assert(enum_type.tag_mode != .nonexhaustive);
  36512     // The `tagValueIndex` function call below relies on the type being the integer tag type.
  36513     // `getCoerced` assumes the value will fit the new type.
  36514     if (!(try sema.intFitsInType(int, enum_type.tag_ty.toType(), null))) return false;
  36515     const int_coerced = try mod.getCoerced(int, enum_type.tag_ty.toType());
  36516 
  36517     return enum_type.tagValueIndex(&mod.intern_pool, int_coerced.toIntern()) != null;
  36518 }
  36519 
  36520 fn intAddWithOverflow(
  36521     sema: *Sema,
  36522     lhs: Value,
  36523     rhs: Value,
  36524     ty: Type,
  36525 ) !Value.OverflowArithmeticResult {
  36526     const mod = sema.mod;
  36527     if (ty.zigTypeTag(mod) == .Vector) {
  36528         const vec_len = ty.vectorLen(mod);
  36529         const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len);
  36530         const result_data = try sema.arena.alloc(InternPool.Index, vec_len);
  36531         const scalar_ty = ty.scalarType(mod);
  36532         for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
  36533             const lhs_elem = try lhs.elemValue(sema.mod, i);
  36534             const rhs_elem = try rhs.elemValue(sema.mod, i);
  36535             const of_math_result = try sema.intAddWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty);
  36536             of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
  36537             scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
  36538         }
  36539         return Value.OverflowArithmeticResult{
  36540             .overflow_bit = (try mod.intern(.{ .aggregate = .{
  36541                 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
  36542                 .storage = .{ .elems = overflowed_data },
  36543             } })).toValue(),
  36544             .wrapped_result = (try mod.intern(.{ .aggregate = .{
  36545                 .ty = ty.toIntern(),
  36546                 .storage = .{ .elems = result_data },
  36547             } })).toValue(),
  36548         };
  36549     }
  36550     return sema.intAddWithOverflowScalar(lhs, rhs, ty);
  36551 }
  36552 
  36553 fn intAddWithOverflowScalar(
  36554     sema: *Sema,
  36555     lhs: Value,
  36556     rhs: Value,
  36557     ty: Type,
  36558 ) !Value.OverflowArithmeticResult {
  36559     const mod = sema.mod;
  36560     const info = ty.intInfo(mod);
  36561 
  36562     var lhs_space: Value.BigIntSpace = undefined;
  36563     var rhs_space: Value.BigIntSpace = undefined;
  36564     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  36565     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  36566     const limbs = try sema.arena.alloc(
  36567         std.math.big.Limb,
  36568         std.math.big.int.calcTwosCompLimbCount(info.bits),
  36569     );
  36570     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  36571     const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
  36572     const result = try mod.intValue_big(ty, result_bigint.toConst());
  36573     return Value.OverflowArithmeticResult{
  36574         .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
  36575         .wrapped_result = result,
  36576     };
  36577 }
  36578 
  36579 /// Asserts the values are comparable. Both operands have type `ty`.
  36580 /// For vectors, returns true if the comparison is true for ALL elements.
  36581 ///
  36582 /// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)`
  36583 fn compareAll(
  36584     sema: *Sema,
  36585     lhs: Value,
  36586     op: std.math.CompareOperator,
  36587     rhs: Value,
  36588     ty: Type,
  36589 ) CompileError!bool {
  36590     const mod = sema.mod;
  36591     if (ty.zigTypeTag(mod) == .Vector) {
  36592         var i: usize = 0;
  36593         while (i < ty.vectorLen(mod)) : (i += 1) {
  36594             const lhs_elem = try lhs.elemValue(sema.mod, i);
  36595             const rhs_elem = try rhs.elemValue(sema.mod, i);
  36596             if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)))) {
  36597                 return false;
  36598             }
  36599         }
  36600         return true;
  36601     }
  36602     return sema.compareScalar(lhs, op, rhs, ty);
  36603 }
  36604 
  36605 /// Asserts the values are comparable. Both operands have type `ty`.
  36606 fn compareScalar(
  36607     sema: *Sema,
  36608     lhs: Value,
  36609     op: std.math.CompareOperator,
  36610     rhs: Value,
  36611     ty: Type,
  36612 ) CompileError!bool {
  36613     const mod = sema.mod;
  36614     const coerced_lhs = try mod.getCoerced(lhs, ty);
  36615     const coerced_rhs = try mod.getCoerced(rhs, ty);
  36616     switch (op) {
  36617         .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty),
  36618         .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)),
  36619         else => return Value.compareHeteroAdvanced(coerced_lhs, op, coerced_rhs, mod, sema),
  36620     }
  36621 }
  36622 
  36623 fn valuesEqual(
  36624     sema: *Sema,
  36625     lhs: Value,
  36626     rhs: Value,
  36627     ty: Type,
  36628 ) CompileError!bool {
  36629     return lhs.eql(rhs, ty, sema.mod);
  36630 }
  36631 
  36632 /// Asserts the values are comparable vectors of type `ty`.
  36633 fn compareVector(
  36634     sema: *Sema,
  36635     lhs: Value,
  36636     op: std.math.CompareOperator,
  36637     rhs: Value,
  36638     ty: Type,
  36639 ) !Value {
  36640     const mod = sema.mod;
  36641     assert(ty.zigTypeTag(mod) == .Vector);
  36642     const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
  36643     for (result_data, 0..) |*scalar, i| {
  36644         const lhs_elem = try lhs.elemValue(sema.mod, i);
  36645         const rhs_elem = try rhs.elemValue(sema.mod, i);
  36646         const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod));
  36647         scalar.* = try Value.makeBool(res_bool).intern(Type.bool, mod);
  36648     }
  36649     return (try mod.intern(.{ .aggregate = .{
  36650         .ty = (try mod.vectorType(.{ .len = ty.vectorLen(mod), .child = .bool_type })).toIntern(),
  36651         .storage = .{ .elems = result_data },
  36652     } })).toValue();
  36653 }
  36654 
  36655 /// Returns the type of a pointer to an element.
  36656 /// Asserts that the type is a pointer, and that the element type is indexable.
  36657 /// For *[N]T, return *T
  36658 /// For [*]T, returns *T
  36659 /// For []T, returns *T
  36660 /// Handles const-ness and address spaces in particular.
  36661 /// This code is duplicated in `analyzePtrArithmetic`.
  36662 fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type {
  36663     const mod = sema.mod;
  36664     const ptr_info = ptr_ty.ptrInfo(mod);
  36665     const elem_ty = ptr_ty.elemType2(mod);
  36666     const is_allowzero = ptr_info.flags.is_allowzero and (offset orelse 0) == 0;
  36667     const parent_ty = ptr_ty.childType(mod);
  36668 
  36669     const VI = InternPool.Key.PtrType.VectorIndex;
  36670 
  36671     const vector_info: struct {
  36672         host_size: u16 = 0,
  36673         alignment: u32 = 0,
  36674         vector_index: VI = .none,
  36675     } = if (parent_ty.isVector(mod) and ptr_info.flags.size == .One) blk: {
  36676         const elem_bits = elem_ty.bitSize(mod);
  36677         if (elem_bits == 0) break :blk .{};
  36678         const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits);
  36679         if (!is_packed) break :blk .{};
  36680 
  36681         break :blk .{
  36682             .host_size = @as(u16, @intCast(parent_ty.arrayLen(mod))),
  36683             .alignment = @as(u32, @intCast(parent_ty.abiAlignment(mod))),
  36684             .vector_index = if (offset) |some| @as(VI, @enumFromInt(some)) else .runtime,
  36685         };
  36686     } else .{};
  36687 
  36688     const alignment: Alignment = a: {
  36689         // Calculate the new pointer alignment.
  36690         if (ptr_info.flags.alignment == .none) {
  36691             if (vector_info.alignment != 0) break :a Alignment.fromNonzeroByteUnits(vector_info.alignment);
  36692             // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
  36693             break :a .none;
  36694         }
  36695         // If the addend is not a comptime-known value we can still count on
  36696         // it being a multiple of the type size.
  36697         const elem_size = try sema.typeAbiSize(elem_ty);
  36698         const addend = if (offset) |off| elem_size * off else elem_size;
  36699 
  36700         // The resulting pointer is aligned to the lcd between the offset (an
  36701         // arbitrary number) and the alignment factor (always a power of two,
  36702         // non zero).
  36703         const new_align = @as(Alignment, @enumFromInt(@min(
  36704             @ctz(addend),
  36705             @intFromEnum(ptr_info.flags.alignment),
  36706         )));
  36707         assert(new_align != .none);
  36708         break :a new_align;
  36709     };
  36710     return mod.ptrType(.{
  36711         .child = elem_ty.toIntern(),
  36712         .flags = .{
  36713             .alignment = alignment,
  36714             .is_const = ptr_info.flags.is_const,
  36715             .is_volatile = ptr_info.flags.is_volatile,
  36716             .is_allowzero = is_allowzero,
  36717             .address_space = ptr_info.flags.address_space,
  36718             .vector_index = vector_info.vector_index,
  36719         },
  36720         .packed_offset = .{
  36721             .host_size = vector_info.host_size,
  36722             .bit_offset = 0,
  36723         },
  36724     });
  36725 }
  36726 
  36727 /// Merge lhs with rhs.
  36728 /// Asserts that lhs and rhs are both error sets and are resolved.
  36729 fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type {
  36730     const mod = sema.mod;
  36731     const arena = sema.arena;
  36732     const lhs_names = lhs.errorSetNames(mod);
  36733     const rhs_names = rhs.errorSetNames(mod);
  36734     var names: Module.Fn.InferredErrorSet.NameMap = .{};
  36735     try names.ensureUnusedCapacity(arena, lhs_names.len);
  36736 
  36737     for (lhs_names) |name| {
  36738         names.putAssumeCapacityNoClobber(name, {});
  36739     }
  36740     for (rhs_names) |name| {
  36741         try names.put(arena, name, {});
  36742     }
  36743 
  36744     return mod.errorSetFromUnsortedNames(names.keys());
  36745 }
  36746 
  36747 /// Avoids crashing the compiler when asking if inferred allocations are noreturn.
  36748 fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool {
  36749     if (ref == .unreachable_value) return true;
  36750     if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) {
  36751         .inferred_alloc, .inferred_alloc_comptime => return false,
  36752         else => {},
  36753     };
  36754     return sema.typeOf(ref).isNoReturn(sema.mod);
  36755 }
  36756 
  36757 /// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type.
  36758 fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool {
  36759     if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) {
  36760         .inferred_alloc, .inferred_alloc_comptime => return false,
  36761         else => {},
  36762     };
  36763     return sema.typeOf(ref).zigTypeTag(sema.mod) == tag;
  36764 }