zig

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

blob 0681de4a (1566898B) - 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                             const is_comptime = field_val != .none;
  17297                             const opt_default_val = if (is_comptime) field_val.toValue() else null;
  17298                             const default_val_ptr = try sema.optRefValue(block, field_ty.toType(), opt_default_val);
  17299                             const struct_field_fields = .{
  17300                                 // name: []const u8,
  17301                                 name_val,
  17302                                 // type: type,
  17303                                 field_ty,
  17304                                 // default_value: ?*const anyopaque,
  17305                                 default_val_ptr.toIntern(),
  17306                                 // is_comptime: bool,
  17307                                 Value.makeBool(is_comptime).toIntern(),
  17308                                 // alignment: comptime_int,
  17309                                 (try mod.intValue(Type.comptime_int, field_ty.toType().abiAlignment(mod))).toIntern(),
  17310                             };
  17311                             struct_field_val.* = try mod.intern(.{ .aggregate = .{
  17312                                 .ty = struct_field_ty.toIntern(),
  17313                                 .storage = .{ .elems = &struct_field_fields },
  17314                             } });
  17315                         }
  17316                         break :fv;
  17317                     },
  17318                     .struct_type => |s| s,
  17319                     else => unreachable,
  17320                 };
  17321                 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse break :fv;
  17322                 struct_field_vals = try gpa.alloc(InternPool.Index, struct_obj.fields.count());
  17323 
  17324                 for (
  17325                     struct_field_vals,
  17326                     struct_obj.fields.keys(),
  17327                     struct_obj.fields.values(),
  17328                 ) |*field_val, name_nts, field| {
  17329                     // TODO: write something like getCoercedInts to avoid needing to dupe
  17330                     const name = try sema.arena.dupe(u8, ip.stringToSlice(name_nts));
  17331                     const name_val = v: {
  17332                         var anon_decl = try block.startAnonDecl();
  17333                         defer anon_decl.deinit();
  17334                         const new_decl_ty = try mod.arrayType(.{
  17335                             .len = name.len,
  17336                             .child = .u8_type,
  17337                         });
  17338                         const new_decl = try anon_decl.finish(
  17339                             new_decl_ty,
  17340                             (try mod.intern(.{ .aggregate = .{
  17341                                 .ty = new_decl_ty.toIntern(),
  17342                                 .storage = .{ .bytes = name },
  17343                             } })).toValue(),
  17344                             .none, // default alignment
  17345                         );
  17346                         break :v try mod.intern(.{ .ptr = .{
  17347                             .ty = .slice_const_u8_type,
  17348                             .addr = .{ .decl = new_decl },
  17349                             .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  17350                         } });
  17351                     };
  17352 
  17353                     const opt_default_val = if (field.default_val == .none)
  17354                         null
  17355                     else
  17356                         field.default_val.toValue();
  17357                     const default_val_ptr = try sema.optRefValue(block, field.ty, opt_default_val);
  17358                     const alignment = field.alignment(mod, layout);
  17359 
  17360                     const struct_field_fields = .{
  17361                         // name: []const u8,
  17362                         name_val,
  17363                         // type: type,
  17364                         field.ty.toIntern(),
  17365                         // default_value: ?*const anyopaque,
  17366                         default_val_ptr.toIntern(),
  17367                         // is_comptime: bool,
  17368                         Value.makeBool(field.is_comptime).toIntern(),
  17369                         // alignment: comptime_int,
  17370                         (try mod.intValue(Type.comptime_int, alignment)).toIntern(),
  17371                     };
  17372                     field_val.* = try mod.intern(.{ .aggregate = .{
  17373                         .ty = struct_field_ty.toIntern(),
  17374                         .storage = .{ .elems = &struct_field_fields },
  17375                     } });
  17376                 }
  17377             }
  17378 
  17379             const fields_val = v: {
  17380                 const array_fields_ty = try mod.arrayType(.{
  17381                     .len = struct_field_vals.len,
  17382                     .child = struct_field_ty.toIntern(),
  17383                 });
  17384                 const new_decl = try fields_anon_decl.finish(
  17385                     array_fields_ty,
  17386                     (try mod.intern(.{ .aggregate = .{
  17387                         .ty = array_fields_ty.toIntern(),
  17388                         .storage = .{ .elems = struct_field_vals },
  17389                     } })).toValue(),
  17390                     .none, // default alignment
  17391                 );
  17392                 break :v try mod.intern(.{ .ptr = .{
  17393                     .ty = (try mod.ptrType(.{
  17394                         .child = struct_field_ty.toIntern(),
  17395                         .flags = .{
  17396                             .size = .Slice,
  17397                             .is_const = true,
  17398                         },
  17399                     })).toIntern(),
  17400                     .addr = .{ .decl = new_decl },
  17401                     .len = (try mod.intValue(Type.usize, struct_field_vals.len)).toIntern(),
  17402                 } });
  17403             };
  17404 
  17405             const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespaceIndex(mod));
  17406 
  17407             const backing_integer_val = try mod.intern(.{ .opt = .{
  17408                 .ty = (try mod.optionalType(.type_type)).toIntern(),
  17409                 .val = if (layout == .Packed) val: {
  17410                     const struct_obj = mod.typeToStruct(struct_ty).?;
  17411                     assert(struct_obj.haveLayout());
  17412                     assert(struct_obj.backing_int_ty.isInt(mod));
  17413                     break :val struct_obj.backing_int_ty.toIntern();
  17414                 } else .none,
  17415             } });
  17416 
  17417             const container_layout_ty = t: {
  17418                 const decl_index = (try sema.namespaceLookup(
  17419                     block,
  17420                     src,
  17421                     (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?,
  17422                     try ip.getOrPutString(gpa, "ContainerLayout"),
  17423                 )).?;
  17424                 try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
  17425                 try sema.ensureDeclAnalyzed(decl_index);
  17426                 const decl = mod.declPtr(decl_index);
  17427                 break :t decl.val.toType();
  17428             };
  17429 
  17430             const field_values = [_]InternPool.Index{
  17431                 // layout: ContainerLayout,
  17432                 (try mod.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(),
  17433                 // backing_integer: ?type,
  17434                 backing_integer_val,
  17435                 // fields: []const StructField,
  17436                 fields_val,
  17437                 // decls: []const Declaration,
  17438                 decls_val,
  17439                 // is_tuple: bool,
  17440                 Value.makeBool(struct_ty.isTuple(mod)).toIntern(),
  17441             };
  17442             return sema.addConstant((try mod.intern(.{ .un = .{
  17443                 .ty = type_info_ty.toIntern(),
  17444                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Struct))).toIntern(),
  17445                 .val = try mod.intern(.{ .aggregate = .{
  17446                     .ty = type_struct_ty.toIntern(),
  17447                     .storage = .{ .elems = &field_values },
  17448                 } }),
  17449             } })).toValue());
  17450         },
  17451         .Opaque => {
  17452             // TODO: look into memoizing this result.
  17453 
  17454             const type_opaque_ty = t: {
  17455                 const type_opaque_ty_decl_index = (try sema.namespaceLookup(
  17456                     block,
  17457                     src,
  17458                     type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17459                     try ip.getOrPutString(gpa, "Opaque"),
  17460                 )).?;
  17461                 try mod.declareDeclDependency(sema.owner_decl_index, type_opaque_ty_decl_index);
  17462                 try sema.ensureDeclAnalyzed(type_opaque_ty_decl_index);
  17463                 const type_opaque_ty_decl = mod.declPtr(type_opaque_ty_decl_index);
  17464                 break :t type_opaque_ty_decl.val.toType();
  17465             };
  17466 
  17467             const opaque_ty = try sema.resolveTypeFields(ty);
  17468             const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespaceIndex(mod));
  17469 
  17470             const field_values = .{
  17471                 // decls: []const Declaration,
  17472                 decls_val,
  17473             };
  17474             return sema.addConstant((try mod.intern(.{ .un = .{
  17475                 .ty = type_info_ty.toIntern(),
  17476                 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Opaque))).toIntern(),
  17477                 .val = try mod.intern(.{ .aggregate = .{
  17478                     .ty = type_opaque_ty.toIntern(),
  17479                     .storage = .{ .elems = &field_values },
  17480                 } }),
  17481             } })).toValue());
  17482         },
  17483         .Frame => return sema.failWithUseOfAsync(block, src),
  17484         .AnyFrame => return sema.failWithUseOfAsync(block, src),
  17485     }
  17486 }
  17487 
  17488 fn typeInfoDecls(
  17489     sema: *Sema,
  17490     block: *Block,
  17491     src: LazySrcLoc,
  17492     type_info_ty: Type,
  17493     opt_namespace: Module.Namespace.OptionalIndex,
  17494 ) CompileError!InternPool.Index {
  17495     const mod = sema.mod;
  17496     const gpa = sema.gpa;
  17497 
  17498     var decls_anon_decl = try block.startAnonDecl();
  17499     defer decls_anon_decl.deinit();
  17500 
  17501     const declaration_ty = t: {
  17502         const declaration_ty_decl_index = (try sema.namespaceLookup(
  17503             block,
  17504             src,
  17505             type_info_ty.getNamespaceIndex(mod).unwrap().?,
  17506             try mod.intern_pool.getOrPutString(gpa, "Declaration"),
  17507         )).?;
  17508         try mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index);
  17509         try sema.ensureDeclAnalyzed(declaration_ty_decl_index);
  17510         const declaration_ty_decl = mod.declPtr(declaration_ty_decl_index);
  17511         break :t declaration_ty_decl.val.toType();
  17512     };
  17513     try sema.queueFullTypeResolution(declaration_ty);
  17514 
  17515     var decl_vals = std.ArrayList(InternPool.Index).init(gpa);
  17516     defer decl_vals.deinit();
  17517 
  17518     var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa);
  17519     defer seen_namespaces.deinit();
  17520 
  17521     if (opt_namespace.unwrap()) |namespace_index| {
  17522         const namespace = mod.namespacePtr(namespace_index);
  17523         try sema.typeInfoNamespaceDecls(block, namespace, declaration_ty, &decl_vals, &seen_namespaces);
  17524     }
  17525 
  17526     const array_decl_ty = try mod.arrayType(.{
  17527         .len = decl_vals.items.len,
  17528         .child = declaration_ty.toIntern(),
  17529     });
  17530     const new_decl = try decls_anon_decl.finish(
  17531         array_decl_ty,
  17532         (try mod.intern(.{ .aggregate = .{
  17533             .ty = array_decl_ty.toIntern(),
  17534             .storage = .{ .elems = decl_vals.items },
  17535         } })).toValue(),
  17536         .none, // default alignment
  17537     );
  17538     return try mod.intern(.{ .ptr = .{
  17539         .ty = (try mod.ptrType(.{
  17540             .child = declaration_ty.toIntern(),
  17541             .flags = .{
  17542                 .size = .Slice,
  17543                 .is_const = true,
  17544             },
  17545         })).toIntern(),
  17546         .addr = .{ .decl = new_decl },
  17547         .len = (try mod.intValue(Type.usize, decl_vals.items.len)).toIntern(),
  17548     } });
  17549 }
  17550 
  17551 fn typeInfoNamespaceDecls(
  17552     sema: *Sema,
  17553     block: *Block,
  17554     namespace: *Namespace,
  17555     declaration_ty: Type,
  17556     decl_vals: *std.ArrayList(InternPool.Index),
  17557     seen_namespaces: *std.AutoHashMap(*Namespace, void),
  17558 ) !void {
  17559     const mod = sema.mod;
  17560     const ip = &mod.intern_pool;
  17561     const gop = try seen_namespaces.getOrPut(namespace);
  17562     if (gop.found_existing) return;
  17563     const decls = namespace.decls.keys();
  17564     for (decls) |decl_index| {
  17565         const decl = mod.declPtr(decl_index);
  17566         if (decl.kind == .@"usingnamespace") {
  17567             if (decl.analysis == .in_progress) continue;
  17568             try mod.ensureDeclAnalyzed(decl_index);
  17569             const new_ns = decl.val.toType().getNamespace(mod).?;
  17570             try sema.typeInfoNamespaceDecls(block, new_ns, declaration_ty, decl_vals, seen_namespaces);
  17571             continue;
  17572         }
  17573         if (decl.kind != .named) continue;
  17574         const name_val = v: {
  17575             var anon_decl = try block.startAnonDecl();
  17576             defer anon_decl.deinit();
  17577             // TODO: write something like getCoercedInts to avoid needing to dupe
  17578             const name = try sema.arena.dupe(u8, ip.stringToSlice(decl.name));
  17579             const new_decl_ty = try mod.arrayType(.{
  17580                 .len = name.len,
  17581                 .child = .u8_type,
  17582             });
  17583             const new_decl = try anon_decl.finish(
  17584                 new_decl_ty,
  17585                 (try mod.intern(.{ .aggregate = .{
  17586                     .ty = new_decl_ty.toIntern(),
  17587                     .storage = .{ .bytes = name },
  17588                 } })).toValue(),
  17589                 .none, // default alignment
  17590             );
  17591             break :v try mod.intern(.{ .ptr = .{
  17592                 .ty = .slice_const_u8_type,
  17593                 .addr = .{ .decl = new_decl },
  17594                 .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
  17595             } });
  17596         };
  17597 
  17598         const fields = .{
  17599             //name: []const u8,
  17600             name_val,
  17601             //is_pub: bool,
  17602             Value.makeBool(decl.is_pub).toIntern(),
  17603         };
  17604         try decl_vals.append(try mod.intern(.{ .aggregate = .{
  17605             .ty = declaration_ty.toIntern(),
  17606             .storage = .{ .elems = &fields },
  17607         } }));
  17608     }
  17609 }
  17610 
  17611 fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17612     _ = block;
  17613     const zir_datas = sema.code.instructions.items(.data);
  17614     const inst_data = zir_datas[inst].un_node;
  17615     const operand = try sema.resolveInst(inst_data.operand);
  17616     const operand_ty = sema.typeOf(operand);
  17617     return sema.addType(operand_ty);
  17618 }
  17619 
  17620 fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17621     const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
  17622     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
  17623     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
  17624 
  17625     var child_block: Block = .{
  17626         .parent = block,
  17627         .sema = sema,
  17628         .src_decl = block.src_decl,
  17629         .namespace = block.namespace,
  17630         .wip_capture_scope = block.wip_capture_scope,
  17631         .instructions = .{},
  17632         .inlining = block.inlining,
  17633         .is_comptime = false,
  17634         .is_typeof = true,
  17635         .want_safety = false,
  17636         .error_return_trace_index = block.error_return_trace_index,
  17637     };
  17638     defer child_block.instructions.deinit(sema.gpa);
  17639 
  17640     const operand = try sema.resolveBody(&child_block, body, inst);
  17641     const operand_ty = sema.typeOf(operand);
  17642     if (operand_ty.isGenericPoison()) return error.GenericPoison;
  17643     return sema.addType(operand_ty);
  17644 }
  17645 
  17646 fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17647     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17648     const src = inst_data.src();
  17649     const operand = try sema.resolveInst(inst_data.operand);
  17650     const operand_ty = sema.typeOf(operand);
  17651     const res_ty = try sema.log2IntType(block, operand_ty, src);
  17652     return sema.addType(res_ty);
  17653 }
  17654 
  17655 fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type {
  17656     const mod = sema.mod;
  17657     switch (operand.zigTypeTag(mod)) {
  17658         .ComptimeInt => return Type.comptime_int,
  17659         .Int => {
  17660             const bits = operand.bitSize(mod);
  17661             const count = if (bits == 0)
  17662                 0
  17663             else blk: {
  17664                 var count: u16 = 0;
  17665                 var s = bits - 1;
  17666                 while (s != 0) : (s >>= 1) {
  17667                     count += 1;
  17668                 }
  17669                 break :blk count;
  17670             };
  17671             return mod.intType(.unsigned, count);
  17672         },
  17673         .Vector => {
  17674             const elem_ty = operand.elemType2(mod);
  17675             const log2_elem_ty = try sema.log2IntType(block, elem_ty, src);
  17676             return mod.vectorType(.{
  17677                 .len = operand.vectorLen(mod),
  17678                 .child = log2_elem_ty.toIntern(),
  17679             });
  17680         },
  17681         else => {},
  17682     }
  17683     return sema.fail(
  17684         block,
  17685         src,
  17686         "bit shifting operation expected integer type, found '{}'",
  17687         .{operand.fmt(mod)},
  17688     );
  17689 }
  17690 
  17691 fn zirTypeofPeer(
  17692     sema: *Sema,
  17693     block: *Block,
  17694     extended: Zir.Inst.Extended.InstData,
  17695 ) CompileError!Air.Inst.Ref {
  17696     const tracy = trace(@src());
  17697     defer tracy.end();
  17698 
  17699     const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand);
  17700     const src = LazySrcLoc.nodeOffset(extra.data.src_node);
  17701     const body = sema.code.extra[extra.data.body_index..][0..extra.data.body_len];
  17702 
  17703     var child_block: Block = .{
  17704         .parent = block,
  17705         .sema = sema,
  17706         .src_decl = block.src_decl,
  17707         .namespace = block.namespace,
  17708         .wip_capture_scope = block.wip_capture_scope,
  17709         .instructions = .{},
  17710         .inlining = block.inlining,
  17711         .is_comptime = false,
  17712         .is_typeof = true,
  17713         .runtime_cond = block.runtime_cond,
  17714         .runtime_loop = block.runtime_loop,
  17715         .runtime_index = block.runtime_index,
  17716     };
  17717     defer child_block.instructions.deinit(sema.gpa);
  17718     // Ignore the result, we only care about the instructions in `args`.
  17719     _ = try sema.analyzeBodyBreak(&child_block, body);
  17720 
  17721     const args = sema.code.refSlice(extra.end, extended.small);
  17722 
  17723     const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len);
  17724     defer sema.gpa.free(inst_list);
  17725 
  17726     for (args, 0..) |arg_ref, i| {
  17727         inst_list[i] = try sema.resolveInst(arg_ref);
  17728     }
  17729 
  17730     const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node });
  17731     return sema.addType(result_type);
  17732 }
  17733 
  17734 fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17735     const tracy = trace(@src());
  17736     defer tracy.end();
  17737 
  17738     const mod = sema.mod;
  17739     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17740     const src = inst_data.src();
  17741     const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
  17742     const uncasted_operand = try sema.resolveInst(inst_data.operand);
  17743 
  17744     const operand = try sema.coerce(block, Type.bool, uncasted_operand, operand_src);
  17745     if (try sema.resolveMaybeUndefVal(operand)) |val| {
  17746         return if (val.isUndef(mod))
  17747             sema.addConstUndef(Type.bool)
  17748         else if (val.toBool())
  17749             Air.Inst.Ref.bool_false
  17750         else
  17751             Air.Inst.Ref.bool_true;
  17752     }
  17753     try sema.requireRuntimeBlock(block, src, null);
  17754     return block.addTyOp(.not, Type.bool, operand);
  17755 }
  17756 
  17757 fn zirBoolBr(
  17758     sema: *Sema,
  17759     parent_block: *Block,
  17760     inst: Zir.Inst.Index,
  17761     is_bool_or: bool,
  17762 ) CompileError!Air.Inst.Ref {
  17763     const tracy = trace(@src());
  17764     defer tracy.end();
  17765 
  17766     const mod = sema.mod;
  17767     const datas = sema.code.instructions.items(.data);
  17768     const inst_data = datas[inst].bool_br;
  17769     const lhs = try sema.resolveInst(inst_data.lhs);
  17770     const lhs_src = sema.src;
  17771     const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
  17772     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
  17773     const gpa = sema.gpa;
  17774 
  17775     if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| {
  17776         if (is_bool_or and lhs_val.toBool()) {
  17777             return Air.Inst.Ref.bool_true;
  17778         } else if (!is_bool_or and !lhs_val.toBool()) {
  17779             return Air.Inst.Ref.bool_false;
  17780         }
  17781         // comptime-known left-hand side. No need for a block here; the result
  17782         // is simply the rhs expression. Here we rely on there only being 1
  17783         // break instruction (`break_inline`).
  17784         return sema.resolveBody(parent_block, body, inst);
  17785     }
  17786 
  17787     const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
  17788     try sema.air_instructions.append(gpa, .{
  17789         .tag = .block,
  17790         .data = .{ .ty_pl = .{
  17791             .ty = .bool_type,
  17792             .payload = undefined,
  17793         } },
  17794     });
  17795 
  17796     var child_block = parent_block.makeSubBlock();
  17797     child_block.runtime_loop = null;
  17798     child_block.runtime_cond = lhs_src;
  17799     child_block.runtime_index.increment();
  17800     defer child_block.instructions.deinit(gpa);
  17801 
  17802     var then_block = child_block.makeSubBlock();
  17803     defer then_block.instructions.deinit(gpa);
  17804 
  17805     var else_block = child_block.makeSubBlock();
  17806     defer else_block.instructions.deinit(gpa);
  17807 
  17808     const lhs_block = if (is_bool_or) &then_block else &else_block;
  17809     const rhs_block = if (is_bool_or) &else_block else &then_block;
  17810 
  17811     const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false;
  17812     _ = try lhs_block.addBr(block_inst, lhs_result);
  17813 
  17814     const rhs_result = try sema.resolveBody(rhs_block, body, inst);
  17815     if (!sema.typeOf(rhs_result).isNoReturn(mod)) {
  17816         _ = try rhs_block.addBr(block_inst, rhs_result);
  17817     }
  17818 
  17819     const result = sema.finishCondBr(parent_block, &child_block, &then_block, &else_block, lhs, block_inst);
  17820     if (!sema.typeOf(rhs_result).isNoReturn(mod)) {
  17821         if (try sema.resolveDefinedValue(rhs_block, sema.src, rhs_result)) |rhs_val| {
  17822             if (is_bool_or and rhs_val.toBool()) {
  17823                 return Air.Inst.Ref.bool_true;
  17824             } else if (!is_bool_or and !rhs_val.toBool()) {
  17825                 return Air.Inst.Ref.bool_false;
  17826             }
  17827         }
  17828     }
  17829 
  17830     return result;
  17831 }
  17832 
  17833 fn finishCondBr(
  17834     sema: *Sema,
  17835     parent_block: *Block,
  17836     child_block: *Block,
  17837     then_block: *Block,
  17838     else_block: *Block,
  17839     cond: Air.Inst.Ref,
  17840     block_inst: Air.Inst.Index,
  17841 ) !Air.Inst.Ref {
  17842     const gpa = sema.gpa;
  17843 
  17844     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
  17845         then_block.instructions.items.len + else_block.instructions.items.len +
  17846         @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len + 1);
  17847 
  17848     const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
  17849         .then_body_len = @as(u32, @intCast(then_block.instructions.items.len)),
  17850         .else_body_len = @as(u32, @intCast(else_block.instructions.items.len)),
  17851     });
  17852     sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items);
  17853     sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items);
  17854 
  17855     _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
  17856         .operand = cond,
  17857         .payload = cond_br_payload,
  17858     } } });
  17859 
  17860     sema.air_instructions.items(.data)[block_inst].ty_pl.payload = sema.addExtraAssumeCapacity(
  17861         Air.Block{ .body_len = @as(u32, @intCast(child_block.instructions.items.len)) },
  17862     );
  17863     sema.air_extra.appendSliceAssumeCapacity(child_block.instructions.items);
  17864 
  17865     try parent_block.instructions.append(gpa, block_inst);
  17866     return Air.indexToRef(block_inst);
  17867 }
  17868 
  17869 fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  17870     const mod = sema.mod;
  17871     switch (ty.zigTypeTag(mod)) {
  17872         .Optional, .Null, .Undefined => return,
  17873         .Pointer => if (ty.isPtrLikeOptional(mod)) return,
  17874         else => {},
  17875     }
  17876     return sema.failWithExpectedOptionalType(block, src, ty);
  17877 }
  17878 
  17879 fn zirIsNonNull(
  17880     sema: *Sema,
  17881     block: *Block,
  17882     inst: Zir.Inst.Index,
  17883 ) CompileError!Air.Inst.Ref {
  17884     const tracy = trace(@src());
  17885     defer tracy.end();
  17886 
  17887     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17888     const src = inst_data.src();
  17889     const operand = try sema.resolveInst(inst_data.operand);
  17890     try sema.checkNullableType(block, src, sema.typeOf(operand));
  17891     return sema.analyzeIsNull(block, src, operand, true);
  17892 }
  17893 
  17894 fn zirIsNonNullPtr(
  17895     sema: *Sema,
  17896     block: *Block,
  17897     inst: Zir.Inst.Index,
  17898 ) CompileError!Air.Inst.Ref {
  17899     const tracy = trace(@src());
  17900     defer tracy.end();
  17901 
  17902     const mod = sema.mod;
  17903     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17904     const src = inst_data.src();
  17905     const ptr = try sema.resolveInst(inst_data.operand);
  17906     try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(mod));
  17907     if ((try sema.resolveMaybeUndefVal(ptr)) == null) {
  17908         return block.addUnOp(.is_non_null_ptr, ptr);
  17909     }
  17910     const loaded = try sema.analyzeLoad(block, src, ptr, src);
  17911     return sema.analyzeIsNull(block, src, loaded, true);
  17912 }
  17913 
  17914 fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  17915     const mod = sema.mod;
  17916     switch (ty.zigTypeTag(mod)) {
  17917         .ErrorSet, .ErrorUnion, .Undefined => return,
  17918         else => return sema.fail(block, src, "expected error union type, found '{}'", .{
  17919             ty.fmt(mod),
  17920         }),
  17921     }
  17922 }
  17923 
  17924 fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17925     const tracy = trace(@src());
  17926     defer tracy.end();
  17927 
  17928     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17929     const src = inst_data.src();
  17930     const operand = try sema.resolveInst(inst_data.operand);
  17931     try sema.checkErrorType(block, src, sema.typeOf(operand));
  17932     return sema.analyzeIsNonErr(block, src, operand);
  17933 }
  17934 
  17935 fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17936     const tracy = trace(@src());
  17937     defer tracy.end();
  17938 
  17939     const mod = sema.mod;
  17940     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17941     const src = inst_data.src();
  17942     const ptr = try sema.resolveInst(inst_data.operand);
  17943     try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(mod));
  17944     const loaded = try sema.analyzeLoad(block, src, ptr, src);
  17945     return sema.analyzeIsNonErr(block, src, loaded);
  17946 }
  17947 
  17948 fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17949     const tracy = trace(@src());
  17950     defer tracy.end();
  17951 
  17952     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  17953     const src = inst_data.src();
  17954     const operand = try sema.resolveInst(inst_data.operand);
  17955     return sema.analyzeIsNonErr(block, src, operand);
  17956 }
  17957 
  17958 fn zirCondbr(
  17959     sema: *Sema,
  17960     parent_block: *Block,
  17961     inst: Zir.Inst.Index,
  17962 ) CompileError!Zir.Inst.Index {
  17963     const tracy = trace(@src());
  17964     defer tracy.end();
  17965 
  17966     const mod = sema.mod;
  17967     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  17968     const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
  17969     const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
  17970 
  17971     const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
  17972     const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
  17973 
  17974     const uncasted_cond = try sema.resolveInst(extra.data.condition);
  17975     const cond = try sema.coerce(parent_block, Type.bool, uncasted_cond, cond_src);
  17976 
  17977     if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| {
  17978         const body = if (cond_val.toBool()) then_body else else_body;
  17979 
  17980         try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src);
  17981         // We use `analyzeBodyInner` since we want to propagate any possible
  17982         // `error.ComptimeBreak` to the caller.
  17983         return sema.analyzeBodyInner(parent_block, body);
  17984     }
  17985 
  17986     const gpa = sema.gpa;
  17987 
  17988     // We'll re-use the sub block to save on memory bandwidth, and yank out the
  17989     // instructions array in between using it for the then block and else block.
  17990     var sub_block = parent_block.makeSubBlock();
  17991     sub_block.runtime_loop = null;
  17992     sub_block.runtime_cond = cond_src;
  17993     sub_block.runtime_index.increment();
  17994     defer sub_block.instructions.deinit(gpa);
  17995 
  17996     try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
  17997     const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
  17998     defer gpa.free(true_instructions);
  17999 
  18000     const err_cond = blk: {
  18001         const index = Zir.refToIndex(extra.data.condition) orelse break :blk null;
  18002         if (sema.code.instructions.items(.tag)[index] != .is_non_err) break :blk null;
  18003 
  18004         const err_inst_data = sema.code.instructions.items(.data)[index].un_node;
  18005         const err_operand = try sema.resolveInst(err_inst_data.operand);
  18006         const operand_ty = sema.typeOf(err_operand);
  18007         assert(operand_ty.zigTypeTag(mod) == .ErrorUnion);
  18008         const result_ty = operand_ty.errorUnionSet(mod);
  18009         break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand);
  18010     };
  18011 
  18012     if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?)) {
  18013         // nothing to do
  18014     } else {
  18015         try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
  18016     }
  18017     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
  18018         true_instructions.len + sub_block.instructions.items.len);
  18019     _ = try parent_block.addInst(.{
  18020         .tag = .cond_br,
  18021         .data = .{ .pl_op = .{
  18022             .operand = cond,
  18023             .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18024                 .then_body_len = @as(u32, @intCast(true_instructions.len)),
  18025                 .else_body_len = @as(u32, @intCast(sub_block.instructions.items.len)),
  18026             }),
  18027         } },
  18028     });
  18029     sema.air_extra.appendSliceAssumeCapacity(true_instructions);
  18030     sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
  18031     return always_noreturn;
  18032 }
  18033 
  18034 fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18035     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  18036     const src = inst_data.src();
  18037     const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  18038     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
  18039     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
  18040     const err_union = try sema.resolveInst(extra.data.operand);
  18041     const err_union_ty = sema.typeOf(err_union);
  18042     const mod = sema.mod;
  18043     if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
  18044         return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
  18045             err_union_ty.fmt(mod),
  18046         });
  18047     }
  18048     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
  18049     if (is_non_err != .none) {
  18050         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
  18051         if (is_non_err_val.toBool()) {
  18052             return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
  18053         }
  18054         // We can analyze the body directly in the parent block because we know there are
  18055         // no breaks from the body possible, and that the body is noreturn.
  18056         return sema.resolveBody(parent_block, body, inst);
  18057     }
  18058 
  18059     var sub_block = parent_block.makeSubBlock();
  18060     defer sub_block.instructions.deinit(sema.gpa);
  18061 
  18062     // This body is guaranteed to end with noreturn and has no breaks.
  18063     _ = try sema.analyzeBodyInner(&sub_block, body);
  18064 
  18065     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len +
  18066         sub_block.instructions.items.len);
  18067     const try_inst = try parent_block.addInst(.{
  18068         .tag = .@"try",
  18069         .data = .{ .pl_op = .{
  18070             .operand = err_union,
  18071             .payload = sema.addExtraAssumeCapacity(Air.Try{
  18072                 .body_len = @as(u32, @intCast(sub_block.instructions.items.len)),
  18073             }),
  18074         } },
  18075     });
  18076     sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
  18077     return try_inst;
  18078 }
  18079 
  18080 fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18081     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  18082     const src = inst_data.src();
  18083     const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  18084     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
  18085     const body = sema.code.extra[extra.end..][0..extra.data.body_len];
  18086     const operand = try sema.resolveInst(extra.data.operand);
  18087     const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src);
  18088     const err_union_ty = sema.typeOf(err_union);
  18089     const mod = sema.mod;
  18090     if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
  18091         return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
  18092             err_union_ty.fmt(mod),
  18093         });
  18094     }
  18095     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
  18096     if (is_non_err != .none) {
  18097         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
  18098         if (is_non_err_val.toBool()) {
  18099             return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
  18100         }
  18101         // We can analyze the body directly in the parent block because we know there are
  18102         // no breaks from the body possible, and that the body is noreturn.
  18103         return sema.resolveBody(parent_block, body, inst);
  18104     }
  18105 
  18106     var sub_block = parent_block.makeSubBlock();
  18107     defer sub_block.instructions.deinit(sema.gpa);
  18108 
  18109     // This body is guaranteed to end with noreturn and has no breaks.
  18110     _ = try sema.analyzeBodyInner(&sub_block, body);
  18111 
  18112     const operand_ty = sema.typeOf(operand);
  18113     const ptr_info = operand_ty.ptrInfo(mod);
  18114     const res_ty = try mod.ptrType(.{
  18115         .child = err_union_ty.errorUnionPayload(mod).toIntern(),
  18116         .flags = .{
  18117             .is_const = ptr_info.flags.is_const,
  18118             .is_volatile = ptr_info.flags.is_volatile,
  18119             .is_allowzero = ptr_info.flags.is_allowzero,
  18120             .address_space = ptr_info.flags.address_space,
  18121         },
  18122     });
  18123     const res_ty_ref = try sema.addType(res_ty);
  18124     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len +
  18125         sub_block.instructions.items.len);
  18126     const try_inst = try parent_block.addInst(.{
  18127         .tag = .try_ptr,
  18128         .data = .{ .ty_pl = .{
  18129             .ty = res_ty_ref,
  18130             .payload = sema.addExtraAssumeCapacity(Air.TryPtr{
  18131                 .ptr = operand,
  18132                 .body_len = @as(u32, @intCast(sub_block.instructions.items.len)),
  18133             }),
  18134         } },
  18135     });
  18136     sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
  18137     return try_inst;
  18138 }
  18139 
  18140 // A `break` statement is inside a runtime condition, but trying to
  18141 // break from an inline loop. In such case we must convert it to
  18142 // a runtime break.
  18143 fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !void {
  18144     const gop = sema.inst_map.getOrPutAssumeCapacity(break_data.block_inst);
  18145     const labeled_block = if (!gop.found_existing) blk: {
  18146         try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1);
  18147 
  18148         const new_block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
  18149         gop.value_ptr.* = Air.indexToRef(new_block_inst);
  18150         try sema.air_instructions.append(sema.gpa, .{
  18151             .tag = .block,
  18152             .data = undefined,
  18153         });
  18154         const labeled_block = try sema.gpa.create(LabeledBlock);
  18155         labeled_block.* = .{
  18156             .label = .{
  18157                 .zir_block = break_data.block_inst,
  18158                 .merges = .{
  18159                     .src_locs = .{},
  18160                     .results = .{},
  18161                     .br_list = .{},
  18162                     .block_inst = new_block_inst,
  18163                 },
  18164             },
  18165             .block = .{
  18166                 .parent = child_block,
  18167                 .sema = sema,
  18168                 .src_decl = child_block.src_decl,
  18169                 .namespace = child_block.namespace,
  18170                 .wip_capture_scope = child_block.wip_capture_scope,
  18171                 .instructions = .{},
  18172                 .label = &labeled_block.label,
  18173                 .inlining = child_block.inlining,
  18174                 .is_comptime = child_block.is_comptime,
  18175             },
  18176         };
  18177         sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block);
  18178         break :blk labeled_block;
  18179     } else blk: {
  18180         const new_block_inst = Air.refToIndex(gop.value_ptr.*).?;
  18181         const labeled_block = sema.post_hoc_blocks.get(new_block_inst).?;
  18182         break :blk labeled_block;
  18183     };
  18184 
  18185     const operand = try sema.resolveInst(break_data.operand);
  18186     const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand);
  18187     try labeled_block.label.merges.results.append(sema.gpa, operand);
  18188     try labeled_block.label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?);
  18189     labeled_block.block.runtime_index.increment();
  18190     if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) {
  18191         labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop;
  18192         labeled_block.block.runtime_loop = child_block.runtime_loop;
  18193     }
  18194 }
  18195 
  18196 fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
  18197     const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
  18198     const src = inst_data.src();
  18199 
  18200     if (block.is_comptime) {
  18201         return sema.fail(block, src, "reached unreachable code", .{});
  18202     }
  18203     // TODO Add compile error for @optimizeFor occurring too late in a scope.
  18204     try block.addUnreachable(true);
  18205     return always_noreturn;
  18206 }
  18207 
  18208 fn zirRetErrValue(
  18209     sema: *Sema,
  18210     block: *Block,
  18211     inst: Zir.Inst.Index,
  18212 ) CompileError!Zir.Inst.Index {
  18213     const mod = sema.mod;
  18214     const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
  18215     const err_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
  18216     _ = try mod.getErrorValue(err_name);
  18217     const src = inst_data.src();
  18218     // Return the error code from the function.
  18219     const error_set_type = try mod.singleErrorSetType(err_name);
  18220     const result_inst = try sema.addConstant((try mod.intern(.{ .err = .{
  18221         .ty = error_set_type.toIntern(),
  18222         .name = err_name,
  18223     } })).toValue());
  18224     return sema.analyzeRet(block, result_inst, src);
  18225 }
  18226 
  18227 fn zirRetImplicit(
  18228     sema: *Sema,
  18229     block: *Block,
  18230     inst: Zir.Inst.Index,
  18231 ) CompileError!Zir.Inst.Index {
  18232     const tracy = trace(@src());
  18233     defer tracy.end();
  18234 
  18235     const mod = sema.mod;
  18236     const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
  18237     const operand = try sema.resolveInst(inst_data.operand);
  18238 
  18239     const r_brace_src = inst_data.src();
  18240     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
  18241     const base_tag = sema.fn_ret_ty.baseZigTypeTag(mod);
  18242     if (base_tag == .NoReturn) {
  18243         const msg = msg: {
  18244             const msg = try sema.errMsg(block, ret_ty_src, "function declared '{}' implicitly returns", .{
  18245                 sema.fn_ret_ty.fmt(mod),
  18246             });
  18247             errdefer msg.destroy(sema.gpa);
  18248             try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{});
  18249             break :msg msg;
  18250         };
  18251         return sema.failWithOwnedErrorMsg(msg);
  18252     } else if (base_tag != .Void) {
  18253         const msg = msg: {
  18254             const msg = try sema.errMsg(block, ret_ty_src, "function with non-void return type '{}' implicitly returns", .{
  18255                 sema.fn_ret_ty.fmt(mod),
  18256             });
  18257             errdefer msg.destroy(sema.gpa);
  18258             try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{});
  18259             break :msg msg;
  18260         };
  18261         return sema.failWithOwnedErrorMsg(msg);
  18262     }
  18263 
  18264     return sema.analyzeRet(block, operand, .unneeded);
  18265 }
  18266 
  18267 fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
  18268     const tracy = trace(@src());
  18269     defer tracy.end();
  18270 
  18271     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  18272     const operand = try sema.resolveInst(inst_data.operand);
  18273     const src = inst_data.src();
  18274 
  18275     return sema.analyzeRet(block, operand, src);
  18276 }
  18277 
  18278 fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
  18279     const tracy = trace(@src());
  18280     defer tracy.end();
  18281 
  18282     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  18283     const src = inst_data.src();
  18284     const ret_ptr = try sema.resolveInst(inst_data.operand);
  18285 
  18286     if (block.is_comptime or block.inlining != null) {
  18287         const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
  18288         return sema.analyzeRet(block, operand, src);
  18289     }
  18290 
  18291     if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
  18292         const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr);
  18293         return sema.retWithErrTracing(block, is_non_err, .ret_load, ret_ptr);
  18294     }
  18295 
  18296     _ = try block.addUnOp(.ret_load, ret_ptr);
  18297     return always_noreturn;
  18298 }
  18299 
  18300 fn retWithErrTracing(
  18301     sema: *Sema,
  18302     block: *Block,
  18303     is_non_err: Air.Inst.Ref,
  18304     ret_tag: Air.Inst.Tag,
  18305     operand: Air.Inst.Ref,
  18306 ) CompileError!Zir.Inst.Index {
  18307     const mod = sema.mod;
  18308     const need_check = switch (is_non_err) {
  18309         .bool_true => {
  18310             _ = try block.addUnOp(ret_tag, operand);
  18311             return always_noreturn;
  18312         },
  18313         .bool_false => false,
  18314         else => true,
  18315     };
  18316     const gpa = sema.gpa;
  18317     const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
  18318     const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
  18319     const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty);
  18320     const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
  18321     const return_err_fn = try sema.getBuiltin("returnError");
  18322     const args: [1]Air.Inst.Ref = .{err_return_trace};
  18323 
  18324     if (!need_check) {
  18325         try sema.callBuiltin(block, return_err_fn, .never_inline, &args);
  18326         _ = try block.addUnOp(ret_tag, operand);
  18327         return always_noreturn;
  18328     }
  18329 
  18330     var then_block = block.makeSubBlock();
  18331     defer then_block.instructions.deinit(gpa);
  18332     _ = try then_block.addUnOp(ret_tag, operand);
  18333 
  18334     var else_block = block.makeSubBlock();
  18335     defer else_block.instructions.deinit(gpa);
  18336     try sema.callBuiltin(&else_block, return_err_fn, .never_inline, &args);
  18337     _ = try else_block.addUnOp(ret_tag, operand);
  18338 
  18339     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
  18340         then_block.instructions.items.len + else_block.instructions.items.len +
  18341         @typeInfo(Air.Block).Struct.fields.len + 1);
  18342 
  18343     const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18344         .then_body_len = @as(u32, @intCast(then_block.instructions.items.len)),
  18345         .else_body_len = @as(u32, @intCast(else_block.instructions.items.len)),
  18346     });
  18347     sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items);
  18348     sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items);
  18349 
  18350     _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
  18351         .operand = is_non_err,
  18352         .payload = cond_br_payload,
  18353     } } });
  18354 
  18355     return always_noreturn;
  18356 }
  18357 
  18358 fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool {
  18359     const mod = sema.mod;
  18360     if (!mod.backendSupportsFeature(.error_return_trace)) return false;
  18361 
  18362     return fn_ret_ty.isError(mod) and
  18363         mod.comp.bin_file.options.error_return_tracing;
  18364 }
  18365 
  18366 fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18367     const mod = sema.mod;
  18368     const inst_data = sema.code.instructions.items(.data)[inst].save_err_ret_index;
  18369 
  18370     if (!mod.backendSupportsFeature(.error_return_trace)) return;
  18371     if (!mod.comp.bin_file.options.error_return_tracing) return;
  18372 
  18373     // This is only relevant at runtime.
  18374     if (block.is_comptime or block.is_typeof) return;
  18375 
  18376     const save_index = inst_data.operand == .none or b: {
  18377         const operand = try sema.resolveInst(inst_data.operand);
  18378         const operand_ty = sema.typeOf(operand);
  18379         break :b operand_ty.isError(mod);
  18380     };
  18381 
  18382     if (save_index)
  18383         block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block);
  18384 }
  18385 
  18386 fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18387     const inst_data = sema.code.instructions.items(.data)[inst].restore_err_ret_index;
  18388     const src = sema.src; // TODO
  18389 
  18390     // This is only relevant at runtime.
  18391     if (start_block.is_comptime or start_block.is_typeof) return;
  18392 
  18393     if (!sema.mod.backendSupportsFeature(.error_return_trace)) return;
  18394     if (!sema.owner_func.?.calls_or_awaits_errorable_fn) return;
  18395     if (!sema.mod.comp.bin_file.options.error_return_tracing) return;
  18396 
  18397     const tracy = trace(@src());
  18398     defer tracy.end();
  18399 
  18400     const saved_index = if (Zir.refToIndexAllowNone(inst_data.block)) |zir_block| b: {
  18401         var block = start_block;
  18402         while (true) {
  18403             if (block.label) |label| {
  18404                 if (label.zir_block == zir_block) {
  18405                     const target_trace_index = if (block.parent) |parent_block| tgt: {
  18406                         break :tgt parent_block.error_return_trace_index;
  18407                     } else sema.error_return_trace_index_on_fn_entry;
  18408 
  18409                     if (start_block.error_return_trace_index != target_trace_index)
  18410                         break :b target_trace_index;
  18411 
  18412                     return; // No need to restore
  18413                 }
  18414             }
  18415             block = block.parent.?;
  18416         }
  18417     } else b: {
  18418         if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry)
  18419             break :b sema.error_return_trace_index_on_fn_entry;
  18420 
  18421         return; // No need to restore
  18422     };
  18423 
  18424     assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere
  18425 
  18426     const operand = try sema.resolveInstAllowNone(inst_data.operand);
  18427     return sema.popErrorReturnTrace(start_block, src, operand, saved_index);
  18428 }
  18429 
  18430 fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
  18431     const mod = sema.mod;
  18432     const gpa = sema.gpa;
  18433     const ip = &mod.intern_pool;
  18434     assert(sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion);
  18435 
  18436     if (mod.typeToInferredErrorSet(sema.fn_ret_ty.errorUnionSet(mod))) |ies| {
  18437         const op_ty = sema.typeOf(uncasted_operand);
  18438         switch (op_ty.zigTypeTag(mod)) {
  18439             .ErrorSet => try ies.addErrorSet(op_ty, ip, gpa),
  18440             .ErrorUnion => try ies.addErrorSet(op_ty.errorUnionSet(mod), ip, gpa),
  18441             else => {},
  18442         }
  18443     }
  18444 }
  18445 
  18446 fn analyzeRet(
  18447     sema: *Sema,
  18448     block: *Block,
  18449     uncasted_operand: Air.Inst.Ref,
  18450     src: LazySrcLoc,
  18451 ) CompileError!Zir.Inst.Index {
  18452     // Special case for returning an error to an inferred error set; we need to
  18453     // add the error tag to the inferred error set of the in-scope function, so
  18454     // that the coercion below works correctly.
  18455     const mod = sema.mod;
  18456     if (sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion) {
  18457         try sema.addToInferredErrorSet(uncasted_operand);
  18458     }
  18459     const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, .{ .is_ret = true }) catch |err| switch (err) {
  18460         error.NotCoercible => unreachable,
  18461         else => |e| return e,
  18462     };
  18463 
  18464     if (block.inlining) |inlining| {
  18465         if (block.is_comptime) {
  18466             _ = try sema.resolveConstMaybeUndefVal(block, src, operand, "value being returned at comptime must be comptime-known");
  18467             inlining.comptime_result = operand;
  18468             return error.ComptimeReturn;
  18469         }
  18470         // We are inlining a function call; rewrite the `ret` as a `break`.
  18471         try inlining.merges.results.append(sema.gpa, operand);
  18472         _ = try block.addBr(inlining.merges.block_inst, operand);
  18473         return always_noreturn;
  18474     } else if (block.is_comptime) {
  18475         return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{});
  18476     }
  18477 
  18478     try sema.resolveTypeLayout(sema.fn_ret_ty);
  18479 
  18480     if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
  18481         // Avoid adding a frame to the error return trace in case the value is comptime-known
  18482         // to be not an error.
  18483         const is_non_err = try sema.analyzeIsNonErr(block, src, operand);
  18484         return sema.retWithErrTracing(block, is_non_err, .ret, operand);
  18485     }
  18486 
  18487     _ = try block.addUnOp(.ret, operand);
  18488 
  18489     return always_noreturn;
  18490 }
  18491 
  18492 fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
  18493     // extend this swich as additional operators are implemented
  18494     return switch (tag) {
  18495         .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true,
  18496         else => false,
  18497     };
  18498 }
  18499 
  18500 fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18501     const tracy = trace(@src());
  18502     defer tracy.end();
  18503 
  18504     const mod = sema.mod;
  18505     const inst_data = sema.code.instructions.items(.data)[inst].ptr_type;
  18506     const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index);
  18507     const elem_ty_src: LazySrcLoc = .{ .node_offset_ptr_elem = extra.data.src_node };
  18508     const sentinel_src: LazySrcLoc = .{ .node_offset_ptr_sentinel = extra.data.src_node };
  18509     const align_src: LazySrcLoc = .{ .node_offset_ptr_align = extra.data.src_node };
  18510     const addrspace_src: LazySrcLoc = .{ .node_offset_ptr_addrspace = extra.data.src_node };
  18511     const bitoffset_src: LazySrcLoc = .{ .node_offset_ptr_bitoffset = extra.data.src_node };
  18512     const hostsize_src: LazySrcLoc = .{ .node_offset_ptr_hostsize = extra.data.src_node };
  18513 
  18514     const elem_ty = blk: {
  18515         const air_inst = try sema.resolveInst(extra.data.elem_type);
  18516         const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| {
  18517             if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(mod)) {
  18518                 try sema.errNote(block, elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{});
  18519             }
  18520             return err;
  18521         };
  18522         if (ty.isGenericPoison()) return error.GenericPoison;
  18523         break :blk ty;
  18524     };
  18525 
  18526     if (elem_ty.zigTypeTag(mod) == .NoReturn)
  18527         return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{});
  18528 
  18529     const target = mod.getTarget();
  18530 
  18531     var extra_i = extra.end;
  18532 
  18533     const sentinel = if (inst_data.flags.has_sentinel) blk: {
  18534         const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i]));
  18535         extra_i += 1;
  18536         const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src);
  18537         const val = try sema.resolveConstValue(block, sentinel_src, coerced, "pointer sentinel value must be comptime-known");
  18538         break :blk val.toIntern();
  18539     } else .none;
  18540 
  18541     const abi_align: Alignment = if (inst_data.flags.has_align) blk: {
  18542         const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i]));
  18543         extra_i += 1;
  18544         const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), align_src);
  18545         const val = try sema.resolveConstValue(block, align_src, coerced, "pointer alignment must be comptime-known");
  18546         // Check if this happens to be the lazy alignment of our element type, in
  18547         // which case we can make this 0 without resolving it.
  18548         switch (mod.intern_pool.indexToKey(val.toIntern())) {
  18549             .int => |int| switch (int.storage) {
  18550                 .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none,
  18551                 else => {},
  18552             },
  18553             else => {},
  18554         }
  18555         const abi_align = @as(u32, @intCast((try val.getUnsignedIntAdvanced(mod, sema)).?));
  18556         try sema.validateAlign(block, align_src, abi_align);
  18557         break :blk Alignment.fromByteUnits(abi_align);
  18558     } else .none;
  18559 
  18560     const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: {
  18561         const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i]));
  18562         extra_i += 1;
  18563         break :blk try sema.analyzeAddressSpace(block, addrspace_src, ref, .pointer);
  18564     } else if (elem_ty.zigTypeTag(mod) == .Fn and target.cpu.arch == .avr) .flash else .generic;
  18565 
  18566     const bit_offset = if (inst_data.flags.has_bit_range) blk: {
  18567         const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i]));
  18568         extra_i += 1;
  18569         const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, Type.u16, "pointer bit-offset must be comptime-known");
  18570         break :blk @as(u16, @intCast(bit_offset));
  18571     } else 0;
  18572 
  18573     const host_size: u16 = if (inst_data.flags.has_bit_range) blk: {
  18574         const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i]));
  18575         extra_i += 1;
  18576         const host_size = try sema.resolveInt(block, hostsize_src, ref, Type.u16, "pointer host size must be comptime-known");
  18577         break :blk @as(u16, @intCast(host_size));
  18578     } else 0;
  18579 
  18580     if (host_size != 0 and bit_offset >= host_size * 8) {
  18581         return sema.fail(block, bitoffset_src, "bit offset starts after end of host integer", .{});
  18582     }
  18583 
  18584     if (elem_ty.zigTypeTag(mod) == .Fn) {
  18585         if (inst_data.size != .One) {
  18586             return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{});
  18587         }
  18588         const fn_align = mod.typeToFunc(elem_ty).?.alignment;
  18589         if (inst_data.flags.has_align and abi_align != .none and fn_align != .none and
  18590             abi_align != fn_align)
  18591         {
  18592             return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{});
  18593         }
  18594     } else if (inst_data.size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) {
  18595         return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
  18596     } else if (inst_data.size == .C) {
  18597         if (!try sema.validateExternType(elem_ty, .other)) {
  18598             const msg = msg: {
  18599                 const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)});
  18600                 errdefer msg.destroy(sema.gpa);
  18601 
  18602                 const src_decl = mod.declPtr(block.src_decl);
  18603                 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl, mod), elem_ty, .other);
  18604 
  18605                 try sema.addDeclaredHereNote(msg, elem_ty);
  18606                 break :msg msg;
  18607             };
  18608             return sema.failWithOwnedErrorMsg(msg);
  18609         }
  18610         if (elem_ty.zigTypeTag(mod) == .Opaque) {
  18611             return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{});
  18612         }
  18613     }
  18614 
  18615     const ty = try mod.ptrType(.{
  18616         .child = elem_ty.toIntern(),
  18617         .sentinel = sentinel,
  18618         .flags = .{
  18619             .alignment = abi_align,
  18620             .address_space = address_space,
  18621             .is_const = !inst_data.flags.is_mutable,
  18622             .is_allowzero = inst_data.flags.is_allowzero,
  18623             .is_volatile = inst_data.flags.is_volatile,
  18624             .size = inst_data.size,
  18625         },
  18626         .packed_offset = .{
  18627             .bit_offset = bit_offset,
  18628             .host_size = host_size,
  18629         },
  18630     });
  18631     return sema.addType(ty);
  18632 }
  18633 
  18634 fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18635     const tracy = trace(@src());
  18636     defer tracy.end();
  18637 
  18638     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  18639     const src = inst_data.src();
  18640     const obj_ty = try sema.resolveType(block, src, inst_data.operand);
  18641     const mod = sema.mod;
  18642 
  18643     switch (obj_ty.zigTypeTag(mod)) {
  18644         .Struct => return sema.structInitEmpty(block, obj_ty, src, src),
  18645         .Array, .Vector => return sema.arrayInitEmpty(block, src, obj_ty),
  18646         .Void => return sema.addConstant(Value.void),
  18647         .Union => return sema.fail(block, src, "union initializer must initialize one field", .{}),
  18648         else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
  18649     }
  18650 }
  18651 
  18652 fn structInitEmpty(
  18653     sema: *Sema,
  18654     block: *Block,
  18655     obj_ty: Type,
  18656     dest_src: LazySrcLoc,
  18657     init_src: LazySrcLoc,
  18658 ) CompileError!Air.Inst.Ref {
  18659     const mod = sema.mod;
  18660     const gpa = sema.gpa;
  18661     // This logic must be synchronized with that in `zirStructInit`.
  18662     const struct_ty = try sema.resolveTypeFields(obj_ty);
  18663 
  18664     // The init values to use for the struct instance.
  18665     const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(mod));
  18666     defer gpa.free(field_inits);
  18667     @memset(field_inits, .none);
  18668 
  18669     return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, false);
  18670 }
  18671 
  18672 fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref {
  18673     const mod = sema.mod;
  18674     const arr_len = obj_ty.arrayLen(mod);
  18675     if (arr_len != 0) {
  18676         if (obj_ty.zigTypeTag(mod) == .Array) {
  18677             return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len});
  18678         } else {
  18679             return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len});
  18680         }
  18681     }
  18682     return sema.addConstant((try mod.intern(.{ .aggregate = .{
  18683         .ty = obj_ty.toIntern(),
  18684         .storage = .{ .elems = &.{} },
  18685     } })).toValue());
  18686 }
  18687 
  18688 fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18689     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  18690     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  18691     const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  18692     const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  18693     const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
  18694     const union_ty = try sema.resolveType(block, ty_src, extra.union_type);
  18695     const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, "name of field being initialized must be comptime-known");
  18696     const init = try sema.resolveInst(extra.init);
  18697     return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src);
  18698 }
  18699 
  18700 fn unionInit(
  18701     sema: *Sema,
  18702     block: *Block,
  18703     uncasted_init: Air.Inst.Ref,
  18704     init_src: LazySrcLoc,
  18705     union_ty: Type,
  18706     union_ty_src: LazySrcLoc,
  18707     field_name: InternPool.NullTerminatedString,
  18708     field_src: LazySrcLoc,
  18709 ) CompileError!Air.Inst.Ref {
  18710     const mod = sema.mod;
  18711     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
  18712     const field = union_ty.unionFields(mod).values()[field_index];
  18713     const init = try sema.coerce(block, field.ty, uncasted_init, init_src);
  18714 
  18715     if (try sema.resolveMaybeUndefVal(init)) |init_val| {
  18716         const tag_ty = union_ty.unionTagTypeHypothetical(mod);
  18717         const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?));
  18718         const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index);
  18719         return sema.addConstant((try mod.intern(.{ .un = .{
  18720             .ty = union_ty.toIntern(),
  18721             .tag = try tag_val.intern(tag_ty, mod),
  18722             .val = try init_val.intern(field.ty, mod),
  18723         } })).toValue());
  18724     }
  18725 
  18726     try sema.requireRuntimeBlock(block, init_src, null);
  18727     _ = union_ty_src;
  18728     try sema.queueFullTypeResolution(union_ty);
  18729     return block.addUnionInit(union_ty, field_index, init);
  18730 }
  18731 
  18732 fn zirStructInit(
  18733     sema: *Sema,
  18734     block: *Block,
  18735     inst: Zir.Inst.Index,
  18736     is_ref: bool,
  18737 ) CompileError!Air.Inst.Ref {
  18738     const gpa = sema.gpa;
  18739     const zir_datas = sema.code.instructions.items(.data);
  18740     const inst_data = zir_datas[inst].pl_node;
  18741     const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index);
  18742     const src = inst_data.src();
  18743 
  18744     const mod = sema.mod;
  18745     const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;
  18746     const first_field_type_data = zir_datas[first_item.field_type].pl_node;
  18747     const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;
  18748     const resolved_ty = try sema.resolveType(block, src, first_field_type_extra.container_type);
  18749     try sema.resolveTypeLayout(resolved_ty);
  18750 
  18751     if (resolved_ty.zigTypeTag(mod) == .Struct) {
  18752         // This logic must be synchronized with that in `zirStructInitEmpty`.
  18753 
  18754         // Maps field index to field_type index of where it was already initialized.
  18755         // For making sure all fields are accounted for and no fields are duplicated.
  18756         const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(mod));
  18757         defer gpa.free(found_fields);
  18758 
  18759         // The init values to use for the struct instance.
  18760         const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(mod));
  18761         defer gpa.free(field_inits);
  18762         @memset(field_inits, .none);
  18763 
  18764         var field_i: u32 = 0;
  18765         var extra_index = extra.end;
  18766 
  18767         const is_packed = resolved_ty.containerLayout(mod) == .Packed;
  18768         while (field_i < extra.data.fields_len) : (field_i += 1) {
  18769             const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index);
  18770             extra_index = item.end;
  18771 
  18772             const field_type_data = zir_datas[item.data.field_type].pl_node;
  18773             const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node };
  18774             const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
  18775             const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start));
  18776             const field_index = if (resolved_ty.isTuple(mod))
  18777                 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)
  18778             else
  18779                 try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
  18780             if (field_inits[field_index] != .none) {
  18781                 const other_field_type = found_fields[field_index];
  18782                 const other_field_type_data = zir_datas[other_field_type].pl_node;
  18783                 const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_type_data.src_node };
  18784                 const msg = msg: {
  18785                     const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
  18786                     errdefer msg.destroy(gpa);
  18787                     try sema.errNote(block, other_field_src, msg, "other field here", .{});
  18788                     break :msg msg;
  18789                 };
  18790                 return sema.failWithOwnedErrorMsg(msg);
  18791             }
  18792             found_fields[field_index] = item.data.field_type;
  18793             field_inits[field_index] = try sema.resolveInst(item.data.init);
  18794             if (!is_packed) if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| {
  18795                 const init_val = (try sema.resolveMaybeUndefVal(field_inits[field_index])) orelse {
  18796                     return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known");
  18797                 };
  18798 
  18799                 if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) {
  18800                     return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
  18801                 }
  18802             };
  18803         }
  18804 
  18805         return sema.finishStructInit(block, src, src, field_inits, resolved_ty, is_ref);
  18806     } else if (resolved_ty.zigTypeTag(mod) == .Union) {
  18807         if (extra.data.fields_len != 1) {
  18808             return sema.fail(block, src, "union initialization expects exactly one field", .{});
  18809         }
  18810 
  18811         const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end);
  18812 
  18813         const field_type_data = zir_datas[item.data.field_type].pl_node;
  18814         const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node };
  18815         const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
  18816         const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start));
  18817         const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src);
  18818         const tag_ty = resolved_ty.unionTagTypeHypothetical(mod);
  18819         const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?));
  18820         const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index);
  18821 
  18822         const init_inst = try sema.resolveInst(item.data.init);
  18823         if (try sema.resolveMaybeUndefVal(init_inst)) |val| {
  18824             const field = resolved_ty.unionFields(mod).values()[field_index];
  18825             return sema.addConstantMaybeRef(block, resolved_ty, (try mod.intern(.{ .un = .{
  18826                 .ty = resolved_ty.toIntern(),
  18827                 .tag = try tag_val.intern(tag_ty, mod),
  18828                 .val = try val.intern(field.ty, mod),
  18829             } })).toValue(), is_ref);
  18830         }
  18831 
  18832         if (is_ref) {
  18833             const target = mod.getTarget();
  18834             const alloc_ty = try mod.ptrType(.{
  18835                 .child = resolved_ty.toIntern(),
  18836                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  18837             });
  18838             const alloc = try block.addTy(.alloc, alloc_ty);
  18839             const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty, true);
  18840             try sema.storePtr(block, src, field_ptr, init_inst);
  18841             const new_tag = try sema.addConstant(tag_val);
  18842             _ = try block.addBinOp(.set_union_tag, alloc, new_tag);
  18843             return sema.makePtrConst(block, alloc);
  18844         }
  18845 
  18846         try sema.requireRuntimeBlock(block, src, null);
  18847         try sema.queueFullTypeResolution(resolved_ty);
  18848         return block.addUnionInit(resolved_ty, field_index, init_inst);
  18849     } else if (resolved_ty.isAnonStruct(mod)) {
  18850         return sema.fail(block, src, "TODO anon struct init validation", .{});
  18851     }
  18852     unreachable;
  18853 }
  18854 
  18855 fn finishStructInit(
  18856     sema: *Sema,
  18857     block: *Block,
  18858     init_src: LazySrcLoc,
  18859     dest_src: LazySrcLoc,
  18860     field_inits: []Air.Inst.Ref,
  18861     struct_ty: Type,
  18862     is_ref: bool,
  18863 ) CompileError!Air.Inst.Ref {
  18864     const mod = sema.mod;
  18865     const ip = &mod.intern_pool;
  18866 
  18867     var root_msg: ?*Module.ErrorMsg = null;
  18868     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  18869 
  18870     switch (ip.indexToKey(struct_ty.toIntern())) {
  18871         .anon_struct_type => |anon_struct| {
  18872             for (anon_struct.values, 0..) |default_val, i| {
  18873                 if (field_inits[i] != .none) continue;
  18874 
  18875                 if (default_val == .none) {
  18876                     if (anon_struct.names.len == 0) {
  18877                         const template = "missing tuple field with index {d}";
  18878                         if (root_msg) |msg| {
  18879                             try sema.errNote(block, init_src, msg, template, .{i});
  18880                         } else {
  18881                             root_msg = try sema.errMsg(block, init_src, template, .{i});
  18882                         }
  18883                     } else {
  18884                         const field_name = anon_struct.names[i];
  18885                         const template = "missing struct field: {}";
  18886                         const args = .{field_name.fmt(ip)};
  18887                         if (root_msg) |msg| {
  18888                             try sema.errNote(block, init_src, msg, template, args);
  18889                         } else {
  18890                             root_msg = try sema.errMsg(block, init_src, template, args);
  18891                         }
  18892                     }
  18893                 } else {
  18894                     field_inits[i] = try sema.addConstant(default_val.toValue());
  18895                 }
  18896             }
  18897         },
  18898         .struct_type => |struct_type| {
  18899             const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
  18900             for (struct_obj.fields.values(), 0..) |field, i| {
  18901                 if (field_inits[i] != .none) continue;
  18902 
  18903                 if (field.default_val == .none) {
  18904                     const field_name = struct_obj.fields.keys()[i];
  18905                     const template = "missing struct field: {}";
  18906                     const args = .{field_name.fmt(ip)};
  18907                     if (root_msg) |msg| {
  18908                         try sema.errNote(block, init_src, msg, template, args);
  18909                     } else {
  18910                         root_msg = try sema.errMsg(block, init_src, template, args);
  18911                     }
  18912                 } else {
  18913                     field_inits[i] = try sema.addConstant(field.default_val.toValue());
  18914                 }
  18915             }
  18916         },
  18917         else => unreachable,
  18918     }
  18919 
  18920     if (root_msg) |msg| {
  18921         if (mod.typeToStruct(struct_ty)) |struct_obj| {
  18922             const fqn = try struct_obj.getFullyQualifiedName(mod);
  18923             try mod.errNoteNonLazy(
  18924                 struct_obj.srcLoc(mod),
  18925                 msg,
  18926                 "struct '{}' declared here",
  18927                 .{fqn.fmt(ip)},
  18928             );
  18929         }
  18930         root_msg = null;
  18931         return sema.failWithOwnedErrorMsg(msg);
  18932     }
  18933 
  18934     // Find which field forces the expression to be runtime, if any.
  18935     const opt_runtime_index = for (field_inits, 0..) |field_init, i| {
  18936         if (!(try sema.isComptimeKnown(field_init))) {
  18937             break i;
  18938         }
  18939     } else null;
  18940 
  18941     const runtime_index = opt_runtime_index orelse {
  18942         const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
  18943         for (elems, field_inits, 0..) |*elem, field_init, field_i| {
  18944             elem.* = try (sema.resolveMaybeUndefVal(field_init) catch unreachable).?
  18945                 .intern(struct_ty.structFieldType(field_i, mod), mod);
  18946         }
  18947         const struct_val = try mod.intern(.{ .aggregate = .{
  18948             .ty = struct_ty.toIntern(),
  18949             .storage = .{ .elems = elems },
  18950         } });
  18951         return sema.addConstantMaybeRef(block, struct_ty, struct_val.toValue(), is_ref);
  18952     };
  18953 
  18954     if (is_ref) {
  18955         try sema.resolveStructLayout(struct_ty);
  18956         const target = sema.mod.getTarget();
  18957         const alloc_ty = try mod.ptrType(.{
  18958             .child = struct_ty.toIntern(),
  18959             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  18960         });
  18961         const alloc = try block.addTy(.alloc, alloc_ty);
  18962         for (field_inits, 0..) |field_init, i_usize| {
  18963             const i = @as(u32, @intCast(i_usize));
  18964             const field_src = dest_src;
  18965             const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, alloc, i, field_src, struct_ty, true);
  18966             try sema.storePtr(block, dest_src, field_ptr, field_init);
  18967         }
  18968 
  18969         return sema.makePtrConst(block, alloc);
  18970     }
  18971 
  18972     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
  18973         error.NeededSourceLocation => {
  18974             const decl = mod.declPtr(block.src_decl);
  18975             const field_src = mod.initSrc(dest_src.node_offset.x, decl, runtime_index);
  18976             try sema.requireRuntimeBlock(block, dest_src, field_src);
  18977             unreachable;
  18978         },
  18979         else => |e| return e,
  18980     };
  18981     try sema.queueFullTypeResolution(struct_ty);
  18982     return block.addAggregateInit(struct_ty, field_inits);
  18983 }
  18984 
  18985 fn zirStructInitAnon(
  18986     sema: *Sema,
  18987     block: *Block,
  18988     inst: Zir.Inst.Index,
  18989     is_ref: bool,
  18990 ) CompileError!Air.Inst.Ref {
  18991     const mod = sema.mod;
  18992     const gpa = sema.gpa;
  18993     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  18994     const src = inst_data.src();
  18995     const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
  18996     const types = try sema.arena.alloc(InternPool.Index, extra.data.fields_len);
  18997     const values = try sema.arena.alloc(InternPool.Index, types.len);
  18998     var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena);
  18999     try fields.ensureUnusedCapacity(types.len);
  19000 
  19001     // Find which field forces the expression to be runtime, if any.
  19002     const opt_runtime_index = rs: {
  19003         var runtime_index: ?usize = null;
  19004         var extra_index = extra.end;
  19005         for (types, 0..) |*field_ty, i_usize| {
  19006             const i = @as(u32, @intCast(i_usize));
  19007             const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
  19008             extra_index = item.end;
  19009 
  19010             const name = sema.code.nullTerminatedString(item.data.field_name);
  19011             const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
  19012             const gop = fields.getOrPutAssumeCapacity(name_ip);
  19013             if (gop.found_existing) {
  19014                 const msg = msg: {
  19015                     const decl = mod.declPtr(block.src_decl);
  19016                     const field_src = mod.initSrc(src.node_offset.x, decl, i);
  19017                     const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
  19018                     errdefer msg.destroy(gpa);
  19019 
  19020                     const prev_source = mod.initSrc(src.node_offset.x, decl, gop.value_ptr.*);
  19021                     try sema.errNote(block, prev_source, msg, "other field here", .{});
  19022                     break :msg msg;
  19023                 };
  19024                 return sema.failWithOwnedErrorMsg(msg);
  19025             }
  19026             gop.value_ptr.* = i;
  19027 
  19028             const init = try sema.resolveInst(item.data.init);
  19029             field_ty.* = sema.typeOf(init).toIntern();
  19030             if (field_ty.toType().zigTypeTag(mod) == .Opaque) {
  19031                 const msg = msg: {
  19032                     const decl = mod.declPtr(block.src_decl);
  19033                     const field_src = mod.initSrc(src.node_offset.x, decl, i);
  19034                     const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  19035                     errdefer msg.destroy(sema.gpa);
  19036 
  19037                     try sema.addDeclaredHereNote(msg, field_ty.toType());
  19038                     break :msg msg;
  19039                 };
  19040                 return sema.failWithOwnedErrorMsg(msg);
  19041             }
  19042             if (try sema.resolveMaybeUndefVal(init)) |init_val| {
  19043                 values[i] = try init_val.intern(field_ty.toType(), mod);
  19044             } else {
  19045                 values[i] = .none;
  19046                 runtime_index = i;
  19047             }
  19048         }
  19049         break :rs runtime_index;
  19050     };
  19051 
  19052     const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
  19053         .names = fields.keys(),
  19054         .types = types,
  19055         .values = values,
  19056     } });
  19057 
  19058     const runtime_index = opt_runtime_index orelse {
  19059         const tuple_val = try mod.intern(.{ .aggregate = .{
  19060             .ty = tuple_ty,
  19061             .storage = .{ .elems = values },
  19062         } });
  19063         return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref);
  19064     };
  19065 
  19066     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
  19067         error.NeededSourceLocation => {
  19068             const decl = mod.declPtr(block.src_decl);
  19069             const field_src = mod.initSrc(src.node_offset.x, decl, runtime_index);
  19070             try sema.requireRuntimeBlock(block, src, field_src);
  19071             unreachable;
  19072         },
  19073         else => |e| return e,
  19074     };
  19075 
  19076     if (is_ref) {
  19077         const target = mod.getTarget();
  19078         const alloc_ty = try mod.ptrType(.{
  19079             .child = tuple_ty,
  19080             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19081         });
  19082         const alloc = try block.addTy(.alloc, alloc_ty);
  19083         var extra_index = extra.end;
  19084         for (types, 0..) |field_ty, i_usize| {
  19085             const i = @as(u32, @intCast(i_usize));
  19086             const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
  19087             extra_index = item.end;
  19088 
  19089             const field_ptr_ty = try mod.ptrType(.{
  19090                 .child = field_ty,
  19091                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19092             });
  19093             if (values[i] == .none) {
  19094                 const init = try sema.resolveInst(item.data.init);
  19095                 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
  19096                 _ = try block.addBinOp(.store, field_ptr, init);
  19097             }
  19098         }
  19099 
  19100         return sema.makePtrConst(block, alloc);
  19101     }
  19102 
  19103     const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len);
  19104     var extra_index = extra.end;
  19105     for (types, 0..) |_, i| {
  19106         const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
  19107         extra_index = item.end;
  19108         element_refs[i] = try sema.resolveInst(item.data.init);
  19109     }
  19110 
  19111     return block.addAggregateInit(tuple_ty.toType(), element_refs);
  19112 }
  19113 
  19114 fn zirArrayInit(
  19115     sema: *Sema,
  19116     block: *Block,
  19117     inst: Zir.Inst.Index,
  19118     is_ref: bool,
  19119 ) CompileError!Air.Inst.Ref {
  19120     const mod = sema.mod;
  19121     const gpa = sema.gpa;
  19122     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  19123     const src = inst_data.src();
  19124 
  19125     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
  19126     const args = sema.code.refSlice(extra.end, extra.data.operands_len);
  19127     assert(args.len >= 2); // array_ty + at least one element
  19128 
  19129     const array_ty = try sema.resolveType(block, src, args[0]);
  19130     const sentinel_val = array_ty.sentinel(mod);
  19131 
  19132     const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @intFromBool(sentinel_val != null));
  19133     defer gpa.free(resolved_args);
  19134     for (args[1..], 0..) |arg, i| {
  19135         const resolved_arg = try sema.resolveInst(arg);
  19136         const elem_ty = if (array_ty.zigTypeTag(mod) == .Struct)
  19137             array_ty.structFieldType(i, mod)
  19138         else
  19139             array_ty.elemType2(mod);
  19140         resolved_args[i] = sema.coerce(block, elem_ty, resolved_arg, .unneeded) catch |err| switch (err) {
  19141             error.NeededSourceLocation => {
  19142                 const decl = mod.declPtr(block.src_decl);
  19143                 const elem_src = mod.initSrc(src.node_offset.x, decl, i);
  19144                 _ = try sema.coerce(block, elem_ty, resolved_arg, elem_src);
  19145                 unreachable;
  19146             },
  19147             else => return err,
  19148         };
  19149     }
  19150 
  19151     if (sentinel_val) |some| {
  19152         resolved_args[resolved_args.len - 1] = try sema.addConstant(some);
  19153     }
  19154 
  19155     const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| {
  19156         const comptime_known = try sema.isComptimeKnown(arg);
  19157         if (!comptime_known) break @as(u32, @intCast(i));
  19158     } else null;
  19159 
  19160     const runtime_index = opt_runtime_index orelse {
  19161         const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len);
  19162         for (elem_vals, resolved_args, 0..) |*val, arg, i| {
  19163             const elem_ty = if (array_ty.zigTypeTag(mod) == .Struct)
  19164                 array_ty.structFieldType(i, mod)
  19165             else
  19166                 array_ty.elemType2(mod);
  19167             // We checked that all args are comptime above.
  19168             val.* = try ((sema.resolveMaybeUndefVal(arg) catch unreachable).?).intern(elem_ty, mod);
  19169         }
  19170         return sema.addConstantMaybeRef(block, array_ty, (try mod.intern(.{ .aggregate = .{
  19171             .ty = array_ty.toIntern(),
  19172             .storage = .{ .elems = elem_vals },
  19173         } })).toValue(), is_ref);
  19174     };
  19175 
  19176     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
  19177         error.NeededSourceLocation => {
  19178             const decl = mod.declPtr(block.src_decl);
  19179             const elem_src = mod.initSrc(src.node_offset.x, decl, runtime_index);
  19180             try sema.requireRuntimeBlock(block, src, elem_src);
  19181             unreachable;
  19182         },
  19183         else => return err,
  19184     };
  19185     try sema.queueFullTypeResolution(array_ty);
  19186 
  19187     if (is_ref) {
  19188         const target = mod.getTarget();
  19189         const alloc_ty = try mod.ptrType(.{
  19190             .child = array_ty.toIntern(),
  19191             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19192         });
  19193         const alloc = try block.addTy(.alloc, alloc_ty);
  19194 
  19195         if (array_ty.isTuple(mod)) {
  19196             for (resolved_args, 0..) |arg, i| {
  19197                 const elem_ptr_ty = try mod.ptrType(.{
  19198                     .child = array_ty.structFieldType(i, mod).toIntern(),
  19199                     .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19200                 });
  19201                 const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty);
  19202 
  19203                 const index = try sema.addIntUnsigned(Type.usize, i);
  19204                 const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref);
  19205                 _ = try block.addBinOp(.store, elem_ptr, arg);
  19206             }
  19207             return sema.makePtrConst(block, alloc);
  19208         }
  19209 
  19210         const elem_ptr_ty = try mod.ptrType(.{
  19211             .child = array_ty.elemType2(mod).toIntern(),
  19212             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19213         });
  19214         const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty);
  19215 
  19216         for (resolved_args, 0..) |arg, i| {
  19217             const index = try sema.addIntUnsigned(Type.usize, i);
  19218             const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref);
  19219             _ = try block.addBinOp(.store, elem_ptr, arg);
  19220         }
  19221         return sema.makePtrConst(block, alloc);
  19222     }
  19223 
  19224     return block.addAggregateInit(array_ty, resolved_args);
  19225 }
  19226 
  19227 fn zirArrayInitAnon(
  19228     sema: *Sema,
  19229     block: *Block,
  19230     inst: Zir.Inst.Index,
  19231     is_ref: bool,
  19232 ) CompileError!Air.Inst.Ref {
  19233     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  19234     const src = inst_data.src();
  19235     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
  19236     const operands = sema.code.refSlice(extra.end, extra.data.operands_len);
  19237     const mod = sema.mod;
  19238 
  19239     const types = try sema.arena.alloc(InternPool.Index, operands.len);
  19240     const values = try sema.arena.alloc(InternPool.Index, operands.len);
  19241 
  19242     const opt_runtime_src = rs: {
  19243         var runtime_src: ?LazySrcLoc = null;
  19244         for (operands, 0..) |operand, i| {
  19245             const operand_src = src; // TODO better source location
  19246             const elem = try sema.resolveInst(operand);
  19247             types[i] = sema.typeOf(elem).toIntern();
  19248             if (types[i].toType().zigTypeTag(mod) == .Opaque) {
  19249                 const msg = msg: {
  19250                     const msg = try sema.errMsg(block, operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  19251                     errdefer msg.destroy(sema.gpa);
  19252 
  19253                     try sema.addDeclaredHereNote(msg, types[i].toType());
  19254                     break :msg msg;
  19255                 };
  19256                 return sema.failWithOwnedErrorMsg(msg);
  19257             }
  19258             if (try sema.resolveMaybeUndefVal(elem)) |val| {
  19259                 values[i] = val.toIntern();
  19260             } else {
  19261                 values[i] = .none;
  19262                 runtime_src = operand_src;
  19263             }
  19264         }
  19265         break :rs runtime_src;
  19266     };
  19267 
  19268     const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
  19269         .types = types,
  19270         .values = values,
  19271         .names = &.{},
  19272     } });
  19273 
  19274     const runtime_src = opt_runtime_src orelse {
  19275         const tuple_val = try mod.intern(.{ .aggregate = .{
  19276             .ty = tuple_ty,
  19277             .storage = .{ .elems = values },
  19278         } });
  19279         return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref);
  19280     };
  19281 
  19282     try sema.requireRuntimeBlock(block, src, runtime_src);
  19283 
  19284     if (is_ref) {
  19285         const target = sema.mod.getTarget();
  19286         const alloc_ty = try mod.ptrType(.{
  19287             .child = tuple_ty,
  19288             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19289         });
  19290         const alloc = try block.addTy(.alloc, alloc_ty);
  19291         for (operands, 0..) |operand, i_usize| {
  19292             const i = @as(u32, @intCast(i_usize));
  19293             const field_ptr_ty = try mod.ptrType(.{
  19294                 .child = types[i],
  19295                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19296             });
  19297             if (values[i] == .none) {
  19298                 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
  19299                 _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand));
  19300             }
  19301         }
  19302 
  19303         return sema.makePtrConst(block, alloc);
  19304     }
  19305 
  19306     const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
  19307     for (operands, 0..) |operand, i| {
  19308         element_refs[i] = try sema.resolveInst(operand);
  19309     }
  19310 
  19311     return block.addAggregateInit(tuple_ty.toType(), element_refs);
  19312 }
  19313 
  19314 fn addConstantMaybeRef(
  19315     sema: *Sema,
  19316     block: *Block,
  19317     ty: Type,
  19318     val: Value,
  19319     is_ref: bool,
  19320 ) !Air.Inst.Ref {
  19321     if (!is_ref) return sema.addConstant(val);
  19322 
  19323     var anon_decl = try block.startAnonDecl();
  19324     defer anon_decl.deinit();
  19325     const decl = try anon_decl.finish(
  19326         ty,
  19327         val,
  19328         .none, // default alignment
  19329     );
  19330     return sema.analyzeDeclRef(decl);
  19331 }
  19332 
  19333 fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19334     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  19335     const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data;
  19336     const ty_src = inst_data.src();
  19337     const field_src = inst_data.src();
  19338     const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
  19339     const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, "field name must be comptime-known");
  19340     return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
  19341 }
  19342 
  19343 fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19344     const mod = sema.mod;
  19345     const ip = &mod.intern_pool;
  19346     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  19347     const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
  19348     const ty_src = inst_data.src();
  19349     const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
  19350     const aggregate_ty = sema.resolveType(block, ty_src, extra.container_type) catch |err| switch (err) {
  19351         // Since this is a ZIR instruction that returns a type, encountering
  19352         // generic poison should not result in a failed compilation, but the
  19353         // generic poison type. This prevents unnecessary failures when
  19354         // constructing types at compile-time.
  19355         error.GenericPoison => return Air.Inst.Ref.generic_poison_type,
  19356         else => |e| return e,
  19357     };
  19358     const zir_field_name = sema.code.nullTerminatedString(extra.name_start);
  19359     const field_name = try ip.getOrPutString(sema.gpa, zir_field_name);
  19360     return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src);
  19361 }
  19362 
  19363 fn fieldType(
  19364     sema: *Sema,
  19365     block: *Block,
  19366     aggregate_ty: Type,
  19367     field_name: InternPool.NullTerminatedString,
  19368     field_src: LazySrcLoc,
  19369     ty_src: LazySrcLoc,
  19370 ) CompileError!Air.Inst.Ref {
  19371     const mod = sema.mod;
  19372     var cur_ty = aggregate_ty;
  19373     while (true) {
  19374         const resolved_ty = try sema.resolveTypeFields(cur_ty);
  19375         cur_ty = resolved_ty;
  19376         switch (cur_ty.zigTypeTag(mod)) {
  19377             .Struct => switch (mod.intern_pool.indexToKey(cur_ty.toIntern())) {
  19378                 .anon_struct_type => |anon_struct| {
  19379                     const field_index = try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src);
  19380                     return sema.addType(anon_struct.types[field_index].toType());
  19381                 },
  19382                 .struct_type => |struct_type| {
  19383                     const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
  19384                     const field = struct_obj.fields.get(field_name) orelse
  19385                         return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
  19386                     return sema.addType(field.ty);
  19387                 },
  19388                 else => unreachable,
  19389             },
  19390             .Union => {
  19391                 const union_obj = mod.typeToUnion(cur_ty).?;
  19392                 const field = union_obj.fields.get(field_name) orelse
  19393                     return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
  19394                 return sema.addType(field.ty);
  19395             },
  19396             .Optional => {
  19397                 // Struct/array init through optional requires the child type to not be a pointer.
  19398                 // If the child of .optional is a pointer it'll error on the next loop.
  19399                 cur_ty = mod.intern_pool.indexToKey(cur_ty.toIntern()).opt_type.toType();
  19400                 continue;
  19401             },
  19402             .ErrorUnion => {
  19403                 cur_ty = cur_ty.errorUnionPayload(mod);
  19404                 continue;
  19405             },
  19406             else => {},
  19407         }
  19408         return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{
  19409             resolved_ty.fmt(sema.mod),
  19410         });
  19411     }
  19412 }
  19413 
  19414 fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
  19415     return sema.getErrorReturnTrace(block);
  19416 }
  19417 
  19418 fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
  19419     const mod = sema.mod;
  19420     const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
  19421     const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
  19422     const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty);
  19423     const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern());
  19424 
  19425     if (sema.owner_func != null and
  19426         sema.owner_func.?.calls_or_awaits_errorable_fn and
  19427         mod.comp.bin_file.options.error_return_tracing and
  19428         mod.backendSupportsFeature(.error_return_trace))
  19429     {
  19430         return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
  19431     }
  19432     return sema.addConstant((try mod.intern(.{ .opt = .{
  19433         .ty = opt_ptr_stack_trace_ty.toIntern(),
  19434         .val = .none,
  19435     } })).toValue());
  19436 }
  19437 
  19438 fn zirFrame(
  19439     sema: *Sema,
  19440     block: *Block,
  19441     extended: Zir.Inst.Extended.InstData,
  19442 ) CompileError!Air.Inst.Ref {
  19443     const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand)));
  19444     return sema.failWithUseOfAsync(block, src);
  19445 }
  19446 
  19447 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19448     const mod = sema.mod;
  19449     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  19450     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  19451     const ty = try sema.resolveType(block, operand_src, inst_data.operand);
  19452     if (ty.isNoReturn(mod)) {
  19453         return sema.fail(block, operand_src, "no align available for type '{}'", .{ty.fmt(sema.mod)});
  19454     }
  19455     const val = try ty.lazyAbiAlignment(mod);
  19456     if (val.isLazyAlign(mod)) {
  19457         try sema.queueFullTypeResolution(ty);
  19458     }
  19459     return sema.addConstant(val);
  19460 }
  19461 
  19462 fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19463     const mod = sema.mod;
  19464     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  19465     const operand = try sema.resolveInst(inst_data.operand);
  19466     if (try sema.resolveMaybeUndefVal(operand)) |val| {
  19467         if (val.isUndef(mod)) return sema.addConstUndef(Type.u1);
  19468         if (val.toBool()) return sema.addConstant(try mod.intValue(Type.u1, 1));
  19469         return sema.addConstant(try mod.intValue(Type.u1, 0));
  19470     }
  19471     return block.addUnOp(.int_from_bool, operand);
  19472 }
  19473 
  19474 fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19475     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  19476     const operand = try sema.resolveInst(inst_data.operand);
  19477     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  19478 
  19479     if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
  19480         const err_name = sema.mod.intern_pool.indexToKey(val.toIntern()).err.name;
  19481         return sema.addStrLit(block, sema.mod.intern_pool.stringToSlice(err_name));
  19482     }
  19483 
  19484     // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass
  19485     // might be able to resolve the result at compile time.
  19486     return block.addUnOp(.error_name, operand);
  19487 }
  19488 
  19489 fn zirUnaryMath(
  19490     sema: *Sema,
  19491     block: *Block,
  19492     inst: Zir.Inst.Index,
  19493     air_tag: Air.Inst.Tag,
  19494     comptime eval: fn (Value, Type, Allocator, *Module) Allocator.Error!Value,
  19495 ) CompileError!Air.Inst.Ref {
  19496     const tracy = trace(@src());
  19497     defer tracy.end();
  19498 
  19499     const mod = sema.mod;
  19500     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  19501     const operand = try sema.resolveInst(inst_data.operand);
  19502     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  19503     const operand_ty = sema.typeOf(operand);
  19504 
  19505     switch (operand_ty.zigTypeTag(mod)) {
  19506         .ComptimeFloat, .Float => {},
  19507         .Vector => {
  19508             const scalar_ty = operand_ty.scalarType(mod);
  19509             switch (scalar_ty.zigTypeTag(mod)) {
  19510                 .ComptimeFloat, .Float => {},
  19511                 else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(sema.mod)}),
  19512             }
  19513         },
  19514         else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(sema.mod)}),
  19515     }
  19516 
  19517     switch (operand_ty.zigTypeTag(mod)) {
  19518         .Vector => {
  19519             const scalar_ty = operand_ty.scalarType(mod);
  19520             const vec_len = operand_ty.vectorLen(mod);
  19521             const result_ty = try mod.vectorType(.{
  19522                 .len = vec_len,
  19523                 .child = scalar_ty.toIntern(),
  19524             });
  19525             if (try sema.resolveMaybeUndefVal(operand)) |val| {
  19526                 if (val.isUndef(mod))
  19527                     return sema.addConstUndef(result_ty);
  19528 
  19529                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  19530                 for (elems, 0..) |*elem, i| {
  19531                     const elem_val = try val.elemValue(sema.mod, i);
  19532                     elem.* = try (try eval(elem_val, scalar_ty, sema.arena, sema.mod)).intern(scalar_ty, mod);
  19533                 }
  19534                 return sema.addConstant((try mod.intern(.{ .aggregate = .{
  19535                     .ty = result_ty.toIntern(),
  19536                     .storage = .{ .elems = elems },
  19537                 } })).toValue());
  19538             }
  19539 
  19540             try sema.requireRuntimeBlock(block, operand_src, null);
  19541             return block.addUnOp(air_tag, operand);
  19542         },
  19543         .ComptimeFloat, .Float => {
  19544             if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
  19545                 if (operand_val.isUndef(mod))
  19546                     return sema.addConstUndef(operand_ty);
  19547                 const result_val = try eval(operand_val, operand_ty, sema.arena, sema.mod);
  19548                 return sema.addConstant(result_val);
  19549             }
  19550 
  19551             try sema.requireRuntimeBlock(block, operand_src, null);
  19552             return block.addUnOp(air_tag, operand);
  19553         },
  19554         else => unreachable,
  19555     }
  19556 }
  19557 
  19558 fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19559     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  19560     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  19561     const src = inst_data.src();
  19562     const operand = try sema.resolveInst(inst_data.operand);
  19563     const operand_ty = sema.typeOf(operand);
  19564     const mod = sema.mod;
  19565     const ip = &mod.intern_pool;
  19566 
  19567     try sema.resolveTypeLayout(operand_ty);
  19568     const enum_ty = switch (operand_ty.zigTypeTag(mod)) {
  19569         .EnumLiteral => {
  19570             const val = try sema.resolveConstValue(block, .unneeded, operand, "");
  19571             const tag_name = ip.indexToKey(val.toIntern()).enum_literal;
  19572             return sema.addStrLit(block, ip.stringToSlice(tag_name));
  19573         },
  19574         .Enum => operand_ty,
  19575         .Union => operand_ty.unionTagType(mod) orelse {
  19576             const msg = msg: {
  19577                 const msg = try sema.errMsg(block, src, "union '{}' is untagged", .{
  19578                     operand_ty.fmt(sema.mod),
  19579                 });
  19580                 errdefer msg.destroy(sema.gpa);
  19581                 try sema.addDeclaredHereNote(msg, operand_ty);
  19582                 break :msg msg;
  19583             };
  19584             return sema.failWithOwnedErrorMsg(msg);
  19585         },
  19586         else => return sema.fail(block, operand_src, "expected enum or union; found '{}'", .{
  19587             operand_ty.fmt(mod),
  19588         }),
  19589     };
  19590     if (enum_ty.enumFieldCount(mod) == 0) {
  19591         // TODO I don't think this is the correct way to handle this but
  19592         // it prevents a crash.
  19593         return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{}'", .{
  19594             enum_ty.fmt(mod),
  19595         });
  19596     }
  19597     const enum_decl_index = enum_ty.getOwnerDecl(mod);
  19598     const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);
  19599     if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {
  19600         const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse {
  19601             const enum_decl = mod.declPtr(enum_decl_index);
  19602             const msg = msg: {
  19603                 const msg = try sema.errMsg(block, src, "no field with value '{}' in enum '{}'", .{
  19604                     val.fmtValue(enum_ty, sema.mod), enum_decl.name.fmt(ip),
  19605                 });
  19606                 errdefer msg.destroy(sema.gpa);
  19607                 try mod.errNoteNonLazy(enum_decl.srcLoc(mod), msg, "declared here", .{});
  19608                 break :msg msg;
  19609             };
  19610             return sema.failWithOwnedErrorMsg(msg);
  19611         };
  19612         // TODO: write something like getCoercedInts to avoid needing to dupe
  19613         const field_name = enum_ty.enumFieldName(field_index, mod);
  19614         return sema.addStrLit(block, ip.stringToSlice(field_name));
  19615     }
  19616     try sema.requireRuntimeBlock(block, src, operand_src);
  19617     if (block.wantSafety() and sema.mod.backendSupportsFeature(.is_named_enum_value)) {
  19618         const ok = try block.addUnOp(.is_named_enum_value, casted_operand);
  19619         try sema.addSafetyCheck(block, ok, .invalid_enum_value);
  19620     }
  19621     // In case the value is runtime-known, we have an AIR instruction for this instead
  19622     // of trying to lower it in Sema because an optimization pass may result in the operand
  19623     // being comptime-known, which would let us elide the `tag_name` AIR instruction.
  19624     return block.addUnOp(.tag_name, casted_operand);
  19625 }
  19626 
  19627 fn zirReify(
  19628     sema: *Sema,
  19629     block: *Block,
  19630     extended: Zir.Inst.Extended.InstData,
  19631     inst: Zir.Inst.Index,
  19632 ) CompileError!Air.Inst.Ref {
  19633     const mod = sema.mod;
  19634     const gpa = sema.gpa;
  19635     const ip = &mod.intern_pool;
  19636     const name_strategy = @as(Zir.Inst.NameStrategy, @enumFromInt(extended.small));
  19637     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  19638     const src = LazySrcLoc.nodeOffset(extra.node);
  19639     const type_info_ty = try sema.getBuiltinType("Type");
  19640     const uncasted_operand = try sema.resolveInst(extra.operand);
  19641     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  19642     const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
  19643     const val = try sema.resolveConstValue(block, operand_src, type_info, "operand to @Type must be comptime-known");
  19644     const union_val = ip.indexToKey(val.toIntern()).un;
  19645     const target = mod.getTarget();
  19646     if (try union_val.val.toValue().anyUndef(mod)) return sema.failWithUseOfUndef(block, src);
  19647     const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag.toValue(), mod).?;
  19648     switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) {
  19649         .Type => return Air.Inst.Ref.type_type,
  19650         .Void => return Air.Inst.Ref.void_type,
  19651         .Bool => return Air.Inst.Ref.bool_type,
  19652         .NoReturn => return Air.Inst.Ref.noreturn_type,
  19653         .ComptimeFloat => return Air.Inst.Ref.comptime_float_type,
  19654         .ComptimeInt => return Air.Inst.Ref.comptime_int_type,
  19655         .Undefined => return Air.Inst.Ref.undefined_type,
  19656         .Null => return Air.Inst.Ref.null_type,
  19657         .AnyFrame => return sema.failWithUseOfAsync(block, src),
  19658         .EnumLiteral => return Air.Inst.Ref.enum_literal_type,
  19659         .Int => {
  19660             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19661             const signedness_val = try union_val.val.toValue().fieldValue(
  19662                 mod,
  19663                 fields.getIndex(try ip.getOrPutString(gpa, "signedness")).?,
  19664             );
  19665             const bits_val = try union_val.val.toValue().fieldValue(
  19666                 mod,
  19667                 fields.getIndex(try ip.getOrPutString(gpa, "bits")).?,
  19668             );
  19669 
  19670             const signedness = mod.toEnum(std.builtin.Signedness, signedness_val);
  19671             const bits = @as(u16, @intCast(bits_val.toUnsignedInt(mod)));
  19672             const ty = try mod.intType(signedness, bits);
  19673             return sema.addType(ty);
  19674         },
  19675         .Vector => {
  19676             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19677             const len_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19678                 try ip.getOrPutString(gpa, "len"),
  19679             ).?);
  19680             const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19681                 try ip.getOrPutString(gpa, "child"),
  19682             ).?);
  19683 
  19684             const len = @as(u32, @intCast(len_val.toUnsignedInt(mod)));
  19685             const child_ty = child_val.toType();
  19686 
  19687             try sema.checkVectorElemType(block, src, child_ty);
  19688 
  19689             const ty = try mod.vectorType(.{
  19690                 .len = len,
  19691                 .child = child_ty.toIntern(),
  19692             });
  19693             return sema.addType(ty);
  19694         },
  19695         .Float => {
  19696             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19697             const bits_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19698                 try ip.getOrPutString(gpa, "bits"),
  19699             ).?);
  19700 
  19701             const bits = @as(u16, @intCast(bits_val.toUnsignedInt(mod)));
  19702             const ty = switch (bits) {
  19703                 16 => Type.f16,
  19704                 32 => Type.f32,
  19705                 64 => Type.f64,
  19706                 80 => Type.f80,
  19707                 128 => Type.f128,
  19708                 else => return sema.fail(block, src, "{}-bit float unsupported", .{bits}),
  19709             };
  19710             return sema.addType(ty);
  19711         },
  19712         .Pointer => {
  19713             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19714             const size_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19715                 try ip.getOrPutString(gpa, "size"),
  19716             ).?);
  19717             const is_const_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19718                 try ip.getOrPutString(gpa, "is_const"),
  19719             ).?);
  19720             const is_volatile_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19721                 try ip.getOrPutString(gpa, "is_volatile"),
  19722             ).?);
  19723             const alignment_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19724                 try ip.getOrPutString(gpa, "alignment"),
  19725             ).?);
  19726             const address_space_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19727                 try ip.getOrPutString(gpa, "address_space"),
  19728             ).?);
  19729             const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19730                 try ip.getOrPutString(gpa, "child"),
  19731             ).?);
  19732             const is_allowzero_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19733                 try ip.getOrPutString(gpa, "is_allowzero"),
  19734             ).?);
  19735             const sentinel_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19736                 try ip.getOrPutString(gpa, "sentinel"),
  19737             ).?);
  19738 
  19739             if (!try sema.intFitsInType(alignment_val, Type.u32, null)) {
  19740                 return sema.fail(block, src, "alignment must fit in 'u32'", .{});
  19741             }
  19742 
  19743             const abi_align = Alignment.fromByteUnits(
  19744                 (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?,
  19745             );
  19746 
  19747             const unresolved_elem_ty = child_val.toType();
  19748             const elem_ty = if (abi_align == .none)
  19749                 unresolved_elem_ty
  19750             else t: {
  19751                 const elem_ty = try sema.resolveTypeFields(unresolved_elem_ty);
  19752                 try sema.resolveTypeLayout(elem_ty);
  19753                 break :t elem_ty;
  19754             };
  19755 
  19756             const ptr_size = mod.toEnum(std.builtin.Type.Pointer.Size, size_val);
  19757 
  19758             const actual_sentinel: InternPool.Index = s: {
  19759                 if (!sentinel_val.isNull(mod)) {
  19760                     if (ptr_size == .One or ptr_size == .C) {
  19761                         return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{});
  19762                     }
  19763                     const sentinel_ptr_val = sentinel_val.optionalValue(mod).?;
  19764                     const ptr_ty = try mod.singleMutPtrType(elem_ty);
  19765                     const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?;
  19766                     break :s sent_val.toIntern();
  19767                 }
  19768                 break :s .none;
  19769             };
  19770 
  19771             if (elem_ty.zigTypeTag(mod) == .NoReturn) {
  19772                 return sema.fail(block, src, "pointer to noreturn not allowed", .{});
  19773             } else if (elem_ty.zigTypeTag(mod) == .Fn) {
  19774                 if (ptr_size != .One) {
  19775                     return sema.fail(block, src, "function pointers must be single pointers", .{});
  19776                 }
  19777                 const fn_align = mod.typeToFunc(elem_ty).?.alignment;
  19778                 if (abi_align != .none and fn_align != .none and
  19779                     abi_align != fn_align)
  19780                 {
  19781                     return sema.fail(block, src, "function pointer alignment disagrees with function alignment", .{});
  19782                 }
  19783             } else if (ptr_size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) {
  19784                 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});
  19785             } else if (ptr_size == .C) {
  19786                 if (!try sema.validateExternType(elem_ty, .other)) {
  19787                     const msg = msg: {
  19788                         const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)});
  19789                         errdefer msg.destroy(gpa);
  19790 
  19791                         const src_decl = mod.declPtr(block.src_decl);
  19792                         try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), elem_ty, .other);
  19793 
  19794                         try sema.addDeclaredHereNote(msg, elem_ty);
  19795                         break :msg msg;
  19796                     };
  19797                     return sema.failWithOwnedErrorMsg(msg);
  19798                 }
  19799                 if (elem_ty.zigTypeTag(mod) == .Opaque) {
  19800                     return sema.fail(block, src, "C pointers cannot point to opaque types", .{});
  19801                 }
  19802             }
  19803 
  19804             const ty = try mod.ptrType(.{
  19805                 .child = elem_ty.toIntern(),
  19806                 .sentinel = actual_sentinel,
  19807                 .flags = .{
  19808                     .size = ptr_size,
  19809                     .is_const = is_const_val.toBool(),
  19810                     .is_volatile = is_volatile_val.toBool(),
  19811                     .alignment = abi_align,
  19812                     .address_space = mod.toEnum(std.builtin.AddressSpace, address_space_val),
  19813                     .is_allowzero = is_allowzero_val.toBool(),
  19814                 },
  19815             });
  19816             return sema.addType(ty);
  19817         },
  19818         .Array => {
  19819             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19820             const len_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19821                 try ip.getOrPutString(gpa, "len"),
  19822             ).?);
  19823             const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19824                 try ip.getOrPutString(gpa, "child"),
  19825             ).?);
  19826             const sentinel_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19827                 try ip.getOrPutString(gpa, "sentinel"),
  19828             ).?);
  19829 
  19830             const len = len_val.toUnsignedInt(mod);
  19831             const child_ty = child_val.toType();
  19832             const sentinel = if (sentinel_val.optionalValue(mod)) |p| blk: {
  19833                 const ptr_ty = try mod.singleMutPtrType(child_ty);
  19834                 break :blk (try sema.pointerDeref(block, src, p, ptr_ty)).?;
  19835             } else null;
  19836 
  19837             const ty = try mod.arrayType(.{
  19838                 .len = len,
  19839                 .sentinel = if (sentinel) |s| s.toIntern() else .none,
  19840                 .child = child_ty.toIntern(),
  19841             });
  19842             return sema.addType(ty);
  19843         },
  19844         .Optional => {
  19845             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19846             const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19847                 try ip.getOrPutString(gpa, "child"),
  19848             ).?);
  19849 
  19850             const child_ty = child_val.toType();
  19851 
  19852             const ty = try mod.optionalType(child_ty.toIntern());
  19853             return sema.addType(ty);
  19854         },
  19855         .ErrorUnion => {
  19856             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19857             const error_set_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19858                 try ip.getOrPutString(gpa, "error_set"),
  19859             ).?);
  19860             const payload_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19861                 try ip.getOrPutString(gpa, "payload"),
  19862             ).?);
  19863 
  19864             const error_set_ty = error_set_val.toType();
  19865             const payload_ty = payload_val.toType();
  19866 
  19867             if (error_set_ty.zigTypeTag(mod) != .ErrorSet) {
  19868                 return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{});
  19869             }
  19870 
  19871             const ty = try mod.errorUnionType(error_set_ty, payload_ty);
  19872             return sema.addType(ty);
  19873         },
  19874         .ErrorSet => {
  19875             const payload_val = union_val.val.toValue().optionalValue(mod) orelse
  19876                 return sema.addType(Type.anyerror);
  19877 
  19878             const len = try sema.usizeCast(block, src, payload_val.sliceLen(mod));
  19879             var names: Module.Fn.InferredErrorSet.NameMap = .{};
  19880             try names.ensureUnusedCapacity(sema.arena, len);
  19881             for (0..len) |i| {
  19882                 const elem_val = try payload_val.elemValue(mod, i);
  19883                 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod);
  19884                 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  19885                     try ip.getOrPutString(gpa, "name"),
  19886                 ).?);
  19887 
  19888                 const name = try name_val.toIpString(Type.slice_const_u8, mod);
  19889                 _ = try mod.getErrorValue(name);
  19890                 const gop = names.getOrPutAssumeCapacity(name);
  19891                 if (gop.found_existing) {
  19892                     return sema.fail(block, src, "duplicate error '{}'", .{
  19893                         name.fmt(ip),
  19894                     });
  19895                 }
  19896             }
  19897 
  19898             const ty = try mod.errorSetFromUnsortedNames(names.keys());
  19899             return sema.addType(ty);
  19900         },
  19901         .Struct => {
  19902             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19903             const layout_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19904                 try ip.getOrPutString(gpa, "layout"),
  19905             ).?);
  19906             const backing_integer_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19907                 try ip.getOrPutString(gpa, "backing_integer"),
  19908             ).?);
  19909             const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19910                 try ip.getOrPutString(gpa, "fields"),
  19911             ).?);
  19912             const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19913                 try ip.getOrPutString(gpa, "decls"),
  19914             ).?);
  19915             const is_tuple_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19916                 try ip.getOrPutString(gpa, "is_tuple"),
  19917             ).?);
  19918 
  19919             const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val);
  19920 
  19921             // Decls
  19922             if (decls_val.sliceLen(mod) > 0) {
  19923                 return sema.fail(block, src, "reified structs must have no decls", .{});
  19924             }
  19925 
  19926             if (layout != .Packed and !backing_integer_val.isNull(mod)) {
  19927                 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{});
  19928             }
  19929 
  19930             return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_val, name_strategy, is_tuple_val.toBool());
  19931         },
  19932         .Enum => {
  19933             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  19934             const tag_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19935                 try ip.getOrPutString(gpa, "tag_type"),
  19936             ).?);
  19937             const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19938                 try ip.getOrPutString(gpa, "fields"),
  19939             ).?);
  19940             const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19941                 try ip.getOrPutString(gpa, "decls"),
  19942             ).?);
  19943             const is_exhaustive_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  19944                 try ip.getOrPutString(gpa, "is_exhaustive"),
  19945             ).?);
  19946 
  19947             // Decls
  19948             if (decls_val.sliceLen(mod) > 0) {
  19949                 return sema.fail(block, src, "reified enums must have no decls", .{});
  19950             }
  19951 
  19952             const int_tag_ty = tag_type_val.toType();
  19953             if (int_tag_ty.zigTypeTag(mod) != .Int) {
  19954                 return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{});
  19955             }
  19956 
  19957             // Because these things each reference each other, `undefined`
  19958             // placeholders are used before being set after the enum type gains
  19959             // an InternPool index.
  19960 
  19961             const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  19962                 .ty = Type.noreturn,
  19963                 .val = Value.@"unreachable",
  19964             }, name_strategy, "enum", inst);
  19965             const new_decl = mod.declPtr(new_decl_index);
  19966             new_decl.owns_tv = true;
  19967             errdefer {
  19968                 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  19969                 mod.abortAnonDecl(new_decl_index);
  19970             }
  19971 
  19972             // Define our empty enum decl
  19973             const fields_len = @as(u32, @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))));
  19974             const incomplete_enum = try ip.getIncompleteEnum(gpa, .{
  19975                 .decl = new_decl_index,
  19976                 .namespace = .none,
  19977                 .fields_len = fields_len,
  19978                 .has_values = true,
  19979                 .tag_mode = if (!is_exhaustive_val.toBool())
  19980                     .nonexhaustive
  19981                 else
  19982                     .explicit,
  19983                 .tag_ty = int_tag_ty.toIntern(),
  19984             });
  19985             // TODO: figure out InternPool removals for incremental compilation
  19986             //errdefer ip.remove(incomplete_enum.index);
  19987 
  19988             new_decl.ty = Type.type;
  19989             new_decl.val = incomplete_enum.index.toValue();
  19990 
  19991             for (0..fields_len) |field_i| {
  19992                 const elem_val = try fields_val.elemValue(mod, field_i);
  19993                 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod);
  19994                 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  19995                     try ip.getOrPutString(gpa, "name"),
  19996                 ).?);
  19997                 const value_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  19998                     try ip.getOrPutString(gpa, "value"),
  19999                 ).?);
  20000 
  20001                 const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
  20002 
  20003                 if (!try sema.intFitsInType(value_val, int_tag_ty, null)) {
  20004                     // TODO: better source location
  20005                     return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{
  20006                         field_name.fmt(ip),
  20007                         value_val.fmtValue(Type.comptime_int, mod),
  20008                         int_tag_ty.fmt(mod),
  20009                     });
  20010                 }
  20011 
  20012                 if (try incomplete_enum.addFieldName(ip, gpa, field_name)) |other_index| {
  20013                     const msg = msg: {
  20014                         const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{
  20015                             field_name.fmt(ip),
  20016                         });
  20017                         errdefer msg.destroy(gpa);
  20018                         _ = other_index; // TODO: this note is incorrect
  20019                         try sema.errNote(block, src, msg, "other field here", .{});
  20020                         break :msg msg;
  20021                     };
  20022                     return sema.failWithOwnedErrorMsg(msg);
  20023                 }
  20024 
  20025                 if (try incomplete_enum.addFieldValue(ip, gpa, (try mod.getCoerced(value_val, int_tag_ty)).toIntern())) |other| {
  20026                     const msg = msg: {
  20027                         const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{value_val.fmtValue(Type.comptime_int, mod)});
  20028                         errdefer msg.destroy(gpa);
  20029                         _ = other; // TODO: this note is incorrect
  20030                         try sema.errNote(block, src, msg, "other enum tag value here", .{});
  20031                         break :msg msg;
  20032                     };
  20033                     return sema.failWithOwnedErrorMsg(msg);
  20034                 }
  20035             }
  20036 
  20037             const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  20038             try mod.finalizeAnonDecl(new_decl_index);
  20039             return decl_val;
  20040         },
  20041         .Opaque => {
  20042             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  20043             const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20044                 try ip.getOrPutString(gpa, "decls"),
  20045             ).?);
  20046 
  20047             // Decls
  20048             if (decls_val.sliceLen(mod) > 0) {
  20049                 return sema.fail(block, src, "reified opaque must have no decls", .{});
  20050             }
  20051 
  20052             // Because these three things each reference each other,
  20053             // `undefined` placeholders are used in two places before being set
  20054             // after the opaque type gains an InternPool index.
  20055 
  20056             const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  20057                 .ty = Type.noreturn,
  20058                 .val = Value.@"unreachable",
  20059             }, name_strategy, "opaque", inst);
  20060             const new_decl = mod.declPtr(new_decl_index);
  20061             new_decl.owns_tv = true;
  20062             errdefer {
  20063                 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  20064                 mod.abortAnonDecl(new_decl_index);
  20065             }
  20066 
  20067             const new_namespace_index = try mod.createNamespace(.{
  20068                 .parent = block.namespace.toOptional(),
  20069                 .ty = undefined,
  20070                 .file_scope = block.getFileScope(mod),
  20071             });
  20072             const new_namespace = mod.namespacePtr(new_namespace_index);
  20073             errdefer mod.destroyNamespace(new_namespace_index);
  20074 
  20075             const opaque_ty = try mod.intern(.{ .opaque_type = .{
  20076                 .decl = new_decl_index,
  20077                 .namespace = new_namespace_index,
  20078             } });
  20079             // TODO: figure out InternPool removals for incremental compilation
  20080             //errdefer ip.remove(opaque_ty);
  20081 
  20082             new_decl.ty = Type.type;
  20083             new_decl.val = opaque_ty.toValue();
  20084             new_namespace.ty = opaque_ty.toType();
  20085 
  20086             const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  20087             try mod.finalizeAnonDecl(new_decl_index);
  20088             return decl_val;
  20089         },
  20090         .Union => {
  20091             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  20092             const layout_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20093                 try ip.getOrPutString(gpa, "layout"),
  20094             ).?);
  20095             const tag_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20096                 try ip.getOrPutString(gpa, "tag_type"),
  20097             ).?);
  20098             const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20099                 try ip.getOrPutString(gpa, "fields"),
  20100             ).?);
  20101             const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20102                 try ip.getOrPutString(gpa, "decls"),
  20103             ).?);
  20104 
  20105             // Decls
  20106             if (decls_val.sliceLen(mod) > 0) {
  20107                 return sema.fail(block, src, "reified unions must have no decls", .{});
  20108             }
  20109             const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val);
  20110 
  20111             // Because these three things each reference each other, `undefined`
  20112             // placeholders are used before being set after the union type gains an
  20113             // InternPool index.
  20114 
  20115             const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  20116                 .ty = Type.noreturn,
  20117                 .val = Value.@"unreachable",
  20118             }, name_strategy, "union", inst);
  20119             const new_decl = mod.declPtr(new_decl_index);
  20120             new_decl.owns_tv = true;
  20121             errdefer {
  20122                 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  20123                 mod.abortAnonDecl(new_decl_index);
  20124             }
  20125 
  20126             const new_namespace_index = try mod.createNamespace(.{
  20127                 .parent = block.namespace.toOptional(),
  20128                 .ty = undefined,
  20129                 .file_scope = block.getFileScope(mod),
  20130             });
  20131             const new_namespace = mod.namespacePtr(new_namespace_index);
  20132             errdefer mod.destroyNamespace(new_namespace_index);
  20133 
  20134             const union_index = try mod.createUnion(.{
  20135                 .owner_decl = new_decl_index,
  20136                 .tag_ty = Type.null,
  20137                 .fields = .{},
  20138                 .zir_index = inst,
  20139                 .layout = layout,
  20140                 .status = .have_field_types,
  20141                 .namespace = new_namespace_index,
  20142             });
  20143             const union_obj = mod.unionPtr(union_index);
  20144             errdefer mod.destroyUnion(union_index);
  20145 
  20146             const union_ty = try ip.get(gpa, .{ .union_type = .{
  20147                 .index = union_index,
  20148                 .runtime_tag = if (!tag_type_val.isNull(mod))
  20149                     .tagged
  20150                 else if (layout != .Auto)
  20151                     .none
  20152                 else switch (mod.optimizeMode()) {
  20153                     .Debug, .ReleaseSafe => .safety,
  20154                     .ReleaseFast, .ReleaseSmall => .none,
  20155                 },
  20156             } });
  20157             // TODO: figure out InternPool removals for incremental compilation
  20158             //errdefer ip.remove(union_ty);
  20159 
  20160             new_decl.ty = Type.type;
  20161             new_decl.val = union_ty.toValue();
  20162             new_namespace.ty = union_ty.toType();
  20163 
  20164             // Tag type
  20165             const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
  20166             var explicit_tags_seen: []bool = &.{};
  20167             var enum_field_names: []InternPool.NullTerminatedString = &.{};
  20168             if (tag_type_val.optionalValue(mod)) |payload_val| {
  20169                 union_obj.tag_ty = payload_val.toType();
  20170 
  20171                 const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) {
  20172                     .enum_type => |x| x,
  20173                     else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}),
  20174                 };
  20175 
  20176                 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
  20177                 @memset(explicit_tags_seen, false);
  20178             } else {
  20179                 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  20180             }
  20181 
  20182             // Fields
  20183             try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len);
  20184 
  20185             for (0..fields_len) |i| {
  20186                 const elem_val = try fields_val.elemValue(mod, i);
  20187                 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod);
  20188                 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20189                     try ip.getOrPutString(gpa, "name"),
  20190                 ).?);
  20191                 const type_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20192                     try ip.getOrPutString(gpa, "type"),
  20193                 ).?);
  20194                 const alignment_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20195                     try ip.getOrPutString(gpa, "alignment"),
  20196                 ).?);
  20197 
  20198                 const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
  20199 
  20200                 if (enum_field_names.len != 0) {
  20201                     enum_field_names[i] = field_name;
  20202                 }
  20203 
  20204                 if (explicit_tags_seen.len > 0) {
  20205                     const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
  20206                     const enum_index = tag_info.nameIndex(ip, field_name) orelse {
  20207                         const msg = msg: {
  20208                             const msg = try sema.errMsg(block, src, "no field named '{}' in enum '{}'", .{
  20209                                 field_name.fmt(ip),
  20210                                 union_obj.tag_ty.fmt(mod),
  20211                             });
  20212                             errdefer msg.destroy(gpa);
  20213                             try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
  20214                             break :msg msg;
  20215                         };
  20216                         return sema.failWithOwnedErrorMsg(msg);
  20217                     };
  20218                     // No check for duplicate because the check already happened in order
  20219                     // to create the enum type in the first place.
  20220                     assert(!explicit_tags_seen[enum_index]);
  20221                     explicit_tags_seen[enum_index] = true;
  20222                 }
  20223 
  20224                 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
  20225                 if (gop.found_existing) {
  20226                     // TODO: better source location
  20227                     return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)});
  20228                 }
  20229 
  20230                 const field_ty = type_val.toType();
  20231                 gop.value_ptr.* = .{
  20232                     .ty = field_ty,
  20233                     .abi_align = Alignment.fromByteUnits((try alignment_val.getUnsignedIntAdvanced(mod, sema)).?),
  20234                 };
  20235 
  20236                 if (field_ty.zigTypeTag(mod) == .Opaque) {
  20237                     const msg = msg: {
  20238                         const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
  20239                         errdefer msg.destroy(gpa);
  20240 
  20241                         try sema.addDeclaredHereNote(msg, field_ty);
  20242                         break :msg msg;
  20243                     };
  20244                     return sema.failWithOwnedErrorMsg(msg);
  20245                 }
  20246                 if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) {
  20247                     const msg = msg: {
  20248                         const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  20249                         errdefer msg.destroy(gpa);
  20250 
  20251                         const src_decl = mod.declPtr(block.src_decl);
  20252                         try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field);
  20253 
  20254                         try sema.addDeclaredHereNote(msg, field_ty);
  20255                         break :msg msg;
  20256                     };
  20257                     return sema.failWithOwnedErrorMsg(msg);
  20258                 } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) {
  20259                     const msg = msg: {
  20260                         const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  20261                         errdefer msg.destroy(gpa);
  20262 
  20263                         const src_decl = mod.declPtr(block.src_decl);
  20264                         try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty);
  20265 
  20266                         try sema.addDeclaredHereNote(msg, field_ty);
  20267                         break :msg msg;
  20268                     };
  20269                     return sema.failWithOwnedErrorMsg(msg);
  20270                 }
  20271             }
  20272 
  20273             if (explicit_tags_seen.len > 0) {
  20274                 const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
  20275                 if (tag_info.names.len > fields_len) {
  20276                     const msg = msg: {
  20277                         const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
  20278                         errdefer msg.destroy(gpa);
  20279 
  20280                         const enum_ty = union_obj.tag_ty;
  20281                         for (tag_info.names, 0..) |field_name, field_index| {
  20282                             if (explicit_tags_seen[field_index]) continue;
  20283                             try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{
  20284                                 field_name.fmt(ip),
  20285                             });
  20286                         }
  20287                         try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
  20288                         break :msg msg;
  20289                     };
  20290                     return sema.failWithOwnedErrorMsg(msg);
  20291                 }
  20292             } else {
  20293                 union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, null);
  20294             }
  20295 
  20296             const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  20297             try mod.finalizeAnonDecl(new_decl_index);
  20298             return decl_val;
  20299         },
  20300         .Fn => {
  20301             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
  20302             const calling_convention_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20303                 try ip.getOrPutString(gpa, "calling_convention"),
  20304             ).?);
  20305             const alignment_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20306                 try ip.getOrPutString(gpa, "alignment"),
  20307             ).?);
  20308             const is_generic_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20309                 try ip.getOrPutString(gpa, "is_generic"),
  20310             ).?);
  20311             const is_var_args_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20312                 try ip.getOrPutString(gpa, "is_var_args"),
  20313             ).?);
  20314             const return_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20315                 try ip.getOrPutString(gpa, "return_type"),
  20316             ).?);
  20317             const params_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex(
  20318                 try ip.getOrPutString(gpa, "params"),
  20319             ).?);
  20320 
  20321             const is_generic = is_generic_val.toBool();
  20322             if (is_generic) {
  20323                 return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{});
  20324             }
  20325 
  20326             const is_var_args = is_var_args_val.toBool();
  20327             const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val);
  20328             if (is_var_args and cc != .C) {
  20329                 return sema.fail(block, src, "varargs functions must have C calling convention", .{});
  20330             }
  20331 
  20332             const alignment = alignment: {
  20333                 if (!try sema.intFitsInType(alignment_val, Type.u32, null)) {
  20334                     return sema.fail(block, src, "alignment must fit in 'u32'", .{});
  20335                 }
  20336                 const alignment = @as(u29, @intCast(alignment_val.toUnsignedInt(mod)));
  20337                 if (alignment == target_util.defaultFunctionAlignment(target)) {
  20338                     break :alignment .none;
  20339                 } else {
  20340                     break :alignment Alignment.fromByteUnits(alignment);
  20341                 }
  20342             };
  20343             const return_type = return_type_val.optionalValue(mod) orelse
  20344                 return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{});
  20345 
  20346             const args_len = try sema.usizeCast(block, src, params_val.sliceLen(mod));
  20347             const param_types = try sema.arena.alloc(InternPool.Index, args_len);
  20348 
  20349             var noalias_bits: u32 = 0;
  20350             for (param_types, 0..) |*param_type, i| {
  20351                 const elem_val = try params_val.elemValue(mod, i);
  20352                 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod);
  20353                 const param_is_generic_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20354                     try ip.getOrPutString(gpa, "is_generic"),
  20355                 ).?);
  20356                 const param_is_noalias_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20357                     try ip.getOrPutString(gpa, "is_noalias"),
  20358                 ).?);
  20359                 const opt_param_type_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20360                     try ip.getOrPutString(gpa, "type"),
  20361                 ).?);
  20362 
  20363                 if (param_is_generic_val.toBool()) {
  20364                     return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{});
  20365                 }
  20366 
  20367                 const param_type_val = opt_param_type_val.optionalValue(mod) orelse
  20368                     return sema.fail(block, src, "Type.Fn.Param.arg_type must be non-null for @Type", .{});
  20369                 param_type.* = param_type_val.toIntern();
  20370 
  20371                 if (param_is_noalias_val.toBool()) {
  20372                     if (!param_type.toType().isPtrAtRuntime(mod)) {
  20373                         return sema.fail(block, src, "non-pointer parameter declared noalias", .{});
  20374                     }
  20375                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, i) orelse
  20376                         return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
  20377                 }
  20378             }
  20379 
  20380             const ty = try mod.funcType(.{
  20381                 .param_types = param_types,
  20382                 .comptime_bits = 0,
  20383                 .noalias_bits = noalias_bits,
  20384                 .return_type = return_type.toIntern(),
  20385                 .alignment = alignment,
  20386                 .cc = cc,
  20387                 .is_var_args = is_var_args,
  20388                 .is_generic = false,
  20389                 .is_noinline = false,
  20390                 .align_is_generic = false,
  20391                 .cc_is_generic = false,
  20392                 .section_is_generic = false,
  20393                 .addrspace_is_generic = false,
  20394             });
  20395             return sema.addType(ty);
  20396         },
  20397         .Frame => return sema.failWithUseOfAsync(block, src),
  20398     }
  20399 }
  20400 
  20401 fn reifyStruct(
  20402     sema: *Sema,
  20403     block: *Block,
  20404     inst: Zir.Inst.Index,
  20405     src: LazySrcLoc,
  20406     layout: std.builtin.Type.ContainerLayout,
  20407     backing_int_val: Value,
  20408     fields_val: Value,
  20409     name_strategy: Zir.Inst.NameStrategy,
  20410     is_tuple: bool,
  20411 ) CompileError!Air.Inst.Ref {
  20412     const mod = sema.mod;
  20413     const gpa = sema.gpa;
  20414     const ip = &mod.intern_pool;
  20415 
  20416     // Because these three things each reference each other, `undefined`
  20417     // placeholders are used before being set after the struct type gains an
  20418     // InternPool index.
  20419 
  20420     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
  20421         .ty = Type.noreturn,
  20422         .val = Value.@"unreachable",
  20423     }, name_strategy, "struct", inst);
  20424     const new_decl = mod.declPtr(new_decl_index);
  20425     new_decl.owns_tv = true;
  20426     errdefer {
  20427         new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
  20428         mod.abortAnonDecl(new_decl_index);
  20429     }
  20430 
  20431     const new_namespace_index = try mod.createNamespace(.{
  20432         .parent = block.namespace.toOptional(),
  20433         .ty = undefined,
  20434         .file_scope = block.getFileScope(mod),
  20435     });
  20436     const new_namespace = mod.namespacePtr(new_namespace_index);
  20437     errdefer mod.destroyNamespace(new_namespace_index);
  20438 
  20439     const struct_index = try mod.createStruct(.{
  20440         .owner_decl = new_decl_index,
  20441         .fields = .{},
  20442         .zir_index = inst,
  20443         .layout = layout,
  20444         .status = .have_field_types,
  20445         .known_non_opv = false,
  20446         .is_tuple = is_tuple,
  20447         .namespace = new_namespace_index,
  20448     });
  20449     const struct_obj = mod.structPtr(struct_index);
  20450     errdefer mod.destroyStruct(struct_index);
  20451 
  20452     const struct_ty = try ip.get(gpa, .{ .struct_type = .{
  20453         .index = struct_index.toOptional(),
  20454         .namespace = new_namespace_index.toOptional(),
  20455     } });
  20456     // TODO: figure out InternPool removals for incremental compilation
  20457     //errdefer ip.remove(struct_ty);
  20458 
  20459     new_decl.ty = Type.type;
  20460     new_decl.val = struct_ty.toValue();
  20461     new_namespace.ty = struct_ty.toType();
  20462 
  20463     // Fields
  20464     const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
  20465     try struct_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len);
  20466     var i: usize = 0;
  20467     while (i < fields_len) : (i += 1) {
  20468         const elem_val = try fields_val.elemValue(mod, i);
  20469         const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod);
  20470         const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20471             try ip.getOrPutString(gpa, "name"),
  20472         ).?);
  20473         const type_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20474             try ip.getOrPutString(gpa, "type"),
  20475         ).?);
  20476         const default_value_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20477             try ip.getOrPutString(gpa, "default_value"),
  20478         ).?);
  20479         const is_comptime_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20480             try ip.getOrPutString(gpa, "is_comptime"),
  20481         ).?);
  20482         const alignment_val = try elem_val.fieldValue(mod, elem_fields.getIndex(
  20483             try ip.getOrPutString(gpa, "alignment"),
  20484         ).?);
  20485 
  20486         if (!try sema.intFitsInType(alignment_val, Type.u32, null)) {
  20487             return sema.fail(block, src, "alignment must fit in 'u32'", .{});
  20488         }
  20489         const abi_align = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?;
  20490 
  20491         if (layout == .Packed) {
  20492             if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
  20493             if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{});
  20494         }
  20495         if (layout == .Extern and is_comptime_val.toBool()) {
  20496             return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{});
  20497         }
  20498 
  20499         const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
  20500 
  20501         if (is_tuple) {
  20502             const field_index = field_name.toUnsigned(ip) orelse return sema.fail(
  20503                 block,
  20504                 src,
  20505                 "tuple cannot have non-numeric field '{}'",
  20506                 .{field_name.fmt(ip)},
  20507             );
  20508 
  20509             if (field_index >= fields_len) {
  20510                 return sema.fail(
  20511                     block,
  20512                     src,
  20513                     "tuple field {} exceeds tuple field count",
  20514                     .{field_index},
  20515                 );
  20516             }
  20517         }
  20518         const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
  20519         if (gop.found_existing) {
  20520             // TODO: better source location
  20521             return sema.fail(block, src, "duplicate struct field {}", .{field_name.fmt(ip)});
  20522         }
  20523 
  20524         const field_ty = type_val.toType();
  20525         const default_val = if (default_value_val.optionalValue(mod)) |opt_val|
  20526             (try sema.pointerDeref(block, src, opt_val, try mod.singleConstPtrType(field_ty)) orelse
  20527                 return sema.failWithNeededComptime(block, src, "struct field default value must be comptime-known")).toIntern()
  20528         else
  20529             .none;
  20530         if (is_comptime_val.toBool() and default_val == .none) {
  20531             return sema.fail(block, src, "comptime field without default initialization value", .{});
  20532         }
  20533 
  20534         gop.value_ptr.* = .{
  20535             .ty = field_ty,
  20536             .abi_align = Alignment.fromByteUnits(abi_align),
  20537             .default_val = default_val,
  20538             .is_comptime = is_comptime_val.toBool(),
  20539             .offset = undefined,
  20540         };
  20541 
  20542         if (field_ty.zigTypeTag(mod) == .Opaque) {
  20543             const msg = msg: {
  20544                 const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  20545                 errdefer msg.destroy(gpa);
  20546 
  20547                 try sema.addDeclaredHereNote(msg, field_ty);
  20548                 break :msg msg;
  20549             };
  20550             return sema.failWithOwnedErrorMsg(msg);
  20551         }
  20552         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  20553             const msg = msg: {
  20554                 const msg = try sema.errMsg(block, src, "struct fields cannot be 'noreturn'", .{});
  20555                 errdefer msg.destroy(gpa);
  20556 
  20557                 try sema.addDeclaredHereNote(msg, field_ty);
  20558                 break :msg msg;
  20559             };
  20560             return sema.failWithOwnedErrorMsg(msg);
  20561         }
  20562         if (struct_obj.layout == .Extern and !try sema.validateExternType(field_ty, .struct_field)) {
  20563             const msg = msg: {
  20564                 const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
  20565                 errdefer msg.destroy(gpa);
  20566 
  20567                 const src_decl = sema.mod.declPtr(block.src_decl);
  20568                 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .struct_field);
  20569 
  20570                 try sema.addDeclaredHereNote(msg, field_ty);
  20571                 break :msg msg;
  20572             };
  20573             return sema.failWithOwnedErrorMsg(msg);
  20574         } else if (struct_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) {
  20575             const msg = msg: {
  20576                 const msg = try sema.errMsg(block, src, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
  20577                 errdefer msg.destroy(gpa);
  20578 
  20579                 const src_decl = sema.mod.declPtr(block.src_decl);
  20580                 try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty);
  20581 
  20582                 try sema.addDeclaredHereNote(msg, field_ty);
  20583                 break :msg msg;
  20584             };
  20585             return sema.failWithOwnedErrorMsg(msg);
  20586         }
  20587     }
  20588 
  20589     if (layout == .Packed) {
  20590         struct_obj.status = .layout_wip;
  20591 
  20592         for (struct_obj.fields.values(), 0..) |field, index| {
  20593             sema.resolveTypeLayout(field.ty) catch |err| switch (err) {
  20594                 error.AnalysisFail => {
  20595                     const msg = sema.err orelse return err;
  20596                     try sema.addFieldErrNote(struct_ty.toType(), index, msg, "while checking this field", .{});
  20597                     return err;
  20598                 },
  20599                 else => return err,
  20600             };
  20601         }
  20602 
  20603         var fields_bit_sum: u64 = 0;
  20604         for (struct_obj.fields.values()) |field| {
  20605             fields_bit_sum += field.ty.bitSize(mod);
  20606         }
  20607 
  20608         if (backing_int_val.optionalValue(mod)) |payload| {
  20609             const backing_int_ty = payload.toType();
  20610             try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum);
  20611             struct_obj.backing_int_ty = backing_int_ty;
  20612         } else {
  20613             struct_obj.backing_int_ty = try mod.intType(.unsigned, @as(u16, @intCast(fields_bit_sum)));
  20614         }
  20615 
  20616         struct_obj.status = .have_layout;
  20617     }
  20618 
  20619     const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
  20620     try mod.finalizeAnonDecl(new_decl_index);
  20621     return decl_val;
  20622 }
  20623 
  20624 fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
  20625     const va_list_ty = try sema.getBuiltinType("VaList");
  20626     const va_list_ptr = try sema.mod.singleMutPtrType(va_list_ty);
  20627 
  20628     const inst = try sema.resolveInst(zir_ref);
  20629     return sema.coerce(block, va_list_ptr, inst, src);
  20630 }
  20631 
  20632 fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20633     const mod = sema.mod;
  20634     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  20635     const src = LazySrcLoc.nodeOffset(extra.node);
  20636     const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  20637     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  20638 
  20639     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs);
  20640     const arg_ty = try sema.resolveType(block, ty_src, extra.rhs);
  20641 
  20642     if (!try sema.validateExternType(arg_ty, .param_ty)) {
  20643         const msg = msg: {
  20644             const msg = try sema.errMsg(block, ty_src, "cannot get '{}' from variadic argument", .{arg_ty.fmt(sema.mod)});
  20645             errdefer msg.destroy(sema.gpa);
  20646 
  20647             const src_decl = sema.mod.declPtr(block.src_decl);
  20648             try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), arg_ty, .param_ty);
  20649 
  20650             try sema.addDeclaredHereNote(msg, arg_ty);
  20651             break :msg msg;
  20652         };
  20653         return sema.failWithOwnedErrorMsg(msg);
  20654     }
  20655 
  20656     try sema.requireRuntimeBlock(block, src, null);
  20657     return block.addTyOp(.c_va_arg, arg_ty, va_list_ref);
  20658 }
  20659 
  20660 fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20661     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  20662     const src = LazySrcLoc.nodeOffset(extra.node);
  20663     const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  20664 
  20665     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
  20666     const va_list_ty = try sema.getBuiltinType("VaList");
  20667 
  20668     try sema.requireRuntimeBlock(block, src, null);
  20669     return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref);
  20670 }
  20671 
  20672 fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20673     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  20674     const src = LazySrcLoc.nodeOffset(extra.node);
  20675     const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  20676 
  20677     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
  20678 
  20679     try sema.requireRuntimeBlock(block, src, null);
  20680     return block.addUnOp(.c_va_end, va_list_ref);
  20681 }
  20682 
  20683 fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20684     const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand)));
  20685 
  20686     const va_list_ty = try sema.getBuiltinType("VaList");
  20687     try sema.requireRuntimeBlock(block, src, null);
  20688     return block.addInst(.{
  20689         .tag = .c_va_start,
  20690         .data = .{ .ty = va_list_ty },
  20691     });
  20692 }
  20693 
  20694 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20695     const mod = sema.mod;
  20696     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  20697     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20698     const ty = try sema.resolveType(block, ty_src, inst_data.operand);
  20699 
  20700     var anon_decl = try block.startAnonDecl();
  20701     defer anon_decl.deinit();
  20702 
  20703     var bytes = std.ArrayList(u8).init(sema.arena);
  20704     defer bytes.deinit();
  20705     try ty.print(bytes.writer(), mod);
  20706 
  20707     const decl_ty = try mod.arrayType(.{
  20708         .len = bytes.items.len,
  20709         .sentinel = .zero_u8,
  20710         .child = .u8_type,
  20711     });
  20712     const new_decl = try anon_decl.finish(
  20713         decl_ty,
  20714         (try mod.intern(.{ .aggregate = .{
  20715             .ty = decl_ty.toIntern(),
  20716             .storage = .{ .bytes = bytes.items },
  20717         } })).toValue(),
  20718         .none, // default alignment
  20719     );
  20720 
  20721     return sema.analyzeDeclRef(new_decl);
  20722 }
  20723 
  20724 fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20725     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  20726     const src = inst_data.src();
  20727     return sema.failWithUseOfAsync(block, src);
  20728 }
  20729 
  20730 fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20731     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  20732     const src = inst_data.src();
  20733     return sema.failWithUseOfAsync(block, src);
  20734 }
  20735 
  20736 fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20737     const mod = sema.mod;
  20738     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  20739     const src = inst_data.src();
  20740     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  20741     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20742     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat");
  20743     const operand = try sema.resolveInst(extra.rhs);
  20744     const operand_ty = sema.typeOf(operand);
  20745 
  20746     _ = try sema.checkIntType(block, src, dest_ty);
  20747     try sema.checkFloatType(block, operand_src, operand_ty);
  20748 
  20749     if (try sema.resolveMaybeUndefVal(operand)) |val| {
  20750         const result_val = try sema.intFromFloat(block, operand_src, val, operand_ty, dest_ty);
  20751         return sema.addConstant(result_val);
  20752     } else if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
  20753         return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_int' must be comptime-known");
  20754     }
  20755 
  20756     try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
  20757     if (dest_ty.intInfo(mod).bits == 0) {
  20758         if (block.wantSafety()) {
  20759             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)));
  20760             try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds);
  20761         }
  20762         return sema.addConstant(try mod.intValue(dest_ty, 0));
  20763     }
  20764     const result = try block.addTyOp(if (block.float_mode == .Optimized) .int_from_float_optimized else .int_from_float, dest_ty, operand);
  20765     if (block.wantSafety()) {
  20766         const back = try block.addTyOp(.float_from_int, operand_ty, result);
  20767         const diff = try block.addBinOp(.sub, operand, back);
  20768         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)));
  20769         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)));
  20770         const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg);
  20771         try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds);
  20772     }
  20773     return result;
  20774 }
  20775 
  20776 fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20777     const mod = sema.mod;
  20778     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  20779     const src = inst_data.src();
  20780     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  20781     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20782     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt");
  20783     const operand = try sema.resolveInst(extra.rhs);
  20784     const operand_ty = sema.typeOf(operand);
  20785 
  20786     try sema.checkFloatType(block, src, dest_ty);
  20787     _ = try sema.checkIntType(block, operand_src, operand_ty);
  20788 
  20789     if (try sema.resolveMaybeUndefVal(operand)) |val| {
  20790         const result_val = try val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, sema.mod, sema);
  20791         return sema.addConstant(result_val);
  20792     } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) {
  20793         return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_float' must be comptime-known");
  20794     }
  20795 
  20796     try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
  20797     return block.addTyOp(.float_from_int, dest_ty, operand);
  20798 }
  20799 
  20800 fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20801     const mod = sema.mod;
  20802     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  20803     const src = inst_data.src();
  20804 
  20805     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  20806 
  20807     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20808     const operand_res = try sema.resolveInst(extra.rhs);
  20809     const operand_coerced = try sema.coerce(block, Type.usize, operand_res, operand_src);
  20810 
  20811     const ptr_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt");
  20812     try sema.checkPtrType(block, src, ptr_ty);
  20813     const elem_ty = ptr_ty.elemType2(mod);
  20814     const ptr_align = try ptr_ty.ptrAlignmentAdvanced(mod, sema);
  20815 
  20816     if (ptr_ty.isSlice(mod)) {
  20817         const msg = msg: {
  20818             const msg = try sema.errMsg(block, src, "integer cannot be converted to slice type '{}'", .{ptr_ty.fmt(sema.mod)});
  20819             errdefer msg.destroy(sema.gpa);
  20820             try sema.errNote(block, src, msg, "slice length cannot be inferred from address", .{});
  20821             break :msg msg;
  20822         };
  20823         return sema.failWithOwnedErrorMsg(msg);
  20824     }
  20825 
  20826     if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
  20827         const addr = val.toUnsignedInt(mod);
  20828         if (!ptr_ty.isAllowzeroPtr(mod) and addr == 0)
  20829             return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{ptr_ty.fmt(sema.mod)});
  20830         if (addr != 0 and ptr_align != 0 and addr % ptr_align != 0)
  20831             return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)});
  20832 
  20833         const ptr_val = switch (ptr_ty.zigTypeTag(mod)) {
  20834             .Optional => (try mod.intern(.{ .opt = .{
  20835                 .ty = ptr_ty.toIntern(),
  20836                 .val = if (addr == 0) .none else (try mod.ptrIntValue(ptr_ty.childType(mod), addr)).toIntern(),
  20837             } })).toValue(),
  20838             .Pointer => try mod.ptrIntValue(ptr_ty, addr),
  20839             else => unreachable,
  20840         };
  20841         return sema.addConstant(ptr_val);
  20842     }
  20843 
  20844     try sema.requireRuntimeBlock(block, src, operand_src);
  20845     if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) {
  20846         if (!ptr_ty.isAllowzeroPtr(mod)) {
  20847             const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
  20848             try sema.addSafetyCheck(block, is_non_zero, .cast_to_null);
  20849         }
  20850 
  20851         if (ptr_align > 1) {
  20852             const align_minus_1 = try sema.addConstant(
  20853                 try mod.intValue(Type.usize, ptr_align - 1),
  20854             );
  20855             const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1);
  20856             const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  20857             try sema.addSafetyCheck(block, is_aligned, .incorrect_alignment);
  20858         }
  20859     }
  20860     return block.addBitCast(ptr_ty, operand_coerced);
  20861 }
  20862 
  20863 fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20864     const mod = sema.mod;
  20865     const ip = &mod.intern_pool;
  20866     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  20867     const src = LazySrcLoc.nodeOffset(extra.node);
  20868     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  20869     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@errSetCast");
  20870     const operand = try sema.resolveInst(extra.rhs);
  20871     const operand_ty = sema.typeOf(operand);
  20872     try sema.checkErrorSetType(block, src, dest_ty);
  20873     try sema.checkErrorSetType(block, operand_src, operand_ty);
  20874 
  20875     // operand must be defined since it can be an invalid error value
  20876     const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand);
  20877 
  20878     if (disjoint: {
  20879         // Try avoiding resolving inferred error sets if we can
  20880         if (!dest_ty.isAnyError(mod) and dest_ty.errorSetNames(mod).len == 0) break :disjoint true;
  20881         if (!operand_ty.isAnyError(mod) and operand_ty.errorSetNames(mod).len == 0) break :disjoint true;
  20882         if (dest_ty.isAnyError(mod)) break :disjoint false;
  20883         if (operand_ty.isAnyError(mod)) break :disjoint false;
  20884         for (dest_ty.errorSetNames(mod)) |dest_err_name| {
  20885             if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name))
  20886                 break :disjoint false;
  20887         }
  20888 
  20889         if (!ip.isInferredErrorSetType(dest_ty.toIntern()) and
  20890             !ip.isInferredErrorSetType(operand_ty.toIntern()))
  20891         {
  20892             break :disjoint true;
  20893         }
  20894 
  20895         try sema.resolveInferredErrorSetTy(block, src, dest_ty);
  20896         try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty);
  20897         for (dest_ty.errorSetNames(mod)) |dest_err_name| {
  20898             if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name))
  20899                 break :disjoint false;
  20900         }
  20901 
  20902         break :disjoint true;
  20903     }) {
  20904         const msg = msg: {
  20905             const msg = try sema.errMsg(
  20906                 block,
  20907                 src,
  20908                 "error sets '{}' and '{}' have no common errors",
  20909                 .{ operand_ty.fmt(sema.mod), dest_ty.fmt(sema.mod) },
  20910             );
  20911             errdefer msg.destroy(sema.gpa);
  20912             try sema.addDeclaredHereNote(msg, operand_ty);
  20913             try sema.addDeclaredHereNote(msg, dest_ty);
  20914             break :msg msg;
  20915         };
  20916         return sema.failWithOwnedErrorMsg(msg);
  20917     }
  20918 
  20919     if (maybe_operand_val) |val| {
  20920         if (!dest_ty.isAnyError(mod)) {
  20921             const error_name = mod.intern_pool.indexToKey(val.toIntern()).err.name;
  20922             if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) {
  20923                 const msg = msg: {
  20924                     const msg = try sema.errMsg(
  20925                         block,
  20926                         src,
  20927                         "'error.{}' not a member of error set '{}'",
  20928                         .{ error_name.fmt(ip), dest_ty.fmt(sema.mod) },
  20929                     );
  20930                     errdefer msg.destroy(sema.gpa);
  20931                     try sema.addDeclaredHereNote(msg, dest_ty);
  20932                     break :msg msg;
  20933                 };
  20934                 return sema.failWithOwnedErrorMsg(msg);
  20935             }
  20936         }
  20937 
  20938         return sema.addConstant(try mod.getCoerced(val, dest_ty));
  20939     }
  20940 
  20941     try sema.requireRuntimeBlock(block, src, operand_src);
  20942     if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) {
  20943         const err_int_inst = try block.addBitCast(Type.err_int, operand);
  20944         const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
  20945         try sema.addSafetyCheck(block, ok, .invalid_error_code);
  20946     }
  20947     return block.addBitCast(dest_ty, operand);
  20948 }
  20949 
  20950 fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  20951     const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(u5, @truncate(extended.small)));
  20952     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  20953     const src = LazySrcLoc.nodeOffset(extra.node);
  20954     const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node };
  20955     const operand = try sema.resolveInst(extra.rhs);
  20956     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName());
  20957     return sema.ptrCastFull(
  20958         block,
  20959         flags,
  20960         src,
  20961         operand,
  20962         operand_src,
  20963         dest_ty,
  20964     );
  20965 }
  20966 
  20967 fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20968     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  20969     const src = inst_data.src();
  20970     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  20971     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  20972     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, "@ptrCast");
  20973     const operand = try sema.resolveInst(extra.rhs);
  20974 
  20975     return sema.ptrCastFull(
  20976         block,
  20977         .{ .ptr_cast = true },
  20978         src,
  20979         operand,
  20980         operand_src,
  20981         dest_ty,
  20982     );
  20983 }
  20984 
  20985 fn ptrCastFull(
  20986     sema: *Sema,
  20987     block: *Block,
  20988     flags: Zir.Inst.FullPtrCastFlags,
  20989     src: LazySrcLoc,
  20990     operand: Air.Inst.Ref,
  20991     operand_src: LazySrcLoc,
  20992     dest_ty: Type,
  20993 ) CompileError!Air.Inst.Ref {
  20994     const mod = sema.mod;
  20995     const operand_ty = sema.typeOf(operand);
  20996 
  20997     try sema.checkPtrType(block, src, dest_ty);
  20998     try sema.checkPtrOperand(block, operand_src, operand_ty);
  20999 
  21000     const src_info = operand_ty.ptrInfo(mod);
  21001     const dest_info = dest_ty.ptrInfo(mod);
  21002 
  21003     try sema.resolveTypeLayout(src_info.child.toType());
  21004     try sema.resolveTypeLayout(dest_info.child.toType());
  21005 
  21006     const src_slice_like = src_info.flags.size == .Slice or
  21007         (src_info.flags.size == .One and src_info.child.toType().zigTypeTag(mod) == .Array);
  21008 
  21009     const dest_slice_like = dest_info.flags.size == .Slice or
  21010         (dest_info.flags.size == .One and dest_info.child.toType().zigTypeTag(mod) == .Array);
  21011 
  21012     if (dest_info.flags.size == .Slice and !src_slice_like) {
  21013         return sema.fail(block, src, "illegal pointer cast to slice", .{});
  21014     }
  21015 
  21016     if (dest_info.flags.size == .Slice) {
  21017         const src_elem_size = switch (src_info.flags.size) {
  21018             .Slice => src_info.child.toType().abiSize(mod),
  21019             // pointer to array
  21020             .One => src_info.child.toType().childType(mod).abiSize(mod),
  21021             else => unreachable,
  21022         };
  21023         const dest_elem_size = dest_info.child.toType().abiSize(mod);
  21024         if (src_elem_size != dest_elem_size) {
  21025             return sema.fail(block, src, "TODO: implement @ptrCast between slices changing the length", .{});
  21026         }
  21027     }
  21028 
  21029     // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs
  21030 
  21031     if (!flags.ptr_cast) {
  21032         check_size: {
  21033             if (src_info.flags.size == dest_info.flags.size) break :check_size;
  21034             if (src_slice_like and dest_slice_like) break :check_size;
  21035             if (src_info.flags.size == .C) break :check_size;
  21036             if (dest_info.flags.size == .C) break :check_size;
  21037             return sema.failWithOwnedErrorMsg(msg: {
  21038                 const msg = try sema.errMsg(block, src, "cannot implicitly convert {s} pointer to {s} pointer", .{
  21039                     pointerSizeString(src_info.flags.size),
  21040                     pointerSizeString(dest_info.flags.size),
  21041                 });
  21042                 errdefer msg.destroy(sema.gpa);
  21043                 if (dest_info.flags.size == .Many and
  21044                     (src_info.flags.size == .Slice or
  21045                     (src_info.flags.size == .One and src_info.child.toType().zigTypeTag(mod) == .Array)))
  21046                 {
  21047                     try sema.errNote(block, src, msg, "use 'ptr' field to convert slice to many pointer", .{});
  21048                 } else {
  21049                     try sema.errNote(block, src, msg, "use @ptrCast to change pointer size", .{});
  21050                 }
  21051                 break :msg msg;
  21052             });
  21053         }
  21054 
  21055         check_child: {
  21056             const src_child = if (dest_info.flags.size == .Slice and src_info.flags.size == .One) blk: {
  21057                 // *[n]T -> []T
  21058                 break :blk src_info.child.toType().childType(mod);
  21059             } else src_info.child.toType();
  21060 
  21061             const dest_child = dest_info.child.toType();
  21062 
  21063             const imc_res = try sema.coerceInMemoryAllowed(
  21064                 block,
  21065                 dest_child,
  21066                 src_child,
  21067                 !dest_info.flags.is_const,
  21068                 mod.getTarget(),
  21069                 src,
  21070                 operand_src,
  21071             );
  21072             if (imc_res == .ok) break :check_child;
  21073             return sema.failWithOwnedErrorMsg(msg: {
  21074                 const msg = try sema.errMsg(block, src, "pointer element type '{}' cannot coerce into element type '{}'", .{
  21075                     src_child.fmt(mod),
  21076                     dest_child.fmt(mod),
  21077                 });
  21078                 errdefer msg.destroy(sema.gpa);
  21079                 try imc_res.report(sema, block, src, msg);
  21080                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer element type", .{});
  21081                 break :msg msg;
  21082             });
  21083         }
  21084 
  21085         check_sent: {
  21086             if (dest_info.sentinel == .none) break :check_sent;
  21087             if (src_info.flags.size == .C) break :check_sent;
  21088             if (src_info.sentinel != .none) {
  21089                 const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child);
  21090                 if (dest_info.sentinel == coerced_sent) break :check_sent;
  21091             }
  21092             if (src_slice_like and src_info.flags.size == .One and dest_info.flags.size == .Slice) {
  21093                 // [*]nT -> []T
  21094                 const arr_ty = src_info.child.toType();
  21095                 if (arr_ty.sentinel(mod)) |src_sentinel| {
  21096                     const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_sentinel.toIntern(), dest_info.child);
  21097                     if (dest_info.sentinel == coerced_sent) break :check_sent;
  21098                 }
  21099             }
  21100             return sema.failWithOwnedErrorMsg(msg: {
  21101                 const msg = if (src_info.sentinel == .none) blk: {
  21102                     break :blk try sema.errMsg(block, src, "destination pointer requires '{}' sentinel", .{
  21103                         dest_info.sentinel.toValue().fmtValue(dest_info.child.toType(), mod),
  21104                     });
  21105                 } else blk: {
  21106                     break :blk try sema.errMsg(block, src, "pointer sentinel '{}' cannot coerce into pointer sentinel '{}'", .{
  21107                         src_info.sentinel.toValue().fmtValue(src_info.child.toType(), mod),
  21108                         dest_info.sentinel.toValue().fmtValue(dest_info.child.toType(), mod),
  21109                     });
  21110                 };
  21111                 errdefer msg.destroy(sema.gpa);
  21112                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer sentinel", .{});
  21113                 break :msg msg;
  21114             });
  21115         }
  21116 
  21117         if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) {
  21118             return sema.failWithOwnedErrorMsg(msg: {
  21119                 const msg = try sema.errMsg(block, src, "pointer host size '{}' cannot coerce into pointer host size '{}'", .{
  21120                     src_info.packed_offset.host_size,
  21121                     dest_info.packed_offset.host_size,
  21122                 });
  21123                 errdefer msg.destroy(sema.gpa);
  21124                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer host size", .{});
  21125                 break :msg msg;
  21126             });
  21127         }
  21128 
  21129         if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) {
  21130             return sema.failWithOwnedErrorMsg(msg: {
  21131                 const msg = try sema.errMsg(block, src, "pointer bit offset '{}' cannot coerce into pointer bit offset '{}'", .{
  21132                     src_info.packed_offset.bit_offset,
  21133                     dest_info.packed_offset.bit_offset,
  21134                 });
  21135                 errdefer msg.destroy(sema.gpa);
  21136                 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer bit offset", .{});
  21137                 break :msg msg;
  21138             });
  21139         }
  21140 
  21141         check_allowzero: {
  21142             const src_allows_zero = operand_ty.ptrAllowsZero(mod);
  21143             const dest_allows_zero = dest_ty.ptrAllowsZero(mod);
  21144             if (!src_allows_zero) break :check_allowzero;
  21145             if (dest_allows_zero) break :check_allowzero;
  21146 
  21147             return sema.failWithOwnedErrorMsg(msg: {
  21148                 const msg = try sema.errMsg(block, src, "'{}' could have null values which are illegal in type '{}'", .{
  21149                     operand_ty.fmt(mod),
  21150                     dest_ty.fmt(mod),
  21151                 });
  21152                 errdefer msg.destroy(sema.gpa);
  21153                 try sema.errNote(block, src, msg, "use @ptrCast to assert the pointer is not null", .{});
  21154                 break :msg msg;
  21155             });
  21156         }
  21157 
  21158         // TODO: vector index?
  21159     }
  21160 
  21161     const src_align = src_info.flags.alignment.toByteUnitsOptional() orelse src_info.child.toType().abiAlignment(mod);
  21162     const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse dest_info.child.toType().abiAlignment(mod);
  21163     if (!flags.align_cast) {
  21164         if (dest_align > src_align) {
  21165             return sema.failWithOwnedErrorMsg(msg: {
  21166                 const msg = try sema.errMsg(block, src, "cast increases pointer alignment", .{});
  21167                 errdefer msg.destroy(sema.gpa);
  21168                 try sema.errNote(block, operand_src, msg, "'{}' has alignment '{d}'", .{
  21169                     operand_ty.fmt(mod), src_align,
  21170                 });
  21171                 try sema.errNote(block, src, msg, "'{}' has alignment '{d}'", .{
  21172                     dest_ty.fmt(mod), dest_align,
  21173                 });
  21174                 try sema.errNote(block, src, msg, "use @alignCast to assert pointer alignment", .{});
  21175                 break :msg msg;
  21176             });
  21177         }
  21178     }
  21179 
  21180     if (!flags.addrspace_cast) {
  21181         if (src_info.flags.address_space != dest_info.flags.address_space) {
  21182             return sema.failWithOwnedErrorMsg(msg: {
  21183                 const msg = try sema.errMsg(block, src, "cast changes pointer address space", .{});
  21184                 errdefer msg.destroy(sema.gpa);
  21185                 try sema.errNote(block, operand_src, msg, "'{}' has address space '{s}'", .{
  21186                     operand_ty.fmt(mod), @tagName(src_info.flags.address_space),
  21187                 });
  21188                 try sema.errNote(block, src, msg, "'{}' has address space '{s}'", .{
  21189                     dest_ty.fmt(mod), @tagName(dest_info.flags.address_space),
  21190                 });
  21191                 try sema.errNote(block, src, msg, "use @addrSpaceCast to cast pointer address space", .{});
  21192                 break :msg msg;
  21193             });
  21194         }
  21195     } else {
  21196         // Some address space casts are always disallowed
  21197         if (!target_util.addrSpaceCastIsValid(mod.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) {
  21198             return sema.failWithOwnedErrorMsg(msg: {
  21199                 const msg = try sema.errMsg(block, src, "invalid address space cast", .{});
  21200                 errdefer msg.destroy(sema.gpa);
  21201                 try sema.errNote(block, operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{
  21202                     @tagName(src_info.flags.address_space),
  21203                     @tagName(dest_info.flags.address_space),
  21204                 });
  21205                 break :msg msg;
  21206             });
  21207         }
  21208     }
  21209 
  21210     if (!flags.const_cast) {
  21211         if (src_info.flags.is_const and !dest_info.flags.is_const) {
  21212             return sema.failWithOwnedErrorMsg(msg: {
  21213                 const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{});
  21214                 errdefer msg.destroy(sema.gpa);
  21215                 try sema.errNote(block, src, msg, "use @constCast to discard const qualifier", .{});
  21216                 break :msg msg;
  21217             });
  21218         }
  21219     }
  21220 
  21221     if (!flags.volatile_cast) {
  21222         if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) {
  21223             return sema.failWithOwnedErrorMsg(msg: {
  21224                 const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{});
  21225                 errdefer msg.destroy(sema.gpa);
  21226                 try sema.errNote(block, src, msg, "use @volatileCast to discard volatile qualifier", .{});
  21227                 break :msg msg;
  21228             });
  21229         }
  21230     }
  21231 
  21232     const ptr = if (src_info.flags.size == .Slice and dest_info.flags.size != .Slice) ptr: {
  21233         break :ptr try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty);
  21234     } else operand;
  21235 
  21236     const dest_ptr_ty = if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) blk: {
  21237         // Only convert to a many-pointer at first
  21238         var info = dest_info;
  21239         info.flags.size = .Many;
  21240         const ty = try mod.ptrType(info);
  21241         if (dest_ty.zigTypeTag(mod) == .Optional) {
  21242             break :blk try mod.optionalType(ty.toIntern());
  21243         } else {
  21244             break :blk ty;
  21245         }
  21246     } else dest_ty;
  21247 
  21248     // Cannot do @addrSpaceCast at comptime
  21249     if (!flags.addrspace_cast) {
  21250         if (try sema.resolveMaybeUndefVal(ptr)) |ptr_val| {
  21251             if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isUndef(mod)) {
  21252                 return sema.failWithUseOfUndef(block, operand_src);
  21253             }
  21254             if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isNull(mod)) {
  21255                 return sema.fail(block, operand_src, "null pointer casted to type '{}'", .{dest_ty.fmt(mod)});
  21256             }
  21257             if (dest_align > src_align) {
  21258                 if (try ptr_val.getUnsignedIntAdvanced(mod, null)) |addr| {
  21259                     if (addr % dest_align != 0) {
  21260                         return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ addr, dest_align });
  21261                     }
  21262                 }
  21263             }
  21264             if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) {
  21265                 if (ptr_val.isUndef(mod)) return sema.addConstUndef(dest_ty);
  21266                 const arr_len = try mod.intValue(Type.usize, src_info.child.toType().arrayLen(mod));
  21267                 return sema.addConstant((try mod.intern(.{ .ptr = .{
  21268                     .ty = dest_ty.toIntern(),
  21269                     .addr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr,
  21270                     .len = arr_len.toIntern(),
  21271                 } })).toValue());
  21272             } else {
  21273                 assert(dest_ptr_ty.eql(dest_ty, mod));
  21274                 return sema.addConstant(try mod.getCoerced(ptr_val, dest_ty));
  21275             }
  21276         }
  21277     }
  21278 
  21279     try sema.requireRuntimeBlock(block, src, null);
  21280 
  21281     if (block.wantSafety() and operand_ty.ptrAllowsZero(mod) and !dest_ty.ptrAllowsZero(mod) and
  21282         (try sema.typeHasRuntimeBits(dest_info.child.toType()) or dest_info.child.toType().zigTypeTag(mod) == .Fn))
  21283     {
  21284         const ptr_int = try block.addUnOp(.int_from_ptr, ptr);
  21285         const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
  21286         const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: {
  21287             const len = try sema.analyzeSliceLen(block, operand_src, ptr);
  21288             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
  21289             break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero);
  21290         } else is_non_zero;
  21291         try sema.addSafetyCheck(block, ok, .cast_to_null);
  21292     }
  21293 
  21294     if (block.wantSafety() and dest_align > src_align and try sema.typeHasRuntimeBits(dest_info.child.toType())) {
  21295         const align_minus_1 = try sema.addConstant(
  21296             try mod.intValue(Type.usize, dest_align - 1),
  21297         );
  21298         const ptr_int = try block.addUnOp(.int_from_ptr, ptr);
  21299         const remainder = try block.addBinOp(.bit_and, ptr_int, align_minus_1);
  21300         const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  21301         const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: {
  21302             const len = try sema.analyzeSliceLen(block, operand_src, ptr);
  21303             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
  21304             break :ok try block.addBinOp(.bit_or, len_zero, is_aligned);
  21305         } else is_aligned;
  21306         try sema.addSafetyCheck(block, ok, .incorrect_alignment);
  21307     }
  21308 
  21309     // If we're going from an array pointer to a slice, this will only be the pointer part!
  21310     const result_ptr = if (flags.addrspace_cast) ptr: {
  21311         // We can't change address spaces with a bitcast, so this requires two instructions
  21312         var intermediate_info = src_info;
  21313         intermediate_info.flags.address_space = dest_info.flags.address_space;
  21314         const intermediate_ptr_ty = try mod.ptrType(intermediate_info);
  21315         const intermediate_ty = if (dest_ptr_ty.zigTypeTag(mod) == .Optional) blk: {
  21316             break :blk try mod.optionalType(intermediate_ptr_ty.toIntern());
  21317         } else intermediate_ptr_ty;
  21318         const intermediate = try block.addInst(.{
  21319             .tag = .addrspace_cast,
  21320             .data = .{ .ty_op = .{
  21321                 .ty = try sema.addType(intermediate_ty),
  21322                 .operand = ptr,
  21323             } },
  21324         });
  21325         if (intermediate_ty.eql(dest_ptr_ty, mod)) {
  21326             // We only changed the address space, so no need for a bitcast
  21327             break :ptr intermediate;
  21328         }
  21329         break :ptr try block.addBitCast(dest_ptr_ty, intermediate);
  21330     } else ptr: {
  21331         break :ptr try block.addBitCast(dest_ptr_ty, ptr);
  21332     };
  21333 
  21334     if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) {
  21335         // We have to construct a slice using the operand's child's array length
  21336         // Note that we know from the check at the start of the function that operand_ty is slice-like
  21337         const arr_len = try sema.addConstant(
  21338             try mod.intValue(Type.usize, src_info.child.toType().arrayLen(mod)),
  21339         );
  21340         return block.addInst(.{
  21341             .tag = .slice,
  21342             .data = .{ .ty_pl = .{
  21343                 .ty = try sema.addType(dest_ty),
  21344                 .payload = try sema.addExtra(Air.Bin{
  21345                     .lhs = result_ptr,
  21346                     .rhs = arr_len,
  21347                 }),
  21348             } },
  21349         });
  21350     } else {
  21351         assert(dest_ptr_ty.eql(dest_ty, mod));
  21352         return result_ptr;
  21353     }
  21354 }
  21355 
  21356 fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21357     const mod = sema.mod;
  21358     const flags = @as(Zir.Inst.FullPtrCastFlags, @bitCast(@as(u5, @truncate(extended.small))));
  21359     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  21360     const src = LazySrcLoc.nodeOffset(extra.node);
  21361     const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node };
  21362     const operand = try sema.resolveInst(extra.operand);
  21363     const operand_ty = sema.typeOf(operand);
  21364     try sema.checkPtrOperand(block, operand_src, operand_ty);
  21365 
  21366     var ptr_info = operand_ty.ptrInfo(mod);
  21367     if (flags.const_cast) ptr_info.flags.is_const = false;
  21368     if (flags.volatile_cast) ptr_info.flags.is_volatile = false;
  21369     const dest_ty = try mod.ptrType(ptr_info);
  21370 
  21371     if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
  21372         return sema.addConstant(try mod.getCoerced(operand_val, dest_ty));
  21373     }
  21374 
  21375     try sema.requireRuntimeBlock(block, src, null);
  21376     return block.addBitCast(dest_ty, operand);
  21377 }
  21378 
  21379 fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21380     const mod = sema.mod;
  21381     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  21382     const src = inst_data.src();
  21383     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21384     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21385     const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate");
  21386     const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src);
  21387     const operand = try sema.resolveInst(extra.rhs);
  21388     const operand_ty = sema.typeOf(operand);
  21389     const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
  21390 
  21391     const operand_is_vector = operand_ty.zigTypeTag(mod) == .Vector;
  21392     const dest_is_vector = dest_ty.zigTypeTag(mod) == .Vector;
  21393     if (operand_is_vector != dest_is_vector) {
  21394         return sema.fail(block, operand_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), operand_ty.fmt(mod) });
  21395     }
  21396 
  21397     if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
  21398         return sema.coerce(block, dest_ty, operand, operand_src);
  21399     }
  21400 
  21401     const dest_info = dest_scalar_ty.intInfo(mod);
  21402 
  21403     if (try sema.typeHasOnePossibleValue(dest_ty)) |val| {
  21404         return sema.addConstant(val);
  21405     }
  21406 
  21407     if (operand_scalar_ty.zigTypeTag(mod) != .ComptimeInt) {
  21408         const operand_info = operand_ty.intInfo(mod);
  21409         if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  21410             return sema.addConstant(val);
  21411         }
  21412 
  21413         if (operand_info.signedness != dest_info.signedness) {
  21414             return sema.fail(block, operand_src, "expected {s} integer type, found '{}'", .{
  21415                 @tagName(dest_info.signedness), operand_ty.fmt(mod),
  21416             });
  21417         }
  21418         if (operand_info.bits < dest_info.bits) {
  21419             const msg = msg: {
  21420                 const msg = try sema.errMsg(
  21421                     block,
  21422                     src,
  21423                     "destination type '{}' has more bits than source type '{}'",
  21424                     .{ dest_ty.fmt(mod), operand_ty.fmt(mod) },
  21425                 );
  21426                 errdefer msg.destroy(sema.gpa);
  21427                 try sema.errNote(block, src, msg, "destination type has {d} bits", .{
  21428                     dest_info.bits,
  21429                 });
  21430                 try sema.errNote(block, operand_src, msg, "operand type has {d} bits", .{
  21431                     operand_info.bits,
  21432                 });
  21433                 break :msg msg;
  21434             };
  21435             return sema.failWithOwnedErrorMsg(msg);
  21436         }
  21437     }
  21438 
  21439     if (try sema.resolveMaybeUndefValIntable(operand)) |val| {
  21440         if (val.isUndef(mod)) return sema.addConstUndef(dest_ty);
  21441         if (!dest_is_vector) {
  21442             return sema.addConstant(try mod.getCoerced(
  21443                 try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, mod),
  21444                 dest_ty,
  21445             ));
  21446         }
  21447         const elems = try sema.arena.alloc(InternPool.Index, operand_ty.vectorLen(mod));
  21448         for (elems, 0..) |*elem, i| {
  21449             const elem_val = try val.elemValue(mod, i);
  21450             elem.* = try (try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, mod)).intern(dest_scalar_ty, mod);
  21451         }
  21452         return sema.addConstant((try mod.intern(.{ .aggregate = .{
  21453             .ty = dest_ty.toIntern(),
  21454             .storage = .{ .elems = elems },
  21455         } })).toValue());
  21456     }
  21457 
  21458     try sema.requireRuntimeBlock(block, src, operand_src);
  21459     return block.addTyOp(.trunc, dest_ty, operand);
  21460 }
  21461 
  21462 fn zirBitCount(
  21463     sema: *Sema,
  21464     block: *Block,
  21465     inst: Zir.Inst.Index,
  21466     air_tag: Air.Inst.Tag,
  21467     comptime comptimeOp: fn (val: Value, ty: Type, mod: *Module) u64,
  21468 ) CompileError!Air.Inst.Ref {
  21469     const mod = sema.mod;
  21470     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  21471     const src = inst_data.src();
  21472     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21473     const operand = try sema.resolveInst(inst_data.operand);
  21474     const operand_ty = sema.typeOf(operand);
  21475     _ = try sema.checkIntOrVector(block, operand, operand_src);
  21476     const bits = operand_ty.intInfo(mod).bits;
  21477 
  21478     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  21479         return sema.addConstant(val);
  21480     }
  21481 
  21482     const result_scalar_ty = try mod.smallestUnsignedInt(bits);
  21483     switch (operand_ty.zigTypeTag(mod)) {
  21484         .Vector => {
  21485             const vec_len = operand_ty.vectorLen(mod);
  21486             const result_ty = try mod.vectorType(.{
  21487                 .len = vec_len,
  21488                 .child = result_scalar_ty.toIntern(),
  21489             });
  21490             if (try sema.resolveMaybeUndefVal(operand)) |val| {
  21491                 if (val.isUndef(mod)) return sema.addConstUndef(result_ty);
  21492 
  21493                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  21494                 const scalar_ty = operand_ty.scalarType(mod);
  21495                 for (elems, 0..) |*elem, i| {
  21496                     const elem_val = try val.elemValue(mod, i);
  21497                     const count = comptimeOp(elem_val, scalar_ty, mod);
  21498                     elem.* = (try mod.intValue(result_scalar_ty, count)).toIntern();
  21499                 }
  21500                 return sema.addConstant((try mod.intern(.{ .aggregate = .{
  21501                     .ty = result_ty.toIntern(),
  21502                     .storage = .{ .elems = elems },
  21503                 } })).toValue());
  21504             } else {
  21505                 try sema.requireRuntimeBlock(block, src, operand_src);
  21506                 return block.addTyOp(air_tag, result_ty, operand);
  21507             }
  21508         },
  21509         .Int => {
  21510             if (try sema.resolveMaybeUndefLazyVal(operand)) |val| {
  21511                 if (val.isUndef(mod)) return sema.addConstUndef(result_scalar_ty);
  21512                 return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, mod));
  21513             } else {
  21514                 try sema.requireRuntimeBlock(block, src, operand_src);
  21515                 return block.addTyOp(air_tag, result_scalar_ty, operand);
  21516             }
  21517         },
  21518         else => unreachable,
  21519     }
  21520 }
  21521 
  21522 fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21523     const mod = sema.mod;
  21524     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  21525     const src = inst_data.src();
  21526     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21527     const operand = try sema.resolveInst(inst_data.operand);
  21528     const operand_ty = sema.typeOf(operand);
  21529     const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src);
  21530     const bits = scalar_ty.intInfo(mod).bits;
  21531     if (bits % 8 != 0) {
  21532         return sema.fail(
  21533             block,
  21534             operand_src,
  21535             "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits",
  21536             .{ scalar_ty.fmt(mod), bits },
  21537         );
  21538     }
  21539 
  21540     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  21541         return sema.addConstant(val);
  21542     }
  21543 
  21544     switch (operand_ty.zigTypeTag(mod)) {
  21545         .Int => {
  21546             const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| {
  21547                 if (val.isUndef(mod)) return sema.addConstUndef(operand_ty);
  21548                 const result_val = try val.byteSwap(operand_ty, mod, sema.arena);
  21549                 return sema.addConstant(result_val);
  21550             } else operand_src;
  21551 
  21552             try sema.requireRuntimeBlock(block, src, runtime_src);
  21553             return block.addTyOp(.byte_swap, operand_ty, operand);
  21554         },
  21555         .Vector => {
  21556             const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| {
  21557                 if (val.isUndef(mod))
  21558                     return sema.addConstUndef(operand_ty);
  21559 
  21560                 const vec_len = operand_ty.vectorLen(mod);
  21561                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  21562                 for (elems, 0..) |*elem, i| {
  21563                     const elem_val = try val.elemValue(mod, i);
  21564                     elem.* = try (try elem_val.byteSwap(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod);
  21565                 }
  21566                 return sema.addConstant((try mod.intern(.{ .aggregate = .{
  21567                     .ty = operand_ty.toIntern(),
  21568                     .storage = .{ .elems = elems },
  21569                 } })).toValue());
  21570             } else operand_src;
  21571 
  21572             try sema.requireRuntimeBlock(block, src, runtime_src);
  21573             return block.addTyOp(.byte_swap, operand_ty, operand);
  21574         },
  21575         else => unreachable,
  21576     }
  21577 }
  21578 
  21579 fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21580     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  21581     const src = inst_data.src();
  21582     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  21583     const operand = try sema.resolveInst(inst_data.operand);
  21584     const operand_ty = sema.typeOf(operand);
  21585     const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src);
  21586 
  21587     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  21588         return sema.addConstant(val);
  21589     }
  21590 
  21591     const mod = sema.mod;
  21592     switch (operand_ty.zigTypeTag(mod)) {
  21593         .Int => {
  21594             const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| {
  21595                 if (val.isUndef(mod)) return sema.addConstUndef(operand_ty);
  21596                 const result_val = try val.bitReverse(operand_ty, mod, sema.arena);
  21597                 return sema.addConstant(result_val);
  21598             } else operand_src;
  21599 
  21600             try sema.requireRuntimeBlock(block, src, runtime_src);
  21601             return block.addTyOp(.bit_reverse, operand_ty, operand);
  21602         },
  21603         .Vector => {
  21604             const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| {
  21605                 if (val.isUndef(mod))
  21606                     return sema.addConstUndef(operand_ty);
  21607 
  21608                 const vec_len = operand_ty.vectorLen(mod);
  21609                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  21610                 for (elems, 0..) |*elem, i| {
  21611                     const elem_val = try val.elemValue(mod, i);
  21612                     elem.* = try (try elem_val.bitReverse(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod);
  21613                 }
  21614                 return sema.addConstant((try mod.intern(.{ .aggregate = .{
  21615                     .ty = operand_ty.toIntern(),
  21616                     .storage = .{ .elems = elems },
  21617                 } })).toValue());
  21618             } else operand_src;
  21619 
  21620             try sema.requireRuntimeBlock(block, src, runtime_src);
  21621             return block.addTyOp(.bit_reverse, operand_ty, operand);
  21622         },
  21623         else => unreachable,
  21624     }
  21625 }
  21626 
  21627 fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21628     const offset = try sema.bitOffsetOf(block, inst);
  21629     return sema.addIntUnsigned(Type.comptime_int, offset);
  21630 }
  21631 
  21632 fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21633     const offset = try sema.bitOffsetOf(block, inst);
  21634     // TODO reminder to make this a compile error for packed structs
  21635     return sema.addIntUnsigned(Type.comptime_int, offset / 8);
  21636 }
  21637 
  21638 fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 {
  21639     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  21640     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
  21641     sema.src = src;
  21642     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  21643     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  21644     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21645 
  21646     const ty = try sema.resolveType(block, lhs_src, extra.lhs);
  21647     const field_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, "name of field must be comptime-known");
  21648 
  21649     const mod = sema.mod;
  21650     try sema.resolveTypeLayout(ty);
  21651     switch (ty.zigTypeTag(mod)) {
  21652         .Struct => {},
  21653         else => {
  21654             const msg = msg: {
  21655                 const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(mod)});
  21656                 errdefer msg.destroy(sema.gpa);
  21657                 try sema.addDeclaredHereNote(msg, ty);
  21658                 break :msg msg;
  21659             };
  21660             return sema.failWithOwnedErrorMsg(msg);
  21661         },
  21662     }
  21663 
  21664     const field_index = if (ty.isTuple(mod)) blk: {
  21665         if (mod.intern_pool.stringEqlSlice(field_name, "len")) {
  21666             return sema.fail(block, src, "no offset available for 'len' field of tuple", .{});
  21667         }
  21668         break :blk try sema.tupleFieldIndex(block, ty, field_name, rhs_src);
  21669     } else try sema.structFieldIndex(block, ty, field_name, rhs_src);
  21670 
  21671     if (ty.structFieldIsComptime(field_index, mod)) {
  21672         return sema.fail(block, src, "no offset available for comptime field", .{});
  21673     }
  21674 
  21675     switch (ty.containerLayout(mod)) {
  21676         .Packed => {
  21677             var bit_sum: u64 = 0;
  21678             const fields = ty.structFields(mod);
  21679             for (fields.values(), 0..) |field, i| {
  21680                 if (i == field_index) {
  21681                     return bit_sum;
  21682                 }
  21683                 bit_sum += field.ty.bitSize(mod);
  21684             } else unreachable;
  21685         },
  21686         else => return ty.structFieldOffset(field_index, mod) * 8,
  21687     }
  21688 }
  21689 
  21690 fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
  21691     const mod = sema.mod;
  21692     switch (ty.zigTypeTag(mod)) {
  21693         .Struct, .Enum, .Union, .Opaque => return,
  21694         else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(mod)}),
  21695     }
  21696 }
  21697 
  21698 /// Returns `true` if the type was a comptime_int.
  21699 fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
  21700     const mod = sema.mod;
  21701     switch (try ty.zigTypeTagOrPoison(mod)) {
  21702         .ComptimeInt => return true,
  21703         .Int => return false,
  21704         else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(mod)}),
  21705     }
  21706 }
  21707 
  21708 fn checkInvalidPtrArithmetic(
  21709     sema: *Sema,
  21710     block: *Block,
  21711     src: LazySrcLoc,
  21712     ty: Type,
  21713 ) CompileError!void {
  21714     const mod = sema.mod;
  21715     switch (try ty.zigTypeTagOrPoison(mod)) {
  21716         .Pointer => switch (ty.ptrSize(mod)) {
  21717             .One, .Slice => return,
  21718             .Many, .C => return sema.fail(
  21719                 block,
  21720                 src,
  21721                 "invalid pointer arithmetic operator",
  21722                 .{},
  21723             ),
  21724         },
  21725         else => return,
  21726     }
  21727 }
  21728 
  21729 fn checkArithmeticOp(
  21730     sema: *Sema,
  21731     block: *Block,
  21732     src: LazySrcLoc,
  21733     scalar_tag: std.builtin.TypeId,
  21734     lhs_zig_ty_tag: std.builtin.TypeId,
  21735     rhs_zig_ty_tag: std.builtin.TypeId,
  21736     zir_tag: Zir.Inst.Tag,
  21737 ) CompileError!void {
  21738     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
  21739     const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
  21740 
  21741     if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
  21742         return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{
  21743             @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag),
  21744         });
  21745     }
  21746 }
  21747 
  21748 fn checkPtrOperand(
  21749     sema: *Sema,
  21750     block: *Block,
  21751     ty_src: LazySrcLoc,
  21752     ty: Type,
  21753 ) CompileError!void {
  21754     const mod = sema.mod;
  21755     switch (ty.zigTypeTag(mod)) {
  21756         .Pointer => return,
  21757         .Fn => {
  21758             const msg = msg: {
  21759                 const msg = try sema.errMsg(
  21760                     block,
  21761                     ty_src,
  21762                     "expected pointer, found '{}'",
  21763                     .{ty.fmt(mod)},
  21764                 );
  21765                 errdefer msg.destroy(sema.gpa);
  21766 
  21767                 try sema.errNote(block, ty_src, msg, "use '&' to obtain a function pointer", .{});
  21768 
  21769                 break :msg msg;
  21770             };
  21771             return sema.failWithOwnedErrorMsg(msg);
  21772         },
  21773         .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return,
  21774         else => {},
  21775     }
  21776     return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)});
  21777 }
  21778 
  21779 fn checkPtrType(
  21780     sema: *Sema,
  21781     block: *Block,
  21782     ty_src: LazySrcLoc,
  21783     ty: Type,
  21784 ) CompileError!void {
  21785     const mod = sema.mod;
  21786     switch (ty.zigTypeTag(mod)) {
  21787         .Pointer => return,
  21788         .Fn => {
  21789             const msg = msg: {
  21790                 const msg = try sema.errMsg(
  21791                     block,
  21792                     ty_src,
  21793                     "expected pointer type, found '{}'",
  21794                     .{ty.fmt(mod)},
  21795                 );
  21796                 errdefer msg.destroy(sema.gpa);
  21797 
  21798                 try sema.errNote(block, ty_src, msg, "use '*const ' to make a function pointer type", .{});
  21799 
  21800                 break :msg msg;
  21801             };
  21802             return sema.failWithOwnedErrorMsg(msg);
  21803         },
  21804         .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return,
  21805         else => {},
  21806     }
  21807     return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)});
  21808 }
  21809 
  21810 fn checkVectorElemType(
  21811     sema: *Sema,
  21812     block: *Block,
  21813     ty_src: LazySrcLoc,
  21814     ty: Type,
  21815 ) CompileError!void {
  21816     const mod = sema.mod;
  21817     switch (ty.zigTypeTag(mod)) {
  21818         .Int, .Float, .Bool => return,
  21819         else => if (ty.isPtrAtRuntime(mod)) return,
  21820     }
  21821     return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(mod)});
  21822 }
  21823 
  21824 fn checkFloatType(
  21825     sema: *Sema,
  21826     block: *Block,
  21827     ty_src: LazySrcLoc,
  21828     ty: Type,
  21829 ) CompileError!void {
  21830     const mod = sema.mod;
  21831     switch (ty.zigTypeTag(mod)) {
  21832         .ComptimeInt, .ComptimeFloat, .Float => {},
  21833         else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(mod)}),
  21834     }
  21835 }
  21836 
  21837 fn checkNumericType(
  21838     sema: *Sema,
  21839     block: *Block,
  21840     ty_src: LazySrcLoc,
  21841     ty: Type,
  21842 ) CompileError!void {
  21843     const mod = sema.mod;
  21844     switch (ty.zigTypeTag(mod)) {
  21845         .ComptimeFloat, .Float, .ComptimeInt, .Int => {},
  21846         .Vector => switch (ty.childType(mod).zigTypeTag(mod)) {
  21847             .ComptimeFloat, .Float, .ComptimeInt, .Int => {},
  21848             else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}),
  21849         },
  21850         else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(mod)}),
  21851     }
  21852 }
  21853 
  21854 /// Returns the casted pointer.
  21855 fn checkAtomicPtrOperand(
  21856     sema: *Sema,
  21857     block: *Block,
  21858     elem_ty: Type,
  21859     elem_ty_src: LazySrcLoc,
  21860     ptr: Air.Inst.Ref,
  21861     ptr_src: LazySrcLoc,
  21862     ptr_const: bool,
  21863 ) CompileError!Air.Inst.Ref {
  21864     const mod = sema.mod;
  21865     var diag: Module.AtomicPtrAlignmentDiagnostics = .{};
  21866     const alignment = mod.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) {
  21867         error.OutOfMemory => return error.OutOfMemory,
  21868         error.FloatTooBig => return sema.fail(
  21869             block,
  21870             elem_ty_src,
  21871             "expected {d}-bit float type or smaller; found {d}-bit float type",
  21872             .{ diag.max_bits, diag.bits },
  21873         ),
  21874         error.IntTooBig => return sema.fail(
  21875             block,
  21876             elem_ty_src,
  21877             "expected {d}-bit integer type or smaller; found {d}-bit integer type",
  21878             .{ diag.max_bits, diag.bits },
  21879         ),
  21880         error.BadType => return sema.fail(
  21881             block,
  21882             elem_ty_src,
  21883             "expected bool, integer, float, enum, or pointer type; found '{}'",
  21884             .{elem_ty.fmt(mod)},
  21885         ),
  21886     };
  21887 
  21888     var wanted_ptr_data: InternPool.Key.PtrType = .{
  21889         .child = elem_ty.toIntern(),
  21890         .flags = .{
  21891             .alignment = alignment,
  21892             .is_const = ptr_const,
  21893         },
  21894     };
  21895 
  21896     const ptr_ty = sema.typeOf(ptr);
  21897     const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison(mod)) {
  21898         .Pointer => ptr_ty.ptrInfo(mod),
  21899         else => {
  21900             const wanted_ptr_ty = try mod.ptrType(wanted_ptr_data);
  21901             _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
  21902             unreachable;
  21903         },
  21904     };
  21905 
  21906     wanted_ptr_data.flags.address_space = ptr_data.flags.address_space;
  21907     wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero;
  21908     wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile;
  21909 
  21910     const wanted_ptr_ty = try mod.ptrType(wanted_ptr_data);
  21911     const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
  21912 
  21913     return casted_ptr;
  21914 }
  21915 
  21916 fn checkPtrIsNotComptimeMutable(
  21917     sema: *Sema,
  21918     block: *Block,
  21919     ptr_val: Value,
  21920     ptr_src: LazySrcLoc,
  21921     operand_src: LazySrcLoc,
  21922 ) CompileError!void {
  21923     _ = operand_src;
  21924     if (ptr_val.isComptimeMutablePtr(sema.mod)) {
  21925         return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{});
  21926     }
  21927 }
  21928 
  21929 fn checkComptimeVarStore(
  21930     sema: *Sema,
  21931     block: *Block,
  21932     src: LazySrcLoc,
  21933     decl_ref_mut: InternPool.Key.Ptr.Addr.MutDecl,
  21934 ) CompileError!void {
  21935     if (@intFromEnum(decl_ref_mut.runtime_index) < @intFromEnum(block.runtime_index)) {
  21936         if (block.runtime_cond) |cond_src| {
  21937             const msg = msg: {
  21938                 const msg = try sema.errMsg(block, src, "store to comptime variable depends on runtime condition", .{});
  21939                 errdefer msg.destroy(sema.gpa);
  21940                 try sema.errNote(block, cond_src, msg, "runtime condition here", .{});
  21941                 break :msg msg;
  21942             };
  21943             return sema.failWithOwnedErrorMsg(msg);
  21944         }
  21945         if (block.runtime_loop) |loop_src| {
  21946             const msg = msg: {
  21947                 const msg = try sema.errMsg(block, src, "cannot store to comptime variable in non-inline loop", .{});
  21948                 errdefer msg.destroy(sema.gpa);
  21949                 try sema.errNote(block, loop_src, msg, "non-inline loop here", .{});
  21950                 break :msg msg;
  21951             };
  21952             return sema.failWithOwnedErrorMsg(msg);
  21953         }
  21954         unreachable;
  21955     }
  21956 }
  21957 
  21958 fn checkIntOrVector(
  21959     sema: *Sema,
  21960     block: *Block,
  21961     operand: Air.Inst.Ref,
  21962     operand_src: LazySrcLoc,
  21963 ) CompileError!Type {
  21964     const mod = sema.mod;
  21965     const operand_ty = sema.typeOf(operand);
  21966     switch (try operand_ty.zigTypeTagOrPoison(mod)) {
  21967         .Int => return operand_ty,
  21968         .Vector => {
  21969             const elem_ty = operand_ty.childType(mod);
  21970             switch (try elem_ty.zigTypeTagOrPoison(mod)) {
  21971                 .Int => return elem_ty,
  21972                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{
  21973                     elem_ty.fmt(mod),
  21974                 }),
  21975             }
  21976         },
  21977         else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{
  21978             operand_ty.fmt(mod),
  21979         }),
  21980     }
  21981 }
  21982 
  21983 fn checkIntOrVectorAllowComptime(
  21984     sema: *Sema,
  21985     block: *Block,
  21986     operand_ty: Type,
  21987     operand_src: LazySrcLoc,
  21988 ) CompileError!Type {
  21989     const mod = sema.mod;
  21990     switch (try operand_ty.zigTypeTagOrPoison(mod)) {
  21991         .Int, .ComptimeInt => return operand_ty,
  21992         .Vector => {
  21993             const elem_ty = operand_ty.childType(mod);
  21994             switch (try elem_ty.zigTypeTagOrPoison(mod)) {
  21995                 .Int, .ComptimeInt => return elem_ty,
  21996                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{
  21997                     elem_ty.fmt(mod),
  21998                 }),
  21999             }
  22000         },
  22001         else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{
  22002             operand_ty.fmt(mod),
  22003         }),
  22004     }
  22005 }
  22006 
  22007 fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
  22008     const mod = sema.mod;
  22009     switch (ty.zigTypeTag(mod)) {
  22010         .ErrorSet => return,
  22011         else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(mod)}),
  22012     }
  22013 }
  22014 
  22015 const SimdBinOp = struct {
  22016     len: ?usize,
  22017     /// Coerced to `result_ty`.
  22018     lhs: Air.Inst.Ref,
  22019     /// Coerced to `result_ty`.
  22020     rhs: Air.Inst.Ref,
  22021     lhs_val: ?Value,
  22022     rhs_val: ?Value,
  22023     /// Only different than `scalar_ty` when it is a vector operation.
  22024     result_ty: Type,
  22025     scalar_ty: Type,
  22026 };
  22027 
  22028 fn checkSimdBinOp(
  22029     sema: *Sema,
  22030     block: *Block,
  22031     src: LazySrcLoc,
  22032     uncasted_lhs: Air.Inst.Ref,
  22033     uncasted_rhs: Air.Inst.Ref,
  22034     lhs_src: LazySrcLoc,
  22035     rhs_src: LazySrcLoc,
  22036 ) CompileError!SimdBinOp {
  22037     const mod = sema.mod;
  22038     const lhs_ty = sema.typeOf(uncasted_lhs);
  22039     const rhs_ty = sema.typeOf(uncasted_rhs);
  22040 
  22041     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  22042     var vec_len: ?usize = if (lhs_ty.zigTypeTag(mod) == .Vector) lhs_ty.vectorLen(mod) else null;
  22043     const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{
  22044         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  22045     });
  22046     const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src);
  22047     const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src);
  22048 
  22049     return SimdBinOp{
  22050         .len = vec_len,
  22051         .lhs = lhs,
  22052         .rhs = rhs,
  22053         .lhs_val = try sema.resolveMaybeUndefVal(lhs),
  22054         .rhs_val = try sema.resolveMaybeUndefVal(rhs),
  22055         .result_ty = result_ty,
  22056         .scalar_ty = result_ty.scalarType(mod),
  22057     };
  22058 }
  22059 
  22060 fn checkVectorizableBinaryOperands(
  22061     sema: *Sema,
  22062     block: *Block,
  22063     src: LazySrcLoc,
  22064     lhs_ty: Type,
  22065     rhs_ty: Type,
  22066     lhs_src: LazySrcLoc,
  22067     rhs_src: LazySrcLoc,
  22068 ) CompileError!void {
  22069     const mod = sema.mod;
  22070     const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
  22071     const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
  22072     if (lhs_zig_ty_tag != .Vector and rhs_zig_ty_tag != .Vector) return;
  22073 
  22074     const lhs_is_vector = switch (lhs_zig_ty_tag) {
  22075         .Vector, .Array => true,
  22076         else => false,
  22077     };
  22078     const rhs_is_vector = switch (rhs_zig_ty_tag) {
  22079         .Vector, .Array => true,
  22080         else => false,
  22081     };
  22082 
  22083     if (lhs_is_vector and rhs_is_vector) {
  22084         const lhs_len = lhs_ty.arrayLen(mod);
  22085         const rhs_len = rhs_ty.arrayLen(mod);
  22086         if (lhs_len != rhs_len) {
  22087             const msg = msg: {
  22088                 const msg = try sema.errMsg(block, src, "vector length mismatch", .{});
  22089                 errdefer msg.destroy(sema.gpa);
  22090                 try sema.errNote(block, lhs_src, msg, "length {d} here", .{lhs_len});
  22091                 try sema.errNote(block, rhs_src, msg, "length {d} here", .{rhs_len});
  22092                 break :msg msg;
  22093             };
  22094             return sema.failWithOwnedErrorMsg(msg);
  22095         }
  22096     } else {
  22097         const msg = msg: {
  22098             const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: '{}' and '{}'", .{
  22099                 lhs_ty.fmt(mod), rhs_ty.fmt(mod),
  22100             });
  22101             errdefer msg.destroy(sema.gpa);
  22102             if (lhs_is_vector) {
  22103                 try sema.errNote(block, lhs_src, msg, "vector here", .{});
  22104                 try sema.errNote(block, rhs_src, msg, "scalar here", .{});
  22105             } else {
  22106                 try sema.errNote(block, lhs_src, msg, "scalar here", .{});
  22107                 try sema.errNote(block, rhs_src, msg, "vector here", .{});
  22108             }
  22109             break :msg msg;
  22110         };
  22111         return sema.failWithOwnedErrorMsg(msg);
  22112     }
  22113 }
  22114 
  22115 fn maybeOptionsSrc(sema: *Sema, block: *Block, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc {
  22116     if (base_src == .unneeded) return .unneeded;
  22117     const mod = sema.mod;
  22118     return mod.optionsSrc(mod.declPtr(block.src_decl), base_src, wanted);
  22119 }
  22120 
  22121 fn resolveExportOptions(
  22122     sema: *Sema,
  22123     block: *Block,
  22124     src: LazySrcLoc,
  22125     zir_ref: Zir.Inst.Ref,
  22126 ) CompileError!Module.Export.Options {
  22127     const mod = sema.mod;
  22128     const gpa = sema.gpa;
  22129     const ip = &mod.intern_pool;
  22130     const export_options_ty = try sema.getBuiltinType("ExportOptions");
  22131     const air_ref = try sema.resolveInst(zir_ref);
  22132     const options = try sema.coerce(block, export_options_ty, air_ref, src);
  22133 
  22134     const name_src = sema.maybeOptionsSrc(block, src, "name");
  22135     const linkage_src = sema.maybeOptionsSrc(block, src, "linkage");
  22136     const section_src = sema.maybeOptionsSrc(block, src, "section");
  22137     const visibility_src = sema.maybeOptionsSrc(block, src, "visibility");
  22138 
  22139     const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src);
  22140     const name_val = try sema.resolveConstValue(block, name_src, name_operand, "name of exported value must be comptime-known");
  22141     const name_ty = Type.slice_const_u8;
  22142     const name = try name_val.toAllocatedBytes(name_ty, sema.arena, mod);
  22143 
  22144     const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src);
  22145     const linkage_val = try sema.resolveConstValue(block, linkage_src, linkage_operand, "linkage of exported value must be comptime-known");
  22146     const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val);
  22147 
  22148     const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "section"), section_src);
  22149     const section_opt_val = try sema.resolveConstValue(block, section_src, section_operand, "linksection of exported value must be comptime-known");
  22150     const section_ty = Type.slice_const_u8;
  22151     const section = if (section_opt_val.optionalValue(mod)) |section_val|
  22152         try section_val.toAllocatedBytes(section_ty, sema.arena, mod)
  22153     else
  22154         null;
  22155 
  22156     const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "visibility"), visibility_src);
  22157     const visibility_val = try sema.resolveConstValue(block, visibility_src, visibility_operand, "visibility of exported value must be comptime-known");
  22158     const visibility = mod.toEnum(std.builtin.SymbolVisibility, visibility_val);
  22159 
  22160     if (name.len < 1) {
  22161         return sema.fail(block, name_src, "exported symbol name cannot be empty", .{});
  22162     }
  22163 
  22164     if (visibility != .default and linkage == .Internal) {
  22165         return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{
  22166             name, @tagName(visibility),
  22167         });
  22168     }
  22169 
  22170     return .{
  22171         .name = try ip.getOrPutString(gpa, name),
  22172         .linkage = linkage,
  22173         .section = try ip.getOrPutStringOpt(gpa, section),
  22174         .visibility = visibility,
  22175     };
  22176 }
  22177 
  22178 fn resolveBuiltinEnum(
  22179     sema: *Sema,
  22180     block: *Block,
  22181     src: LazySrcLoc,
  22182     zir_ref: Zir.Inst.Ref,
  22183     comptime name: []const u8,
  22184     reason: []const u8,
  22185 ) CompileError!@field(std.builtin, name) {
  22186     const mod = sema.mod;
  22187     const ty = try sema.getBuiltinType(name);
  22188     const air_ref = try sema.resolveInst(zir_ref);
  22189     const coerced = try sema.coerce(block, ty, air_ref, src);
  22190     const val = try sema.resolveConstValue(block, src, coerced, reason);
  22191     return mod.toEnum(@field(std.builtin, name), val);
  22192 }
  22193 
  22194 fn resolveAtomicOrder(
  22195     sema: *Sema,
  22196     block: *Block,
  22197     src: LazySrcLoc,
  22198     zir_ref: Zir.Inst.Ref,
  22199     reason: []const u8,
  22200 ) CompileError!std.builtin.AtomicOrder {
  22201     return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicOrder", reason);
  22202 }
  22203 
  22204 fn resolveAtomicRmwOp(
  22205     sema: *Sema,
  22206     block: *Block,
  22207     src: LazySrcLoc,
  22208     zir_ref: Zir.Inst.Ref,
  22209 ) CompileError!std.builtin.AtomicRmwOp {
  22210     return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicRmwOp", "@atomicRmW operation must be comptime-known");
  22211 }
  22212 
  22213 fn zirCmpxchg(
  22214     sema: *Sema,
  22215     block: *Block,
  22216     extended: Zir.Inst.Extended.InstData,
  22217 ) CompileError!Air.Inst.Ref {
  22218     const mod = sema.mod;
  22219     const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data;
  22220     const air_tag: Air.Inst.Tag = switch (extended.small) {
  22221         0 => .cmpxchg_weak,
  22222         1 => .cmpxchg_strong,
  22223         else => unreachable,
  22224     };
  22225     const src = LazySrcLoc.nodeOffset(extra.node);
  22226     // zig fmt: off
  22227     const elem_ty_src      : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  22228     const ptr_src          : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  22229     const expected_src     : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node };
  22230     const new_value_src    : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node };
  22231     const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = extra.node };
  22232     const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = extra.node };
  22233     // zig fmt: on
  22234     const expected_value = try sema.resolveInst(extra.expected_value);
  22235     const elem_ty = sema.typeOf(expected_value);
  22236     if (elem_ty.zigTypeTag(mod) == .Float) {
  22237         return sema.fail(
  22238             block,
  22239             elem_ty_src,
  22240             "expected bool, integer, enum, or pointer type; found '{}'",
  22241             .{elem_ty.fmt(mod)},
  22242         );
  22243     }
  22244     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  22245     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  22246     const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src);
  22247     const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, "atomic order of cmpxchg success must be comptime-known");
  22248     const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, "atomic order of cmpxchg failure must be comptime-known");
  22249 
  22250     if (@intFromEnum(success_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) {
  22251         return sema.fail(block, success_order_src, "success atomic ordering must be Monotonic or stricter", .{});
  22252     }
  22253     if (@intFromEnum(failure_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) {
  22254         return sema.fail(block, failure_order_src, "failure atomic ordering must be Monotonic or stricter", .{});
  22255     }
  22256     if (@intFromEnum(failure_order) > @intFromEnum(success_order)) {
  22257         return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{});
  22258     }
  22259     if (failure_order == .Release or failure_order == .AcqRel) {
  22260         return sema.fail(block, failure_order_src, "failure atomic ordering must not be Release or AcqRel", .{});
  22261     }
  22262 
  22263     const result_ty = try mod.optionalType(elem_ty.toIntern());
  22264 
  22265     // special case zero bit types
  22266     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
  22267         return sema.addConstant((try mod.intern(.{ .opt = .{
  22268             .ty = result_ty.toIntern(),
  22269             .val = .none,
  22270         } })).toValue());
  22271     }
  22272 
  22273     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  22274         if (try sema.resolveMaybeUndefVal(expected_value)) |expected_val| {
  22275             if (try sema.resolveMaybeUndefVal(new_value)) |new_val| {
  22276                 if (expected_val.isUndef(mod) or new_val.isUndef(mod)) {
  22277                     // TODO: this should probably cause the memory stored at the pointer
  22278                     // to become undef as well
  22279                     return sema.addConstUndef(result_ty);
  22280                 }
  22281                 const ptr_ty = sema.typeOf(ptr);
  22282                 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
  22283                 const result_val = try mod.intern(.{ .opt = .{
  22284                     .ty = result_ty.toIntern(),
  22285                     .val = if (stored_val.eql(expected_val, elem_ty, mod)) blk: {
  22286                         try sema.storePtr(block, src, ptr, new_value);
  22287                         break :blk .none;
  22288                     } else stored_val.toIntern(),
  22289                 } });
  22290                 return sema.addConstant(result_val.toValue());
  22291             } else break :rs new_value_src;
  22292         } else break :rs expected_src;
  22293     } else ptr_src;
  22294 
  22295     const flags: u32 = @as(u32, @intFromEnum(success_order)) |
  22296         (@as(u32, @intFromEnum(failure_order)) << 3);
  22297 
  22298     try sema.requireRuntimeBlock(block, src, runtime_src);
  22299     return block.addInst(.{
  22300         .tag = air_tag,
  22301         .data = .{ .ty_pl = .{
  22302             .ty = try sema.addType(result_ty),
  22303             .payload = try sema.addExtra(Air.Cmpxchg{
  22304                 .ptr = ptr,
  22305                 .expected_value = expected_value,
  22306                 .new_value = new_value,
  22307                 .flags = flags,
  22308             }),
  22309         } },
  22310     });
  22311 }
  22312 
  22313 fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22314     const mod = sema.mod;
  22315     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22316     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  22317     const len_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
  22318     const scalar_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
  22319     const len = @as(u32, @intCast(try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector splat destination length must be comptime-known")));
  22320     const scalar = try sema.resolveInst(extra.rhs);
  22321     const scalar_ty = sema.typeOf(scalar);
  22322     try sema.checkVectorElemType(block, scalar_src, scalar_ty);
  22323     const vector_ty = try mod.vectorType(.{
  22324         .len = len,
  22325         .child = scalar_ty.toIntern(),
  22326     });
  22327     if (try sema.resolveMaybeUndefVal(scalar)) |scalar_val| {
  22328         if (scalar_val.isUndef(mod)) return sema.addConstUndef(vector_ty);
  22329         return sema.addConstant(try sema.splat(vector_ty, scalar_val));
  22330     }
  22331 
  22332     try sema.requireRuntimeBlock(block, inst_data.src(), scalar_src);
  22333     return block.addTyOp(.splat, vector_ty, scalar);
  22334 }
  22335 
  22336 fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22337     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22338     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  22339     const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22340     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22341     const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp", "@reduce operation must be comptime-known");
  22342     const operand = try sema.resolveInst(extra.rhs);
  22343     const operand_ty = sema.typeOf(operand);
  22344     const mod = sema.mod;
  22345 
  22346     if (operand_ty.zigTypeTag(mod) != .Vector) {
  22347         return sema.fail(block, operand_src, "expected vector, found '{}'", .{operand_ty.fmt(mod)});
  22348     }
  22349 
  22350     const scalar_ty = operand_ty.childType(mod);
  22351 
  22352     // Type-check depending on operation.
  22353     switch (operation) {
  22354         .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(mod)) {
  22355             .Int, .Bool => {},
  22356             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{}'", .{
  22357                 @tagName(operation), operand_ty.fmt(mod),
  22358             }),
  22359         },
  22360         .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(mod)) {
  22361             .Int, .Float => {},
  22362             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{}'", .{
  22363                 @tagName(operation), operand_ty.fmt(mod),
  22364             }),
  22365         },
  22366     }
  22367 
  22368     const vec_len = operand_ty.vectorLen(mod);
  22369     if (vec_len == 0) {
  22370         // TODO re-evaluate if we should introduce a "neutral value" for some operations,
  22371         // e.g. zero for add and one for mul.
  22372         return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{});
  22373     }
  22374 
  22375     if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
  22376         if (operand_val.isUndef(mod)) return sema.addConstUndef(scalar_ty);
  22377 
  22378         var accum: Value = try operand_val.elemValue(mod, 0);
  22379         var i: u32 = 1;
  22380         while (i < vec_len) : (i += 1) {
  22381             const elem_val = try operand_val.elemValue(mod, i);
  22382             switch (operation) {
  22383                 .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, mod),
  22384                 .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, mod),
  22385                 .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, mod),
  22386                 .Min => accum = accum.numberMin(elem_val, mod),
  22387                 .Max => accum = accum.numberMax(elem_val, mod),
  22388                 .Add => accum = try sema.numberAddWrapScalar(accum, elem_val, scalar_ty),
  22389                 .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, mod),
  22390             }
  22391         }
  22392         return sema.addConstant(accum);
  22393     }
  22394 
  22395     try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
  22396     return block.addInst(.{
  22397         .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
  22398         .data = .{ .reduce = .{
  22399             .operand = operand,
  22400             .operation = operation,
  22401         } },
  22402     });
  22403 }
  22404 
  22405 fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22406     const mod = sema.mod;
  22407     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22408     const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
  22409     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22410     const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  22411 
  22412     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  22413     try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
  22414     var a = try sema.resolveInst(extra.a);
  22415     var b = try sema.resolveInst(extra.b);
  22416     var mask = try sema.resolveInst(extra.mask);
  22417     var mask_ty = sema.typeOf(mask);
  22418 
  22419     const mask_len = switch (sema.typeOf(mask).zigTypeTag(mod)) {
  22420         .Array, .Vector => sema.typeOf(mask).arrayLen(mod),
  22421         else => return sema.fail(block, mask_src, "expected vector or array, found '{}'", .{sema.typeOf(mask).fmt(sema.mod)}),
  22422     };
  22423     mask_ty = try mod.vectorType(.{
  22424         .len = @as(u32, @intCast(mask_len)),
  22425         .child = .i32_type,
  22426     });
  22427     mask = try sema.coerce(block, mask_ty, mask, mask_src);
  22428     const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask, "shuffle mask must be comptime-known");
  22429     return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @as(u32, @intCast(mask_len)));
  22430 }
  22431 
  22432 fn analyzeShuffle(
  22433     sema: *Sema,
  22434     block: *Block,
  22435     src_node: i32,
  22436     elem_ty: Type,
  22437     a_arg: Air.Inst.Ref,
  22438     b_arg: Air.Inst.Ref,
  22439     mask: Value,
  22440     mask_len: u32,
  22441 ) CompileError!Air.Inst.Ref {
  22442     const mod = sema.mod;
  22443     const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node };
  22444     const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node };
  22445     const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node };
  22446     var a = a_arg;
  22447     var b = b_arg;
  22448 
  22449     const res_ty = try mod.vectorType(.{
  22450         .len = mask_len,
  22451         .child = elem_ty.toIntern(),
  22452     });
  22453 
  22454     var maybe_a_len = switch (sema.typeOf(a).zigTypeTag(mod)) {
  22455         .Array, .Vector => sema.typeOf(a).arrayLen(mod),
  22456         .Undefined => null,
  22457         else => return sema.fail(block, a_src, "expected vector or array with element type '{}', found '{}'", .{
  22458             elem_ty.fmt(sema.mod),
  22459             sema.typeOf(a).fmt(sema.mod),
  22460         }),
  22461     };
  22462     var maybe_b_len = switch (sema.typeOf(b).zigTypeTag(mod)) {
  22463         .Array, .Vector => sema.typeOf(b).arrayLen(mod),
  22464         .Undefined => null,
  22465         else => return sema.fail(block, b_src, "expected vector or array with element type '{}', found '{}'", .{
  22466             elem_ty.fmt(sema.mod),
  22467             sema.typeOf(b).fmt(sema.mod),
  22468         }),
  22469     };
  22470     if (maybe_a_len == null and maybe_b_len == null) {
  22471         return sema.addConstUndef(res_ty);
  22472     }
  22473     const a_len = @as(u32, @intCast(maybe_a_len orelse maybe_b_len.?));
  22474     const b_len = @as(u32, @intCast(maybe_b_len orelse a_len));
  22475 
  22476     const a_ty = try mod.vectorType(.{
  22477         .len = a_len,
  22478         .child = elem_ty.toIntern(),
  22479     });
  22480     const b_ty = try mod.vectorType(.{
  22481         .len = b_len,
  22482         .child = elem_ty.toIntern(),
  22483     });
  22484 
  22485     if (maybe_a_len == null) a = try sema.addConstUndef(a_ty) else a = try sema.coerce(block, a_ty, a, a_src);
  22486     if (maybe_b_len == null) b = try sema.addConstUndef(b_ty) else b = try sema.coerce(block, b_ty, b, b_src);
  22487 
  22488     const operand_info = [2]std.meta.Tuple(&.{ u64, LazySrcLoc, Type }){
  22489         .{ a_len, a_src, a_ty },
  22490         .{ b_len, b_src, b_ty },
  22491     };
  22492 
  22493     for (0..@as(usize, @intCast(mask_len))) |i| {
  22494         const elem = try mask.elemValue(sema.mod, i);
  22495         if (elem.isUndef(mod)) continue;
  22496         const int = elem.toSignedInt(mod);
  22497         var unsigned: u32 = undefined;
  22498         var chosen: u32 = undefined;
  22499         if (int >= 0) {
  22500             unsigned = @as(u32, @intCast(int));
  22501             chosen = 0;
  22502         } else {
  22503             unsigned = @as(u32, @intCast(~int));
  22504             chosen = 1;
  22505         }
  22506         if (unsigned >= operand_info[chosen][0]) {
  22507             const msg = msg: {
  22508                 const msg = try sema.errMsg(block, mask_src, "mask index '{d}' has out-of-bounds selection", .{i});
  22509                 errdefer msg.destroy(sema.gpa);
  22510 
  22511                 try sema.errNote(block, operand_info[chosen][1], msg, "selected index '{d}' out of bounds of '{}'", .{
  22512                     unsigned,
  22513                     operand_info[chosen][2].fmt(sema.mod),
  22514                 });
  22515 
  22516                 if (chosen == 0) {
  22517                     try sema.errNote(block, b_src, msg, "selections from the second vector are specified with negative numbers", .{});
  22518                 }
  22519 
  22520                 break :msg msg;
  22521             };
  22522             return sema.failWithOwnedErrorMsg(msg);
  22523         }
  22524     }
  22525 
  22526     if (try sema.resolveMaybeUndefVal(a)) |a_val| {
  22527         if (try sema.resolveMaybeUndefVal(b)) |b_val| {
  22528             const values = try sema.arena.alloc(InternPool.Index, mask_len);
  22529             for (values, 0..) |*value, i| {
  22530                 const mask_elem_val = try mask.elemValue(sema.mod, i);
  22531                 if (mask_elem_val.isUndef(mod)) {
  22532                     value.* = try mod.intern(.{ .undef = elem_ty.toIntern() });
  22533                     continue;
  22534                 }
  22535                 const int = mask_elem_val.toSignedInt(mod);
  22536                 const unsigned = if (int >= 0) @as(u32, @intCast(int)) else @as(u32, @intCast(~int));
  22537                 values[i] = try (try (if (int >= 0) a_val else b_val).elemValue(mod, unsigned)).intern(elem_ty, mod);
  22538             }
  22539             return sema.addConstant((try mod.intern(.{ .aggregate = .{
  22540                 .ty = res_ty.toIntern(),
  22541                 .storage = .{ .elems = values },
  22542             } })).toValue());
  22543         }
  22544     }
  22545 
  22546     // All static analysis passed, and not comptime.
  22547     // For runtime codegen, vectors a and b must be the same length. Here we
  22548     // recursively @shuffle the smaller vector to append undefined elements
  22549     // to it up to the length of the longer vector. This recursion terminates
  22550     // in 1 call because these calls to analyzeShuffle guarantee a_len == b_len.
  22551     if (a_len != b_len) {
  22552         const min_len = @min(a_len, b_len);
  22553         const max_src = if (a_len > b_len) a_src else b_src;
  22554         const max_len = try sema.usizeCast(block, max_src, @max(a_len, b_len));
  22555 
  22556         const expand_mask_values = try sema.arena.alloc(InternPool.Index, max_len);
  22557         for (@as(usize, @intCast(0))..@as(usize, @intCast(min_len))) |i| {
  22558             expand_mask_values[i] = (try mod.intValue(Type.comptime_int, i)).toIntern();
  22559         }
  22560         for (@as(usize, @intCast(min_len))..@as(usize, @intCast(max_len))) |i| {
  22561             expand_mask_values[i] = (try mod.intValue(Type.comptime_int, -1)).toIntern();
  22562         }
  22563         const expand_mask = try mod.intern(.{ .aggregate = .{
  22564             .ty = (try mod.vectorType(.{ .len = @as(u32, @intCast(max_len)), .child = .comptime_int_type })).toIntern(),
  22565             .storage = .{ .elems = expand_mask_values },
  22566         } });
  22567 
  22568         if (a_len < b_len) {
  22569             const undef = try sema.addConstUndef(a_ty);
  22570             a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, expand_mask.toValue(), @as(u32, @intCast(max_len)));
  22571         } else {
  22572             const undef = try sema.addConstUndef(b_ty);
  22573             b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, expand_mask.toValue(), @as(u32, @intCast(max_len)));
  22574         }
  22575     }
  22576 
  22577     return block.addInst(.{
  22578         .tag = .shuffle,
  22579         .data = .{ .ty_pl = .{
  22580             .ty = try sema.addType(res_ty),
  22581             .payload = try block.sema.addExtra(Air.Shuffle{
  22582                 .a = a,
  22583                 .b = b,
  22584                 .mask = mask.toIntern(),
  22585                 .mask_len = mask_len,
  22586             }),
  22587         } },
  22588     });
  22589 }
  22590 
  22591 fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  22592     const mod = sema.mod;
  22593     const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data;
  22594 
  22595     const src = LazySrcLoc.nodeOffset(extra.node);
  22596     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  22597     const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  22598     const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node };
  22599     const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node };
  22600 
  22601     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  22602     try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
  22603     const pred_uncoerced = try sema.resolveInst(extra.pred);
  22604     const pred_ty = sema.typeOf(pred_uncoerced);
  22605 
  22606     const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison(mod)) {
  22607         .Vector, .Array => pred_ty.arrayLen(mod),
  22608         else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(mod)}),
  22609     };
  22610     const vec_len = @as(u32, @intCast(try sema.usizeCast(block, pred_src, vec_len_u64)));
  22611 
  22612     const bool_vec_ty = try mod.vectorType(.{
  22613         .len = vec_len,
  22614         .child = .bool_type,
  22615     });
  22616     const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src);
  22617 
  22618     const vec_ty = try mod.vectorType(.{
  22619         .len = vec_len,
  22620         .child = elem_ty.toIntern(),
  22621     });
  22622     const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src);
  22623     const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src);
  22624 
  22625     const maybe_pred = try sema.resolveMaybeUndefVal(pred);
  22626     const maybe_a = try sema.resolveMaybeUndefVal(a);
  22627     const maybe_b = try sema.resolveMaybeUndefVal(b);
  22628 
  22629     const runtime_src = if (maybe_pred) |pred_val| rs: {
  22630         if (pred_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22631 
  22632         if (maybe_a) |a_val| {
  22633             if (a_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22634 
  22635             if (maybe_b) |b_val| {
  22636                 if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22637 
  22638                 const elems = try sema.gpa.alloc(InternPool.Index, vec_len);
  22639                 for (elems, 0..) |*elem, i| {
  22640                     const pred_elem_val = try pred_val.elemValue(mod, i);
  22641                     const should_choose_a = pred_elem_val.toBool();
  22642                     elem.* = try (try (if (should_choose_a) a_val else b_val).elemValue(mod, i)).intern(elem_ty, mod);
  22643                 }
  22644 
  22645                 return sema.addConstant((try mod.intern(.{ .aggregate = .{
  22646                     .ty = vec_ty.toIntern(),
  22647                     .storage = .{ .elems = elems },
  22648                 } })).toValue());
  22649             } else {
  22650                 break :rs b_src;
  22651             }
  22652         } else {
  22653             if (maybe_b) |b_val| {
  22654                 if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22655             }
  22656             break :rs a_src;
  22657         }
  22658     } else rs: {
  22659         if (maybe_a) |a_val| {
  22660             if (a_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22661         }
  22662         if (maybe_b) |b_val| {
  22663             if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty);
  22664         }
  22665         break :rs pred_src;
  22666     };
  22667 
  22668     try sema.requireRuntimeBlock(block, src, runtime_src);
  22669     return block.addInst(.{
  22670         .tag = .select,
  22671         .data = .{ .pl_op = .{
  22672             .operand = pred,
  22673             .payload = try block.sema.addExtra(Air.Bin{
  22674                 .lhs = a,
  22675                 .rhs = b,
  22676             }),
  22677         } },
  22678     });
  22679 }
  22680 
  22681 fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22682     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22683     const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data;
  22684     // zig fmt: off
  22685     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22686     const ptr_src    : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22687     const order_src  : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22688     // zig fmt: on
  22689     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  22690     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  22691     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true);
  22692     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicLoad must be comptime-known");
  22693 
  22694     switch (order) {
  22695         .Release, .AcqRel => {
  22696             return sema.fail(
  22697                 block,
  22698                 order_src,
  22699                 "@atomicLoad atomic ordering must not be Release or AcqRel",
  22700                 .{},
  22701             );
  22702         },
  22703         else => {},
  22704     }
  22705 
  22706     if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
  22707         return sema.addConstant(val);
  22708     }
  22709 
  22710     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
  22711         if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| {
  22712             return sema.addConstant(elem_val);
  22713         }
  22714     }
  22715 
  22716     try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);
  22717     return block.addInst(.{
  22718         .tag = .atomic_load,
  22719         .data = .{ .atomic_load = .{
  22720             .ptr = ptr,
  22721             .order = order,
  22722         } },
  22723     });
  22724 }
  22725 
  22726 fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22727     const mod = sema.mod;
  22728     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22729     const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data;
  22730     const src = inst_data.src();
  22731     // zig fmt: off
  22732     const elem_ty_src   : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22733     const ptr_src       : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22734     const op_src        : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22735     const operand_src   : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  22736     const order_src     : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node };
  22737     // zig fmt: on
  22738     const operand = try sema.resolveInst(extra.operand);
  22739     const elem_ty = sema.typeOf(operand);
  22740     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  22741     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  22742     const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation);
  22743 
  22744     switch (elem_ty.zigTypeTag(mod)) {
  22745         .Enum => if (op != .Xchg) {
  22746             return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{});
  22747         },
  22748         .Bool => if (op != .Xchg) {
  22749             return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{});
  22750         },
  22751         .Float => switch (op) {
  22752             .Xchg, .Add, .Sub, .Max, .Min => {},
  22753             else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}),
  22754         },
  22755         else => {},
  22756     }
  22757     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicRmW must be comptime-known");
  22758 
  22759     if (order == .Unordered) {
  22760         return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be Unordered", .{});
  22761     }
  22762 
  22763     // special case zero bit types
  22764     if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
  22765         return sema.addConstant(val);
  22766     }
  22767 
  22768     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  22769         const maybe_operand_val = try sema.resolveMaybeUndefVal(operand);
  22770         const operand_val = maybe_operand_val orelse {
  22771             try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src);
  22772             break :rs operand_src;
  22773         };
  22774         if (ptr_val.isComptimeMutablePtr(mod)) {
  22775             const ptr_ty = sema.typeOf(ptr);
  22776             const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
  22777             const new_val = switch (op) {
  22778                 // zig fmt: off
  22779                 .Xchg => operand_val,
  22780                 .Add  => try sema.numberAddWrapScalar(stored_val, operand_val, elem_ty),
  22781                 .Sub  => try sema.numberSubWrapScalar(stored_val, operand_val, elem_ty),
  22782                 .And  => try                   stored_val.bitwiseAnd   (operand_val, elem_ty, sema.arena, mod),
  22783                 .Nand => try                   stored_val.bitwiseNand  (operand_val, elem_ty, sema.arena, mod),
  22784                 .Or   => try                   stored_val.bitwiseOr    (operand_val, elem_ty, sema.arena, mod),
  22785                 .Xor  => try                   stored_val.bitwiseXor   (operand_val, elem_ty, sema.arena, mod),
  22786                 .Max  =>                       stored_val.numberMax    (operand_val,                      mod),
  22787                 .Min  =>                       stored_val.numberMin    (operand_val,                      mod),
  22788                 // zig fmt: on
  22789             };
  22790             try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty);
  22791             return sema.addConstant(stored_val);
  22792         } else break :rs ptr_src;
  22793     } else ptr_src;
  22794 
  22795     const flags: u32 = @as(u32, @intFromEnum(order)) | (@as(u32, @intFromEnum(op)) << 3);
  22796 
  22797     try sema.requireRuntimeBlock(block, src, runtime_src);
  22798     return block.addInst(.{
  22799         .tag = .atomic_rmw,
  22800         .data = .{ .pl_op = .{
  22801             .operand = ptr,
  22802             .payload = try sema.addExtra(Air.AtomicRmw{
  22803                 .operand = operand,
  22804                 .flags = flags,
  22805             }),
  22806         } },
  22807     });
  22808 }
  22809 
  22810 fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  22811     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22812     const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
  22813     const src = inst_data.src();
  22814     // zig fmt: off
  22815     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22816     const ptr_src       : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22817     const operand_src   : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22818     const order_src     : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  22819     // zig fmt: on
  22820     const operand = try sema.resolveInst(extra.operand);
  22821     const elem_ty = sema.typeOf(operand);
  22822     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  22823     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  22824     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicStore must be comptime-known");
  22825 
  22826     const air_tag: Air.Inst.Tag = switch (order) {
  22827         .Acquire, .AcqRel => {
  22828             return sema.fail(
  22829                 block,
  22830                 order_src,
  22831                 "@atomicStore atomic ordering must not be Acquire or AcqRel",
  22832                 .{},
  22833             );
  22834         },
  22835         .Unordered => .atomic_store_unordered,
  22836         .Monotonic => .atomic_store_monotonic,
  22837         .Release => .atomic_store_release,
  22838         .SeqCst => .atomic_store_seq_cst,
  22839     };
  22840 
  22841     return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
  22842 }
  22843 
  22844 fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22845     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22846     const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data;
  22847     const src = inst_data.src();
  22848 
  22849     const mulend1_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22850     const mulend2_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22851     const addend_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
  22852 
  22853     const addend = try sema.resolveInst(extra.addend);
  22854     const ty = sema.typeOf(addend);
  22855     const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src);
  22856     const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src);
  22857 
  22858     const maybe_mulend1 = try sema.resolveMaybeUndefVal(mulend1);
  22859     const maybe_mulend2 = try sema.resolveMaybeUndefVal(mulend2);
  22860     const maybe_addend = try sema.resolveMaybeUndefVal(addend);
  22861     const mod = sema.mod;
  22862 
  22863     switch (ty.scalarType(mod).zigTypeTag(mod)) {
  22864         .ComptimeFloat, .Float => {},
  22865         else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(sema.mod)}),
  22866     }
  22867 
  22868     const runtime_src = if (maybe_mulend1) |mulend1_val| rs: {
  22869         if (maybe_mulend2) |mulend2_val| {
  22870             if (mulend2_val.isUndef(mod)) return sema.addConstUndef(ty);
  22871 
  22872             if (maybe_addend) |addend_val| {
  22873                 if (addend_val.isUndef(mod)) return sema.addConstUndef(ty);
  22874                 const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, sema.mod);
  22875                 return sema.addConstant(result_val);
  22876             } else {
  22877                 break :rs addend_src;
  22878             }
  22879         } else {
  22880             if (maybe_addend) |addend_val| {
  22881                 if (addend_val.isUndef(mod)) return sema.addConstUndef(ty);
  22882             }
  22883             break :rs mulend2_src;
  22884         }
  22885     } else rs: {
  22886         if (maybe_mulend2) |mulend2_val| {
  22887             if (mulend2_val.isUndef(mod)) return sema.addConstUndef(ty);
  22888         }
  22889         if (maybe_addend) |addend_val| {
  22890             if (addend_val.isUndef(mod)) return sema.addConstUndef(ty);
  22891         }
  22892         break :rs mulend1_src;
  22893     };
  22894 
  22895     try sema.requireRuntimeBlock(block, src, runtime_src);
  22896     return block.addInst(.{
  22897         .tag = .mul_add,
  22898         .data = .{ .pl_op = .{
  22899             .operand = addend,
  22900             .payload = try sema.addExtra(Air.Bin{
  22901                 .lhs = mulend1,
  22902                 .rhs = mulend2,
  22903             }),
  22904         } },
  22905     });
  22906 }
  22907 
  22908 fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22909     const tracy = trace(@src());
  22910     defer tracy.end();
  22911 
  22912     const mod = sema.mod;
  22913     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22914     const modifier_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22915     const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22916     const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22917     const call_src = inst_data.src();
  22918 
  22919     const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;
  22920     var func = try sema.resolveInst(extra.callee);
  22921 
  22922     const modifier_ty = try sema.getBuiltinType("CallModifier");
  22923     const air_ref = try sema.resolveInst(extra.modifier);
  22924     const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src);
  22925     const modifier_val = try sema.resolveConstValue(block, modifier_src, modifier_ref, "call modifier must be comptime-known");
  22926     var modifier = mod.toEnum(std.builtin.CallModifier, modifier_val);
  22927     switch (modifier) {
  22928         // These can be upgraded to comptime or nosuspend calls.
  22929         .auto, .never_tail, .no_async => {
  22930             if (block.is_comptime) {
  22931                 if (modifier == .never_tail) {
  22932                     return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{});
  22933                 }
  22934                 modifier = .compile_time;
  22935             } else if (extra.flags.is_nosuspend) {
  22936                 modifier = .no_async;
  22937             }
  22938         },
  22939         // These can be upgraded to comptime. nosuspend bit can be safely ignored.
  22940         .always_inline, .compile_time => {
  22941             _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse {
  22942                 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)});
  22943             };
  22944 
  22945             if (block.is_comptime) {
  22946                 modifier = .compile_time;
  22947             }
  22948         },
  22949         .always_tail => {
  22950             if (block.is_comptime) {
  22951                 modifier = .compile_time;
  22952             }
  22953         },
  22954         .async_kw => {
  22955             if (extra.flags.is_nosuspend) {
  22956                 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{});
  22957             }
  22958             if (block.is_comptime) {
  22959                 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{});
  22960             }
  22961         },
  22962         .never_inline => {
  22963             if (block.is_comptime) {
  22964                 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{});
  22965             }
  22966         },
  22967     }
  22968 
  22969     const args = try sema.resolveInst(extra.args);
  22970 
  22971     const args_ty = sema.typeOf(args);
  22972     if (!args_ty.isTuple(mod) and args_ty.toIntern() != .empty_struct_type) {
  22973         return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(sema.mod)});
  22974     }
  22975 
  22976     var resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(mod));
  22977     for (resolved_args, 0..) |*resolved, i| {
  22978         resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @as(u32, @intCast(i)), args_ty);
  22979     }
  22980 
  22981     const callee_ty = sema.typeOf(func);
  22982     const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false);
  22983     const ensure_result_used = extra.flags.ensure_result_used;
  22984     return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, null, null);
  22985 }
  22986 
  22987 fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22988     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  22989     const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data;
  22990     const src = inst_data.src();
  22991     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  22992     const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  22993     const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
  22994 
  22995     const parent_ty = try sema.resolveType(block, ty_src, extra.parent_type);
  22996     const field_name = try sema.resolveConstStringIntern(block, name_src, extra.field_name, "field name must be comptime-known");
  22997     const field_ptr = try sema.resolveInst(extra.field_ptr);
  22998     const field_ptr_ty = sema.typeOf(field_ptr);
  22999     const mod = sema.mod;
  23000     const ip = &mod.intern_pool;
  23001 
  23002     if (parent_ty.zigTypeTag(mod) != .Struct and parent_ty.zigTypeTag(mod) != .Union) {
  23003         return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)});
  23004     }
  23005     try sema.resolveTypeLayout(parent_ty);
  23006 
  23007     const field_index = switch (parent_ty.zigTypeTag(mod)) {
  23008         .Struct => blk: {
  23009             if (parent_ty.isTuple(mod)) {
  23010                 if (ip.stringEqlSlice(field_name, "len")) {
  23011                     return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
  23012                 }
  23013                 break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, name_src);
  23014             } else {
  23015                 break :blk try sema.structFieldIndex(block, parent_ty, field_name, name_src);
  23016             }
  23017         },
  23018         .Union => try sema.unionFieldIndex(block, parent_ty, field_name, name_src),
  23019         else => unreachable,
  23020     };
  23021 
  23022     if (parent_ty.zigTypeTag(mod) == .Struct and parent_ty.structFieldIsComptime(field_index, mod)) {
  23023         return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{});
  23024     }
  23025 
  23026     try sema.checkPtrOperand(block, ptr_src, field_ptr_ty);
  23027     const field_ptr_ty_info = field_ptr_ty.ptrInfo(mod);
  23028 
  23029     var ptr_ty_data: InternPool.Key.PtrType = .{
  23030         .child = parent_ty.structFieldType(field_index, mod).toIntern(),
  23031         .flags = .{
  23032             .address_space = field_ptr_ty_info.flags.address_space,
  23033             .is_const = field_ptr_ty_info.flags.is_const,
  23034         },
  23035     };
  23036 
  23037     if (parent_ty.containerLayout(mod) == .Packed) {
  23038         return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{});
  23039     } else {
  23040         ptr_ty_data.flags.alignment = blk: {
  23041             if (mod.typeToStruct(parent_ty)) |struct_obj| {
  23042                 break :blk struct_obj.fields.values()[field_index].abi_align;
  23043             } else if (mod.typeToUnion(parent_ty)) |union_obj| {
  23044                 break :blk union_obj.fields.values()[field_index].abi_align;
  23045             } else {
  23046                 break :blk .none;
  23047             }
  23048         };
  23049     }
  23050 
  23051     const actual_field_ptr_ty = try mod.ptrType(ptr_ty_data);
  23052     const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src);
  23053 
  23054     ptr_ty_data.child = parent_ty.toIntern();
  23055     const result_ptr = try mod.ptrType(ptr_ty_data);
  23056 
  23057     if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| {
  23058         const field = switch (ip.indexToKey(field_ptr_val.toIntern())) {
  23059             .ptr => |ptr| switch (ptr.addr) {
  23060                 .field => |field| field,
  23061                 else => null,
  23062             },
  23063             else => null,
  23064         } orelse return sema.fail(block, ptr_src, "pointer value not based on parent struct", .{});
  23065 
  23066         if (field.index != field_index) {
  23067             const msg = msg: {
  23068                 const msg = try sema.errMsg(
  23069                     block,
  23070                     src,
  23071                     "field '{}' has index '{d}' but pointer value is index '{d}' of struct '{}'",
  23072                     .{
  23073                         field_name.fmt(ip),
  23074                         field_index,
  23075                         field.index,
  23076                         parent_ty.fmt(sema.mod),
  23077                     },
  23078                 );
  23079                 errdefer msg.destroy(sema.gpa);
  23080                 try sema.addDeclaredHereNote(msg, parent_ty);
  23081                 break :msg msg;
  23082             };
  23083             return sema.failWithOwnedErrorMsg(msg);
  23084         }
  23085         return sema.addConstant(field.base.toValue());
  23086     }
  23087 
  23088     try sema.requireRuntimeBlock(block, src, ptr_src);
  23089     try sema.queueFullTypeResolution(result_ptr);
  23090     return block.addInst(.{
  23091         .tag = .field_parent_ptr,
  23092         .data = .{ .ty_pl = .{
  23093             .ty = try sema.addType(result_ptr),
  23094             .payload = try block.sema.addExtra(Air.FieldParentPtr{
  23095                 .field_ptr = casted_field_ptr,
  23096                 .field_index = @as(u32, @intCast(field_index)),
  23097             }),
  23098         } },
  23099     });
  23100 }
  23101 
  23102 fn zirMinMax(
  23103     sema: *Sema,
  23104     block: *Block,
  23105     inst: Zir.Inst.Index,
  23106     comptime air_tag: Air.Inst.Tag,
  23107 ) CompileError!Air.Inst.Ref {
  23108     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  23109     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23110     const src = inst_data.src();
  23111     const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23112     const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23113     const lhs = try sema.resolveInst(extra.lhs);
  23114     const rhs = try sema.resolveInst(extra.rhs);
  23115     try sema.checkNumericType(block, lhs_src, sema.typeOf(lhs));
  23116     try sema.checkNumericType(block, rhs_src, sema.typeOf(rhs));
  23117     return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src });
  23118 }
  23119 
  23120 fn zirMinMaxMulti(
  23121     sema: *Sema,
  23122     block: *Block,
  23123     extended: Zir.Inst.Extended.InstData,
  23124     comptime air_tag: Air.Inst.Tag,
  23125 ) CompileError!Air.Inst.Ref {
  23126     const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
  23127     const src_node = extra.data.src_node;
  23128     const src = LazySrcLoc.nodeOffset(src_node);
  23129     const operands = sema.code.refSlice(extra.end, extended.small);
  23130 
  23131     const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
  23132     const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len);
  23133 
  23134     for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| {
  23135         op_src.* = switch (i) {
  23136             0 => .{ .node_offset_builtin_call_arg0 = src_node },
  23137             1 => .{ .node_offset_builtin_call_arg1 = src_node },
  23138             2 => .{ .node_offset_builtin_call_arg2 = src_node },
  23139             3 => .{ .node_offset_builtin_call_arg3 = src_node },
  23140             4 => .{ .node_offset_builtin_call_arg4 = src_node },
  23141             5 => .{ .node_offset_builtin_call_arg5 = src_node },
  23142             else => src, // TODO: better source location
  23143         };
  23144         air_ref.* = try sema.resolveInst(zir_ref);
  23145         try sema.checkNumericType(block, op_src.*, sema.typeOf(air_ref.*));
  23146     }
  23147 
  23148     return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs);
  23149 }
  23150 
  23151 fn analyzeMinMax(
  23152     sema: *Sema,
  23153     block: *Block,
  23154     src: LazySrcLoc,
  23155     comptime air_tag: Air.Inst.Tag,
  23156     operands: []const Air.Inst.Ref,
  23157     operand_srcs: []const LazySrcLoc,
  23158 ) CompileError!Air.Inst.Ref {
  23159     assert(operands.len == operand_srcs.len);
  23160     assert(operands.len > 0);
  23161     const mod = sema.mod;
  23162 
  23163     if (operands.len == 1) return operands[0];
  23164 
  23165     const opFunc = switch (air_tag) {
  23166         .min => Value.numberMin,
  23167         .max => Value.numberMax,
  23168         else => @compileError("unreachable"),
  23169     };
  23170 
  23171     // The set of runtime-known operands. Set up in the loop below.
  23172     var runtime_known = try std.DynamicBitSet.initFull(sema.arena, operands.len);
  23173     // The current minmax value - initially this will always be comptime-known, then we'll add
  23174     // runtime values into the mix later.
  23175     var cur_minmax: ?Air.Inst.Ref = null;
  23176     var cur_minmax_src: LazySrcLoc = undefined; // defined if cur_minmax not null
  23177     // The current known scalar bounds of the value.
  23178     var bounds_status: enum {
  23179         unknown, // We've only seen undef comptime_ints so far, so do not know the bounds.
  23180         defined, // We've seen only integers, so the bounds are defined.
  23181         non_integral, // There are floats in the mix, so the bounds aren't defined.
  23182     } = .unknown;
  23183     var cur_min_scalar: Value = undefined;
  23184     var cur_max_scalar: Value = undefined;
  23185 
  23186     // First, find all comptime-known arguments, and get their min/max
  23187 
  23188     for (operands, operand_srcs, 0..) |operand, operand_src, operand_idx| {
  23189         // Resolve the value now to avoid redundant calls to `checkSimdBinOp` - we'll have to call
  23190         // it in the runtime path anyway since the result type may have been refined
  23191         const unresolved_uncoerced_val = try sema.resolveMaybeUndefVal(operand) orelse continue;
  23192         const uncoerced_val = try sema.resolveLazyValue(unresolved_uncoerced_val);
  23193 
  23194         runtime_known.unset(operand_idx);
  23195 
  23196         switch (bounds_status) {
  23197             .unknown, .defined => refine_bounds: {
  23198                 const ty = sema.typeOf(operand);
  23199                 if (!ty.scalarType(mod).isInt(mod) and !ty.scalarType(mod).eql(Type.comptime_int, mod)) {
  23200                     bounds_status = .non_integral;
  23201                     break :refine_bounds;
  23202                 }
  23203                 const scalar_bounds: ?[2]Value = bounds: {
  23204                     if (!ty.isVector(mod)) break :bounds try uncoerced_val.intValueBounds(mod);
  23205                     var cur_bounds: [2]Value = try Value.intValueBounds(try uncoerced_val.elemValue(mod, 0), mod) orelse break :bounds null;
  23206                     const len = try sema.usizeCast(block, src, ty.vectorLen(mod));
  23207                     for (1..len) |i| {
  23208                         const elem = try uncoerced_val.elemValue(mod, i);
  23209                         const elem_bounds = try elem.intValueBounds(mod) orelse break :bounds null;
  23210                         cur_bounds = .{
  23211                             Value.numberMin(elem_bounds[0], cur_bounds[0], mod),
  23212                             Value.numberMax(elem_bounds[1], cur_bounds[1], mod),
  23213                         };
  23214                     }
  23215                     break :bounds cur_bounds;
  23216                 };
  23217                 if (scalar_bounds) |bounds| {
  23218                     if (bounds_status == .unknown) {
  23219                         cur_min_scalar = bounds[0];
  23220                         cur_max_scalar = bounds[1];
  23221                         bounds_status = .defined;
  23222                     } else {
  23223                         cur_min_scalar = opFunc(cur_min_scalar, bounds[0], mod);
  23224                         cur_max_scalar = opFunc(cur_max_scalar, bounds[1], mod);
  23225                     }
  23226                 }
  23227             },
  23228             .non_integral => {},
  23229         }
  23230 
  23231         const cur = cur_minmax orelse {
  23232             cur_minmax = operand;
  23233             cur_minmax_src = operand_src;
  23234             continue;
  23235         };
  23236 
  23237         const simd_op = try sema.checkSimdBinOp(block, src, cur, operand, cur_minmax_src, operand_src);
  23238         const cur_val = try sema.resolveLazyValue(simd_op.lhs_val.?); // cur_minmax is comptime-known
  23239         const operand_val = try sema.resolveLazyValue(simd_op.rhs_val.?); // we checked the operand was resolvable above
  23240 
  23241         const vec_len = simd_op.len orelse {
  23242             const result_val = opFunc(cur_val, operand_val, mod);
  23243             cur_minmax = try sema.addConstant(result_val);
  23244             continue;
  23245         };
  23246         const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  23247         for (elems, 0..) |*elem, i| {
  23248             const lhs_elem_val = try cur_val.elemValue(mod, i);
  23249             const rhs_elem_val = try operand_val.elemValue(mod, i);
  23250             const uncoerced_elem = opFunc(lhs_elem_val, rhs_elem_val, mod);
  23251             elem.* = (try mod.getCoerced(uncoerced_elem, simd_op.scalar_ty)).toIntern();
  23252         }
  23253         cur_minmax = try sema.addConstant((try mod.intern(.{ .aggregate = .{
  23254             .ty = simd_op.result_ty.toIntern(),
  23255             .storage = .{ .elems = elems },
  23256         } })).toValue());
  23257     }
  23258 
  23259     const opt_runtime_idx = runtime_known.findFirstSet();
  23260 
  23261     if (cur_minmax) |ct_minmax_ref| refine: {
  23262         // Refine the comptime-known result type based on the bounds. This isn't strictly necessary
  23263         // in the runtime case, since we'll refine the type again later, but keeping things as small
  23264         // as possible will allow us to emit more optimal AIR (if all the runtime operands have
  23265         // smaller types than the non-refined comptime type).
  23266 
  23267         const val = (try sema.resolveMaybeUndefVal(ct_minmax_ref)).?;
  23268         const orig_ty = sema.typeOf(ct_minmax_ref);
  23269 
  23270         if (opt_runtime_idx == null and orig_ty.scalarType(mod).eql(Type.comptime_int, mod)) {
  23271             // If all arguments were `comptime_int`, and there are no runtime args, we'll preserve that type
  23272             break :refine;
  23273         }
  23274 
  23275         // We can't refine float types
  23276         if (orig_ty.scalarType(mod).isAnyFloat()) break :refine;
  23277 
  23278         assert(bounds_status == .defined); // there was a non-comptime-int integral comptime-known arg
  23279 
  23280         const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar);
  23281         const refined_ty = if (orig_ty.isVector(mod)) try mod.vectorType(.{
  23282             .len = orig_ty.vectorLen(mod),
  23283             .child = refined_scalar_ty.toIntern(),
  23284         }) else refined_scalar_ty;
  23285 
  23286         // Apply the refined type to the current value
  23287         if (std.debug.runtime_safety) {
  23288             assert(try sema.intFitsInType(val, refined_ty, null));
  23289         }
  23290         cur_minmax = try sema.coerceInMemory(val, refined_ty);
  23291     }
  23292 
  23293     const runtime_idx = opt_runtime_idx orelse return cur_minmax.?;
  23294     const runtime_src = operand_srcs[runtime_idx];
  23295     try sema.requireRuntimeBlock(block, src, runtime_src);
  23296 
  23297     // Now, iterate over runtime operands, emitting a min/max instruction for each. We'll refine the
  23298     // type again at the end, based on the comptime-known bound.
  23299 
  23300     // If the comptime-known part is undef we can avoid emitting actual instructions later
  23301     const known_undef = if (cur_minmax) |operand| blk: {
  23302         const val = (try sema.resolveMaybeUndefVal(operand)).?;
  23303         break :blk val.isUndef(mod);
  23304     } else false;
  23305 
  23306     if (cur_minmax == null) {
  23307         // No comptime operands - use the first operand as the starting value
  23308         assert(bounds_status == .unknown);
  23309         assert(runtime_idx == 0);
  23310         cur_minmax = operands[0];
  23311         cur_minmax_src = runtime_src;
  23312         runtime_known.unset(0); // don't look at this operand in the loop below
  23313         const scalar_ty = sema.typeOf(cur_minmax.?).scalarType(mod);
  23314         if (scalar_ty.isInt(mod)) {
  23315             cur_min_scalar = try scalar_ty.minInt(mod, scalar_ty);
  23316             cur_max_scalar = try scalar_ty.maxInt(mod, scalar_ty);
  23317             bounds_status = .defined;
  23318         } else {
  23319             bounds_status = .non_integral;
  23320         }
  23321     }
  23322 
  23323     var it = runtime_known.iterator(.{});
  23324     while (it.next()) |idx| {
  23325         const lhs = cur_minmax.?;
  23326         const lhs_src = cur_minmax_src;
  23327         const rhs = operands[idx];
  23328         const rhs_src = operand_srcs[idx];
  23329         const simd_op = try sema.checkSimdBinOp(block, src, lhs, rhs, lhs_src, rhs_src);
  23330         if (known_undef) {
  23331             cur_minmax = try sema.addConstUndef(simd_op.result_ty);
  23332         } else {
  23333             cur_minmax = try block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs);
  23334         }
  23335         // Compute the bounds of this type
  23336         switch (bounds_status) {
  23337             .unknown, .defined => refine_bounds: {
  23338                 const scalar_ty = sema.typeOf(rhs).scalarType(mod);
  23339                 if (scalar_ty.isAnyFloat()) {
  23340                     bounds_status = .non_integral;
  23341                     break :refine_bounds;
  23342                 }
  23343                 const scalar_min = try scalar_ty.minInt(mod, scalar_ty);
  23344                 const scalar_max = try scalar_ty.maxInt(mod, scalar_ty);
  23345                 if (bounds_status == .unknown) {
  23346                     cur_min_scalar = scalar_min;
  23347                     cur_max_scalar = scalar_max;
  23348                     bounds_status = .defined;
  23349                 } else {
  23350                     cur_min_scalar = opFunc(cur_min_scalar, scalar_min, mod);
  23351                     cur_max_scalar = opFunc(cur_max_scalar, scalar_max, mod);
  23352                 }
  23353             },
  23354             .non_integral => {},
  23355         }
  23356     }
  23357 
  23358     // Finally, refine the type based on the known bounds.
  23359     const unrefined_ty = sema.typeOf(cur_minmax.?);
  23360     if (unrefined_ty.scalarType(mod).isAnyFloat()) {
  23361         // We can't refine floats, so we're done.
  23362         return cur_minmax.?;
  23363     }
  23364     assert(bounds_status == .defined); // there were integral runtime operands
  23365     const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar);
  23366     const refined_ty = if (unrefined_ty.isVector(mod)) try mod.vectorType(.{
  23367         .len = unrefined_ty.vectorLen(mod),
  23368         .child = refined_scalar_ty.toIntern(),
  23369     }) else refined_scalar_ty;
  23370 
  23371     if (!refined_ty.eql(unrefined_ty, mod)) {
  23372         // We've reduced the type - cast the result down
  23373         return block.addTyOp(.intcast, refined_ty, cur_minmax.?);
  23374     }
  23375 
  23376     return cur_minmax.?;
  23377 }
  23378 
  23379 fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref {
  23380     const mod = sema.mod;
  23381     const info = sema.typeOf(ptr).ptrInfo(mod);
  23382     if (info.flags.size == .One) {
  23383         // Already an array pointer.
  23384         return ptr;
  23385     }
  23386     const new_ty = try mod.ptrType(.{
  23387         .child = (try mod.arrayType(.{
  23388             .len = len,
  23389             .sentinel = info.sentinel,
  23390             .child = info.child,
  23391         })).toIntern(),
  23392         .flags = .{
  23393             .alignment = info.flags.alignment,
  23394             .is_const = info.flags.is_const,
  23395             .is_volatile = info.flags.is_volatile,
  23396             .is_allowzero = info.flags.is_allowzero,
  23397             .address_space = info.flags.address_space,
  23398         },
  23399     });
  23400     if (info.flags.size == .Slice) {
  23401         return block.addTyOp(.slice_ptr, new_ty, ptr);
  23402     }
  23403     return block.addBitCast(new_ty, ptr);
  23404 }
  23405 
  23406 fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  23407     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  23408     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23409     const src = inst_data.src();
  23410     const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23411     const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23412     const dest_ptr = try sema.resolveInst(extra.lhs);
  23413     const src_ptr = try sema.resolveInst(extra.rhs);
  23414     const dest_ty = sema.typeOf(dest_ptr);
  23415     const src_ty = sema.typeOf(src_ptr);
  23416     const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr);
  23417     const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr);
  23418     const target = sema.mod.getTarget();
  23419     const mod = sema.mod;
  23420 
  23421     if (dest_ty.isConstPtr(mod)) {
  23422         return sema.fail(block, dest_src, "cannot memcpy to constant pointer", .{});
  23423     }
  23424 
  23425     if (dest_len == .none and src_len == .none) {
  23426         const msg = msg: {
  23427             const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{});
  23428             errdefer msg.destroy(sema.gpa);
  23429             try sema.errNote(block, dest_src, msg, "destination type '{}' provides no length", .{
  23430                 dest_ty.fmt(sema.mod),
  23431             });
  23432             try sema.errNote(block, src_src, msg, "source type '{}' provides no length", .{
  23433                 src_ty.fmt(sema.mod),
  23434             });
  23435             break :msg msg;
  23436         };
  23437         return sema.failWithOwnedErrorMsg(msg);
  23438     }
  23439 
  23440     var len_val: ?Value = null;
  23441 
  23442     if (dest_len != .none and src_len != .none) check: {
  23443         // If we can check at compile-time, no need for runtime safety.
  23444         if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
  23445             len_val = dest_len_val;
  23446             if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  23447                 if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) {
  23448                     const msg = msg: {
  23449                         const msg = try sema.errMsg(block, src, "non-matching @memcpy lengths", .{});
  23450                         errdefer msg.destroy(sema.gpa);
  23451                         try sema.errNote(block, dest_src, msg, "length {} here", .{
  23452                             dest_len_val.fmtValue(Type.usize, sema.mod),
  23453                         });
  23454                         try sema.errNote(block, src_src, msg, "length {} here", .{
  23455                             src_len_val.fmtValue(Type.usize, sema.mod),
  23456                         });
  23457                         break :msg msg;
  23458                     };
  23459                     return sema.failWithOwnedErrorMsg(msg);
  23460                 }
  23461                 break :check;
  23462             }
  23463         } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  23464             len_val = src_len_val;
  23465         }
  23466 
  23467         if (block.wantSafety()) {
  23468             const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);
  23469             try sema.addSafetyCheck(block, ok, .memcpy_len_mismatch);
  23470         }
  23471     } else if (dest_len != .none) {
  23472         if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
  23473             len_val = dest_len_val;
  23474         }
  23475     } else if (src_len != .none) {
  23476         if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  23477             len_val = src_len_val;
  23478         }
  23479     }
  23480 
  23481     const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: {
  23482         if (!dest_ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src;
  23483         if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |_| {
  23484             const len_u64 = (try len_val.?.getUnsignedIntAdvanced(mod, sema)).?;
  23485             const len = try sema.usizeCast(block, dest_src, len_u64);
  23486             for (0..len) |i| {
  23487                 const elem_index = try sema.addIntUnsigned(Type.usize, i);
  23488                 const dest_elem_ptr = try sema.elemPtrOneLayerOnly(
  23489                     block,
  23490                     src,
  23491                     dest_ptr,
  23492                     elem_index,
  23493                     src,
  23494                     true, // init
  23495                     false, // oob_safety
  23496                 );
  23497                 const src_elem_ptr = try sema.elemPtrOneLayerOnly(
  23498                     block,
  23499                     src,
  23500                     src_ptr,
  23501                     elem_index,
  23502                     src,
  23503                     false, // init
  23504                     false, // oob_safety
  23505                 );
  23506                 const uncoerced_elem = try sema.analyzeLoad(block, src, src_elem_ptr, src_src);
  23507                 try sema.storePtr2(
  23508                     block,
  23509                     src,
  23510                     dest_elem_ptr,
  23511                     dest_src,
  23512                     uncoerced_elem,
  23513                     src_src,
  23514                     .store,
  23515                 );
  23516             }
  23517             return;
  23518         } else break :rs src_src;
  23519     } else dest_src;
  23520 
  23521     // If in-memory coercion is not allowed, explode this memcpy call into a
  23522     // for loop that copies element-wise.
  23523     // Likewise if this is an iterable rather than a pointer, do the same
  23524     // lowering. The AIR instruction requires pointers with element types of
  23525     // equal ABI size.
  23526 
  23527     if (dest_ty.zigTypeTag(mod) != .Pointer or src_ty.zigTypeTag(mod) != .Pointer) {
  23528         return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{});
  23529     }
  23530 
  23531     const dest_elem_ty = dest_ty.elemType2(mod);
  23532     const src_elem_ty = src_ty.elemType2(mod);
  23533     if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) {
  23534         return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{});
  23535     }
  23536 
  23537     // If the length is comptime-known, then upgrade src and destination types
  23538     // into pointer-to-array. At this point we know they are both pointers
  23539     // already.
  23540     var new_dest_ptr = dest_ptr;
  23541     var new_src_ptr = src_ptr;
  23542     if (len_val) |val| {
  23543         const len = val.toUnsignedInt(mod);
  23544         if (len == 0) {
  23545             // This AIR instruction guarantees length > 0 if it is comptime-known.
  23546             return;
  23547         }
  23548         new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len);
  23549         new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);
  23550     }
  23551 
  23552     if (dest_len != .none) {
  23553         // Change the src from slice to a many pointer, to avoid multiple ptr
  23554         // slice extractions in AIR instructions.
  23555         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  23556         if (new_src_ptr_ty.isSlice(mod)) {
  23557             new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
  23558         }
  23559     } else if (dest_len == .none and len_val == null) {
  23560         // Change the dest to a slice, since its type must have the length.
  23561         const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr);
  23562         new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, .unneeded, dest_src, dest_src, dest_src, false);
  23563         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  23564         if (new_src_ptr_ty.isSlice(mod)) {
  23565             new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
  23566         }
  23567     }
  23568 
  23569     try sema.requireRuntimeBlock(block, src, runtime_src);
  23570 
  23571     // Aliasing safety check.
  23572     if (block.wantSafety()) {
  23573         const len = if (len_val) |v|
  23574             try sema.addConstant(v)
  23575         else if (dest_len != .none)
  23576             dest_len
  23577         else
  23578             src_len;
  23579 
  23580         // Extract raw pointer from dest slice. The AIR instructions could support them, but
  23581         // it would cause redundant machine code instructions.
  23582         const new_dest_ptr_ty = sema.typeOf(new_dest_ptr);
  23583         const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(mod))
  23584             try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty)
  23585         else if (new_dest_ptr_ty.ptrSize(mod) == .One) ptr: {
  23586             var dest_manyptr_ty_key = mod.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type;
  23587             assert(dest_manyptr_ty_key.flags.size == .One);
  23588             dest_manyptr_ty_key.child = dest_elem_ty.toIntern();
  23589             dest_manyptr_ty_key.flags.size = .Many;
  23590             break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(dest_manyptr_ty_key), new_dest_ptr, dest_src);
  23591         } else new_dest_ptr;
  23592 
  23593         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  23594         const raw_src_ptr = if (new_src_ptr_ty.isSlice(mod))
  23595             try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty)
  23596         else if (new_src_ptr_ty.ptrSize(mod) == .One) ptr: {
  23597             var src_manyptr_ty_key = mod.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type;
  23598             assert(src_manyptr_ty_key.flags.size == .One);
  23599             src_manyptr_ty_key.child = src_elem_ty.toIntern();
  23600             src_manyptr_ty_key.flags.size = .Many;
  23601             break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(src_manyptr_ty_key), new_src_ptr, src_src);
  23602         } else new_src_ptr;
  23603 
  23604         // ok1: dest >= src + len
  23605         // ok2: src >= dest + len
  23606         const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src);
  23607         const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src);
  23608         const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
  23609         const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
  23610         const ok = try block.addBinOp(.bit_or, ok1, ok2);
  23611         try sema.addSafetyCheck(block, ok, .memcpy_alias);
  23612     }
  23613 
  23614     _ = try block.addInst(.{
  23615         .tag = .memcpy,
  23616         .data = .{ .bin_op = .{
  23617             .lhs = new_dest_ptr,
  23618             .rhs = new_src_ptr,
  23619         } },
  23620     });
  23621 }
  23622 
  23623 fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  23624     const mod = sema.mod;
  23625     const gpa = sema.gpa;
  23626     const ip = &mod.intern_pool;
  23627     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  23628     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23629     const src = inst_data.src();
  23630     const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
  23631     const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
  23632     const dest_ptr = try sema.resolveInst(extra.lhs);
  23633     const uncoerced_elem = try sema.resolveInst(extra.rhs);
  23634     const dest_ptr_ty = sema.typeOf(dest_ptr);
  23635     try checkMemOperand(sema, block, dest_src, dest_ptr_ty);
  23636 
  23637     if (dest_ptr_ty.isConstPtr(mod)) {
  23638         return sema.fail(block, dest_src, "cannot memset constant pointer", .{});
  23639     }
  23640 
  23641     const dest_elem_ty = dest_ptr_ty.elemType2(mod);
  23642 
  23643     const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: {
  23644         const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, "len"), dest_src);
  23645         const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse
  23646             break :rs dest_src;
  23647         const len_u64 = (try len_val.getUnsignedIntAdvanced(mod, sema)).?;
  23648         const len = try sema.usizeCast(block, dest_src, len_u64);
  23649         if (len == 0) {
  23650             // This AIR instruction guarantees length > 0 if it is comptime-known.
  23651             return;
  23652         }
  23653 
  23654         if (!ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src;
  23655         if (try sema.resolveMaybeUndefVal(uncoerced_elem)) |_| {
  23656             for (0..len) |i| {
  23657                 const elem_index = try sema.addIntUnsigned(Type.usize, i);
  23658                 const elem_ptr = try sema.elemPtrOneLayerOnly(
  23659                     block,
  23660                     src,
  23661                     dest_ptr,
  23662                     elem_index,
  23663                     src,
  23664                     true, // init
  23665                     false, // oob_safety
  23666                 );
  23667                 try sema.storePtr2(
  23668                     block,
  23669                     src,
  23670                     elem_ptr,
  23671                     dest_src,
  23672                     uncoerced_elem,
  23673                     value_src,
  23674                     .store,
  23675                 );
  23676             }
  23677             return;
  23678         } else break :rs value_src;
  23679     } else dest_src;
  23680 
  23681     const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src);
  23682 
  23683     try sema.requireRuntimeBlock(block, src, runtime_src);
  23684     _ = try block.addInst(.{
  23685         .tag = if (block.wantSafety()) .memset_safe else .memset,
  23686         .data = .{ .bin_op = .{
  23687             .lhs = dest_ptr,
  23688             .rhs = elem,
  23689         } },
  23690     });
  23691 }
  23692 
  23693 fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  23694     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  23695     const src = LazySrcLoc.nodeOffset(extra.node);
  23696     return sema.failWithUseOfAsync(block, src);
  23697 }
  23698 
  23699 fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23700     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  23701     const src = inst_data.src();
  23702     return sema.failWithUseOfAsync(block, src);
  23703 }
  23704 
  23705 fn zirAwait(
  23706     sema: *Sema,
  23707     block: *Block,
  23708     inst: Zir.Inst.Index,
  23709 ) CompileError!Air.Inst.Ref {
  23710     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
  23711     const src = inst_data.src();
  23712 
  23713     return sema.failWithUseOfAsync(block, src);
  23714 }
  23715 
  23716 fn zirAwaitNosuspend(
  23717     sema: *Sema,
  23718     block: *Block,
  23719     extended: Zir.Inst.Extended.InstData,
  23720 ) CompileError!Air.Inst.Ref {
  23721     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  23722     const src = LazySrcLoc.nodeOffset(extra.node);
  23723 
  23724     return sema.failWithUseOfAsync(block, src);
  23725 }
  23726 
  23727 fn zirVarExtended(
  23728     sema: *Sema,
  23729     block: *Block,
  23730     extended: Zir.Inst.Extended.InstData,
  23731 ) CompileError!Air.Inst.Ref {
  23732     const mod = sema.mod;
  23733     const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
  23734     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 };
  23735     const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 };
  23736     const small = @as(Zir.Inst.ExtendedVar.Small, @bitCast(extended.small));
  23737 
  23738     var extra_index: usize = extra.end;
  23739 
  23740     const lib_name: ?[]const u8 = if (small.has_lib_name) blk: {
  23741         const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
  23742         extra_index += 1;
  23743         break :blk lib_name;
  23744     } else null;
  23745 
  23746     // ZIR supports encoding this information but it is not used; the information
  23747     // is encoded via the Decl entry.
  23748     assert(!small.has_align);
  23749 
  23750     const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: {
  23751         const init_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23752         extra_index += 1;
  23753         break :blk try sema.resolveInst(init_ref);
  23754     } else .none;
  23755 
  23756     const have_ty = extra.data.var_type != .none;
  23757     const var_ty = if (have_ty)
  23758         try sema.resolveType(block, ty_src, extra.data.var_type)
  23759     else
  23760         sema.typeOf(uncasted_init);
  23761 
  23762     const init_val = if (uncasted_init != .none) blk: {
  23763         const init = if (have_ty)
  23764             try sema.coerce(block, var_ty, uncasted_init, init_src)
  23765         else
  23766             uncasted_init;
  23767 
  23768         break :blk ((try sema.resolveMaybeUndefVal(init)) orelse
  23769             return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime-known")).toIntern();
  23770     } else .none;
  23771 
  23772     try sema.validateVarType(block, ty_src, var_ty, small.is_extern);
  23773 
  23774     return sema.addConstant((try mod.intern(.{ .variable = .{
  23775         .ty = var_ty.toIntern(),
  23776         .init = init_val,
  23777         .decl = sema.owner_decl_index,
  23778         .lib_name = if (lib_name) |lname| (try mod.intern_pool.getOrPutString(
  23779             sema.gpa,
  23780             try sema.handleExternLibName(block, ty_src, lname),
  23781         )).toOptional() else .none,
  23782         .is_extern = small.is_extern,
  23783         .is_threadlocal = small.is_threadlocal,
  23784     } })).toValue());
  23785 }
  23786 
  23787 fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23788     const tracy = trace(@src());
  23789     defer tracy.end();
  23790 
  23791     const mod = sema.mod;
  23792     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
  23793     const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
  23794     const target = mod.getTarget();
  23795 
  23796     const align_src: LazySrcLoc = .{ .node_offset_fn_type_align = inst_data.src_node };
  23797     const addrspace_src: LazySrcLoc = .{ .node_offset_fn_type_addrspace = inst_data.src_node };
  23798     const section_src: LazySrcLoc = .{ .node_offset_fn_type_section = inst_data.src_node };
  23799     const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node };
  23800     const ret_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node };
  23801     const has_body = extra.data.body_len != 0;
  23802 
  23803     var extra_index: usize = extra.end;
  23804 
  23805     const lib_name: ?[]const u8 = if (extra.data.bits.has_lib_name) blk: {
  23806         const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
  23807         extra_index += 1;
  23808         break :blk lib_name;
  23809     } else null;
  23810 
  23811     if (has_body and
  23812         (extra.data.bits.has_align_body or extra.data.bits.has_align_ref) and
  23813         !target_util.supportsFunctionAlignment(target))
  23814     {
  23815         return sema.fail(block, align_src, "target does not support function alignment", .{});
  23816     }
  23817 
  23818     const @"align": ?Alignment = if (extra.data.bits.has_align_body) blk: {
  23819         const body_len = sema.code.extra[extra_index];
  23820         extra_index += 1;
  23821         const body = sema.code.extra[extra_index..][0..body_len];
  23822         extra_index += body.len;
  23823 
  23824         const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29, "alignment must be comptime-known");
  23825         if (val.isGenericPoison()) {
  23826             break :blk null;
  23827         }
  23828         const alignment = @as(u32, @intCast(val.toUnsignedInt(mod)));
  23829         try sema.validateAlign(block, align_src, alignment);
  23830         if (alignment == target_util.defaultFunctionAlignment(target)) {
  23831             break :blk .none;
  23832         } else {
  23833             break :blk Alignment.fromNonzeroByteUnits(alignment);
  23834         }
  23835     } else if (extra.data.bits.has_align_ref) blk: {
  23836         const align_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23837         extra_index += 1;
  23838         const align_tv = sema.resolveInstConst(block, align_src, align_ref, "alignment must be comptime-known") catch |err| switch (err) {
  23839             error.GenericPoison => {
  23840                 break :blk null;
  23841             },
  23842             else => |e| return e,
  23843         };
  23844         const alignment = @as(u32, @intCast(align_tv.val.toUnsignedInt(mod)));
  23845         try sema.validateAlign(block, align_src, alignment);
  23846         if (alignment == target_util.defaultFunctionAlignment(target)) {
  23847             break :blk .none;
  23848         } else {
  23849             break :blk Alignment.fromNonzeroByteUnits(alignment);
  23850         }
  23851     } else .none;
  23852 
  23853     const @"addrspace": ?std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: {
  23854         const body_len = sema.code.extra[extra_index];
  23855         extra_index += 1;
  23856         const body = sema.code.extra[extra_index..][0..body_len];
  23857         extra_index += body.len;
  23858 
  23859         const addrspace_ty = try sema.getBuiltinType("AddressSpace");
  23860         const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty, "addrespace must be comptime-known");
  23861         if (val.isGenericPoison()) {
  23862             break :blk null;
  23863         }
  23864         break :blk mod.toEnum(std.builtin.AddressSpace, val);
  23865     } else if (extra.data.bits.has_addrspace_ref) blk: {
  23866         const addrspace_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23867         extra_index += 1;
  23868         const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref, "addrespace must be comptime-known") catch |err| switch (err) {
  23869             error.GenericPoison => {
  23870                 break :blk null;
  23871             },
  23872             else => |e| return e,
  23873         };
  23874         break :blk mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val);
  23875     } else target_util.defaultAddressSpace(target, .function);
  23876 
  23877     const @"linksection": FuncLinkSection = if (extra.data.bits.has_section_body) blk: {
  23878         const body_len = sema.code.extra[extra_index];
  23879         extra_index += 1;
  23880         const body = sema.code.extra[extra_index..][0..body_len];
  23881         extra_index += body.len;
  23882 
  23883         const ty = Type.slice_const_u8;
  23884         const val = try sema.resolveGenericBody(block, section_src, body, inst, ty, "linksection must be comptime-known");
  23885         if (val.isGenericPoison()) {
  23886             break :blk FuncLinkSection{ .generic = {} };
  23887         }
  23888         break :blk FuncLinkSection{ .explicit = try val.toIpString(ty, mod) };
  23889     } else if (extra.data.bits.has_section_ref) blk: {
  23890         const section_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23891         extra_index += 1;
  23892         const section_name = sema.resolveConstStringIntern(block, section_src, section_ref, "linksection must be comptime-known") catch |err| switch (err) {
  23893             error.GenericPoison => {
  23894                 break :blk FuncLinkSection{ .generic = {} };
  23895             },
  23896             else => |e| return e,
  23897         };
  23898         break :blk FuncLinkSection{ .explicit = section_name };
  23899     } else FuncLinkSection{ .default = {} };
  23900 
  23901     const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
  23902         const body_len = sema.code.extra[extra_index];
  23903         extra_index += 1;
  23904         const body = sema.code.extra[extra_index..][0..body_len];
  23905         extra_index += body.len;
  23906 
  23907         const cc_ty = try sema.getBuiltinType("CallingConvention");
  23908         const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, "calling convention must be comptime-known");
  23909         if (val.isGenericPoison()) {
  23910             break :blk null;
  23911         }
  23912         break :blk mod.toEnum(std.builtin.CallingConvention, val);
  23913     } else if (extra.data.bits.has_cc_ref) blk: {
  23914         const cc_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23915         extra_index += 1;
  23916         const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref, "calling convention must be comptime-known") catch |err| switch (err) {
  23917             error.GenericPoison => {
  23918                 break :blk null;
  23919             },
  23920             else => |e| return e,
  23921         };
  23922         break :blk mod.toEnum(std.builtin.CallingConvention, cc_tv.val);
  23923     } else if (sema.owner_decl.is_exported and has_body)
  23924         .C
  23925     else
  23926         .Unspecified;
  23927 
  23928     const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
  23929         const body_len = sema.code.extra[extra_index];
  23930         extra_index += 1;
  23931         const body = sema.code.extra[extra_index..][0..body_len];
  23932         extra_index += body.len;
  23933 
  23934         const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type, "return type must be comptime-known");
  23935         const ty = val.toType();
  23936         break :blk ty;
  23937     } else if (extra.data.bits.has_ret_ty_ref) blk: {
  23938         const ret_ty_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index]));
  23939         extra_index += 1;
  23940         const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref, "return type must be comptime-known") catch |err| switch (err) {
  23941             error.GenericPoison => {
  23942                 break :blk Type.generic_poison;
  23943             },
  23944             else => |e| return e,
  23945         };
  23946         const ty = ret_ty_tv.val.toType();
  23947         break :blk ty;
  23948     } else Type.void;
  23949 
  23950     const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: {
  23951         const x = sema.code.extra[extra_index];
  23952         extra_index += 1;
  23953         break :blk x;
  23954     } else 0;
  23955 
  23956     var src_locs: Zir.Inst.Func.SrcLocs = undefined;
  23957     if (has_body) {
  23958         extra_index += extra.data.body_len;
  23959         src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
  23960     }
  23961 
  23962     const is_var_args = extra.data.bits.is_var_args;
  23963     const is_inferred_error = extra.data.bits.is_inferred_error;
  23964     const is_extern = extra.data.bits.is_extern;
  23965     const is_noinline = extra.data.bits.is_noinline;
  23966 
  23967     return sema.funcCommon(
  23968         block,
  23969         inst_data.src_node,
  23970         inst,
  23971         @"align",
  23972         @"addrspace",
  23973         @"linksection",
  23974         cc,
  23975         ret_ty,
  23976         is_var_args,
  23977         is_inferred_error,
  23978         is_extern,
  23979         has_body,
  23980         src_locs,
  23981         lib_name,
  23982         noalias_bits,
  23983         is_noinline,
  23984     );
  23985 }
  23986 
  23987 fn zirCUndef(
  23988     sema: *Sema,
  23989     block: *Block,
  23990     extended: Zir.Inst.Extended.InstData,
  23991 ) CompileError!Air.Inst.Ref {
  23992     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  23993     const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  23994 
  23995     const name = try sema.resolveConstString(block, src, extra.operand, "name of macro being undefined must be comptime-known");
  23996     try block.c_import_buf.?.writer().print("#undef {s}\n", .{name});
  23997     return Air.Inst.Ref.void_value;
  23998 }
  23999 
  24000 fn zirCInclude(
  24001     sema: *Sema,
  24002     block: *Block,
  24003     extended: Zir.Inst.Extended.InstData,
  24004 ) CompileError!Air.Inst.Ref {
  24005     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  24006     const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24007 
  24008     const name = try sema.resolveConstString(block, src, extra.operand, "path being included must be comptime-known");
  24009     try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name});
  24010     return Air.Inst.Ref.void_value;
  24011 }
  24012 
  24013 fn zirCDefine(
  24014     sema: *Sema,
  24015     block: *Block,
  24016     extended: Zir.Inst.Extended.InstData,
  24017 ) CompileError!Air.Inst.Ref {
  24018     const mod = sema.mod;
  24019     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  24020     const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24021     const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  24022 
  24023     const name = try sema.resolveConstString(block, name_src, extra.lhs, "name of macro being undefined must be comptime-known");
  24024     const rhs = try sema.resolveInst(extra.rhs);
  24025     if (sema.typeOf(rhs).zigTypeTag(mod) != .Void) {
  24026         const value = try sema.resolveConstString(block, val_src, extra.rhs, "value of macro being undefined must be comptime-known");
  24027         try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value });
  24028     } else {
  24029         try block.c_import_buf.?.writer().print("#define {s}\n", .{name});
  24030     }
  24031     return Air.Inst.Ref.void_value;
  24032 }
  24033 
  24034 fn zirWasmMemorySize(
  24035     sema: *Sema,
  24036     block: *Block,
  24037     extended: Zir.Inst.Extended.InstData,
  24038 ) CompileError!Air.Inst.Ref {
  24039     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  24040     const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24041     const builtin_src = LazySrcLoc.nodeOffset(extra.node);
  24042     const target = sema.mod.getTarget();
  24043     if (!target.isWasm()) {
  24044         return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
  24045     }
  24046 
  24047     const index = @as(u32, @intCast(try sema.resolveInt(block, index_src, extra.operand, Type.u32, "wasm memory size index must be comptime-known")));
  24048     try sema.requireRuntimeBlock(block, builtin_src, null);
  24049     return block.addInst(.{
  24050         .tag = .wasm_memory_size,
  24051         .data = .{ .pl_op = .{
  24052             .operand = .none,
  24053             .payload = index,
  24054         } },
  24055     });
  24056 }
  24057 
  24058 fn zirWasmMemoryGrow(
  24059     sema: *Sema,
  24060     block: *Block,
  24061     extended: Zir.Inst.Extended.InstData,
  24062 ) CompileError!Air.Inst.Ref {
  24063     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  24064     const builtin_src = LazySrcLoc.nodeOffset(extra.node);
  24065     const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24066     const delta_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  24067     const target = sema.mod.getTarget();
  24068     if (!target.isWasm()) {
  24069         return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
  24070     }
  24071 
  24072     const index = @as(u32, @intCast(try sema.resolveInt(block, index_src, extra.lhs, Type.u32, "wasm memory size index must be comptime-known")));
  24073     const delta = try sema.coerce(block, Type.u32, try sema.resolveInst(extra.rhs), delta_src);
  24074 
  24075     try sema.requireRuntimeBlock(block, builtin_src, null);
  24076     return block.addInst(.{
  24077         .tag = .wasm_memory_grow,
  24078         .data = .{ .pl_op = .{
  24079             .operand = delta,
  24080             .payload = index,
  24081         } },
  24082     });
  24083 }
  24084 
  24085 fn resolvePrefetchOptions(
  24086     sema: *Sema,
  24087     block: *Block,
  24088     src: LazySrcLoc,
  24089     zir_ref: Zir.Inst.Ref,
  24090 ) CompileError!std.builtin.PrefetchOptions {
  24091     const mod = sema.mod;
  24092     const gpa = sema.gpa;
  24093     const ip = &mod.intern_pool;
  24094     const options_ty = try sema.getBuiltinType("PrefetchOptions");
  24095     const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src);
  24096 
  24097     const rw_src = sema.maybeOptionsSrc(block, src, "rw");
  24098     const locality_src = sema.maybeOptionsSrc(block, src, "locality");
  24099     const cache_src = sema.maybeOptionsSrc(block, src, "cache");
  24100 
  24101     const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "rw"), rw_src);
  24102     const rw_val = try sema.resolveConstValue(block, rw_src, rw, "prefetch read/write must be comptime-known");
  24103 
  24104     const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "locality"), locality_src);
  24105     const locality_val = try sema.resolveConstValue(block, locality_src, locality, "prefetch locality must be comptime-known");
  24106 
  24107     const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "cache"), cache_src);
  24108     const cache_val = try sema.resolveConstValue(block, cache_src, cache, "prefetch cache must be comptime-known");
  24109 
  24110     return std.builtin.PrefetchOptions{
  24111         .rw = mod.toEnum(std.builtin.PrefetchOptions.Rw, rw_val),
  24112         .locality = @as(u2, @intCast(locality_val.toUnsignedInt(mod))),
  24113         .cache = mod.toEnum(std.builtin.PrefetchOptions.Cache, cache_val),
  24114     };
  24115 }
  24116 
  24117 fn zirPrefetch(
  24118     sema: *Sema,
  24119     block: *Block,
  24120     extended: Zir.Inst.Extended.InstData,
  24121 ) CompileError!Air.Inst.Ref {
  24122     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  24123     const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24124     const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  24125     const ptr = try sema.resolveInst(extra.lhs);
  24126     try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr));
  24127 
  24128     const options = sema.resolvePrefetchOptions(block, .unneeded, extra.rhs) catch |err| switch (err) {
  24129         error.NeededSourceLocation => {
  24130             _ = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs);
  24131             unreachable;
  24132         },
  24133         else => |e| return e,
  24134     };
  24135 
  24136     if (!block.is_comptime) {
  24137         _ = try block.addInst(.{
  24138             .tag = .prefetch,
  24139             .data = .{ .prefetch = .{
  24140                 .ptr = ptr,
  24141                 .rw = options.rw,
  24142                 .locality = options.locality,
  24143                 .cache = options.cache,
  24144             } },
  24145         });
  24146     }
  24147 
  24148     return Air.Inst.Ref.void_value;
  24149 }
  24150 
  24151 fn resolveExternOptions(
  24152     sema: *Sema,
  24153     block: *Block,
  24154     src: LazySrcLoc,
  24155     zir_ref: Zir.Inst.Ref,
  24156 ) CompileError!struct {
  24157     name: InternPool.NullTerminatedString,
  24158     library_name: InternPool.OptionalNullTerminatedString = .none,
  24159     linkage: std.builtin.GlobalLinkage = .Strong,
  24160     is_thread_local: bool = false,
  24161 } {
  24162     const mod = sema.mod;
  24163     const gpa = sema.gpa;
  24164     const ip = &mod.intern_pool;
  24165     const options_inst = try sema.resolveInst(zir_ref);
  24166     const extern_options_ty = try sema.getBuiltinType("ExternOptions");
  24167     const options = try sema.coerce(block, extern_options_ty, options_inst, src);
  24168 
  24169     const name_src = sema.maybeOptionsSrc(block, src, "name");
  24170     const library_src = sema.maybeOptionsSrc(block, src, "library");
  24171     const linkage_src = sema.maybeOptionsSrc(block, src, "linkage");
  24172     const thread_local_src = sema.maybeOptionsSrc(block, src, "thread_local");
  24173 
  24174     const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src);
  24175     const name_val = try sema.resolveConstValue(block, name_src, name_ref, "name of the extern symbol must be comptime-known");
  24176     const name = try name_val.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod);
  24177 
  24178     const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "library_name"), library_src);
  24179     const library_name_val = try sema.resolveConstValue(block, library_src, library_name_inst, "library in which extern symbol is must be comptime-known");
  24180 
  24181     const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src);
  24182     const linkage_val = try sema.resolveConstValue(block, linkage_src, linkage_ref, "linkage of the extern symbol must be comptime-known");
  24183     const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val);
  24184 
  24185     const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "is_thread_local"), thread_local_src);
  24186     const is_thread_local_val = try sema.resolveConstValue(block, thread_local_src, is_thread_local, "threadlocality of the extern symbol must be comptime-known");
  24187 
  24188     const library_name = if (library_name_val.optionalValue(mod)) |payload| blk: {
  24189         const library_name = try payload.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod);
  24190         if (library_name.len == 0) {
  24191             return sema.fail(block, library_src, "library name cannot be empty", .{});
  24192         }
  24193         break :blk try sema.handleExternLibName(block, library_src, library_name);
  24194     } else null;
  24195 
  24196     if (name.len == 0) {
  24197         return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
  24198     }
  24199 
  24200     if (linkage != .Weak and linkage != .Strong) {
  24201         return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{});
  24202     }
  24203 
  24204     return .{
  24205         .name = try ip.getOrPutString(gpa, name),
  24206         .library_name = try ip.getOrPutStringOpt(gpa, library_name),
  24207         .linkage = linkage,
  24208         .is_thread_local = is_thread_local_val.toBool(),
  24209     };
  24210 }
  24211 
  24212 fn zirBuiltinExtern(
  24213     sema: *Sema,
  24214     block: *Block,
  24215     extended: Zir.Inst.Extended.InstData,
  24216 ) CompileError!Air.Inst.Ref {
  24217     const mod = sema.mod;
  24218     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  24219     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24220     const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
  24221 
  24222     var ty = try sema.resolveType(block, ty_src, extra.lhs);
  24223     if (!ty.isPtrAtRuntime(mod)) {
  24224         return sema.fail(block, ty_src, "expected (optional) pointer", .{});
  24225     }
  24226     if (!try sema.validateExternType(ty.childType(mod), .other)) {
  24227         const msg = msg: {
  24228             const msg = try sema.errMsg(block, ty_src, "extern symbol cannot have type '{}'", .{ty.fmt(mod)});
  24229             errdefer msg.destroy(sema.gpa);
  24230             const src_decl = sema.mod.declPtr(block.src_decl);
  24231             try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), ty, .other);
  24232             break :msg msg;
  24233         };
  24234         return sema.failWithOwnedErrorMsg(msg);
  24235     }
  24236 
  24237     const options = sema.resolveExternOptions(block, .unneeded, extra.rhs) catch |err| switch (err) {
  24238         error.NeededSourceLocation => {
  24239             _ = try sema.resolveExternOptions(block, options_src, extra.rhs);
  24240             unreachable;
  24241         },
  24242         else => |e| return e,
  24243     };
  24244 
  24245     if (options.linkage == .Weak and !ty.ptrAllowsZero(mod)) {
  24246         ty = try mod.optionalType(ty.toIntern());
  24247     }
  24248 
  24249     // TODO check duplicate extern
  24250 
  24251     const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null);
  24252     errdefer mod.destroyDecl(new_decl_index);
  24253     const new_decl = mod.declPtr(new_decl_index);
  24254     new_decl.name = options.name;
  24255 
  24256     {
  24257         const new_var = try mod.intern(.{ .variable = .{
  24258             .ty = ty.toIntern(),
  24259             .init = .none,
  24260             .decl = sema.owner_decl_index,
  24261             .is_extern = true,
  24262             .is_const = true,
  24263             .is_threadlocal = options.is_thread_local,
  24264             .is_weak_linkage = options.linkage == .Weak,
  24265         } });
  24266 
  24267         new_decl.src_line = sema.owner_decl.src_line;
  24268         // We only access this decl through the decl_ref with the correct type created
  24269         // below, so this type doesn't matter
  24270         new_decl.ty = ty;
  24271         new_decl.val = new_var.toValue();
  24272         new_decl.alignment = .none;
  24273         new_decl.@"linksection" = .none;
  24274         new_decl.has_tv = true;
  24275         new_decl.analysis = .complete;
  24276         new_decl.generation = mod.generation;
  24277     }
  24278 
  24279     try mod.declareDeclDependency(sema.owner_decl_index, new_decl_index);
  24280     try sema.ensureDeclAnalyzed(new_decl_index);
  24281 
  24282     return sema.addConstant(try mod.getCoerced((try mod.intern(.{ .ptr = .{
  24283         .ty = switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  24284             .ptr_type => ty.toIntern(),
  24285             .opt_type => |child_type| child_type,
  24286             else => unreachable,
  24287         },
  24288         .addr = .{ .decl = new_decl_index },
  24289     } })).toValue(), ty));
  24290 }
  24291 
  24292 fn zirWorkItem(
  24293     sema: *Sema,
  24294     block: *Block,
  24295     extended: Zir.Inst.Extended.InstData,
  24296     zir_tag: Zir.Inst.Extended,
  24297 ) CompileError!Air.Inst.Ref {
  24298     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  24299     const dimension_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
  24300     const builtin_src = LazySrcLoc.nodeOffset(extra.node);
  24301     const target = sema.mod.getTarget();
  24302 
  24303     switch (target.cpu.arch) {
  24304         // TODO: Allow for other GPU targets.
  24305         .amdgcn => {},
  24306         else => {
  24307             return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)});
  24308         },
  24309     }
  24310 
  24311     const dimension = @as(u32, @intCast(try sema.resolveInt(block, dimension_src, extra.operand, Type.u32, "dimension must be comptime-known")));
  24312     try sema.requireRuntimeBlock(block, builtin_src, null);
  24313 
  24314     return block.addInst(.{
  24315         .tag = switch (zir_tag) {
  24316             .work_item_id => .work_item_id,
  24317             .work_group_size => .work_group_size,
  24318             .work_group_id => .work_group_id,
  24319             else => unreachable,
  24320         },
  24321         .data = .{ .pl_op = .{
  24322             .operand = .none,
  24323             .payload = dimension,
  24324         } },
  24325     });
  24326 }
  24327 
  24328 fn zirInComptime(
  24329     sema: *Sema,
  24330     block: *Block,
  24331 ) CompileError!Air.Inst.Ref {
  24332     _ = sema;
  24333     if (block.is_comptime) {
  24334         return Air.Inst.Ref.bool_true;
  24335     } else {
  24336         return Air.Inst.Ref.bool_false;
  24337     }
  24338 }
  24339 
  24340 fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
  24341     if (block.is_comptime) {
  24342         const msg = msg: {
  24343             const msg = try sema.errMsg(block, src, "unable to evaluate comptime expression", .{});
  24344             errdefer msg.destroy(sema.gpa);
  24345 
  24346             if (runtime_src) |some| {
  24347                 try sema.errNote(block, some, msg, "operation is runtime due to this operand", .{});
  24348             }
  24349             if (block.comptime_reason) |some| {
  24350                 try some.explain(sema, msg);
  24351             }
  24352             break :msg msg;
  24353         };
  24354         return sema.failWithOwnedErrorMsg(msg);
  24355     }
  24356 }
  24357 
  24358 /// Emit a compile error if type cannot be used for a runtime variable.
  24359 fn validateVarType(
  24360     sema: *Sema,
  24361     block: *Block,
  24362     src: LazySrcLoc,
  24363     var_ty: Type,
  24364     is_extern: bool,
  24365 ) CompileError!void {
  24366     const mod = sema.mod;
  24367     if (is_extern and !try sema.validateExternType(var_ty, .other)) {
  24368         const msg = msg: {
  24369             const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)});
  24370             errdefer msg.destroy(sema.gpa);
  24371             const src_decl = mod.declPtr(block.src_decl);
  24372             try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), var_ty, .other);
  24373             break :msg msg;
  24374         };
  24375         return sema.failWithOwnedErrorMsg(msg);
  24376     }
  24377 
  24378     if (try sema.validateRunTimeType(var_ty, is_extern)) return;
  24379 
  24380     const msg = msg: {
  24381         const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(mod)});
  24382         errdefer msg.destroy(sema.gpa);
  24383 
  24384         const src_decl = mod.declPtr(block.src_decl);
  24385         try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), var_ty);
  24386         if (var_ty.zigTypeTag(mod) == .ComptimeInt or var_ty.zigTypeTag(mod) == .ComptimeFloat) {
  24387             try sema.errNote(block, src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{});
  24388         }
  24389 
  24390         break :msg msg;
  24391     };
  24392     return sema.failWithOwnedErrorMsg(msg);
  24393 }
  24394 
  24395 fn validateRunTimeType(
  24396     sema: *Sema,
  24397     var_ty: Type,
  24398     is_extern: bool,
  24399 ) CompileError!bool {
  24400     const mod = sema.mod;
  24401     var ty = var_ty;
  24402     while (true) switch (ty.zigTypeTag(mod)) {
  24403         .Bool,
  24404         .Int,
  24405         .Float,
  24406         .ErrorSet,
  24407         .Frame,
  24408         .AnyFrame,
  24409         .Void,
  24410         => return true,
  24411 
  24412         .Enum => return !(try sema.typeRequiresComptime(ty)),
  24413 
  24414         .ComptimeFloat,
  24415         .ComptimeInt,
  24416         .EnumLiteral,
  24417         .NoReturn,
  24418         .Type,
  24419         .Undefined,
  24420         .Null,
  24421         .Fn,
  24422         => return false,
  24423 
  24424         .Pointer => {
  24425             const elem_ty = ty.childType(mod);
  24426             switch (elem_ty.zigTypeTag(mod)) {
  24427                 .Opaque => return true,
  24428                 .Fn => return elem_ty.isFnOrHasRuntimeBits(mod),
  24429                 else => ty = elem_ty,
  24430             }
  24431         },
  24432         .Opaque => return is_extern,
  24433 
  24434         .Optional => {
  24435             const child_ty = ty.optionalChild(mod);
  24436             return sema.validateRunTimeType(child_ty, is_extern);
  24437         },
  24438         .Array, .Vector => ty = ty.childType(mod),
  24439 
  24440         .ErrorUnion => ty = ty.errorUnionPayload(mod),
  24441 
  24442         .Struct, .Union => {
  24443             const resolved_ty = try sema.resolveTypeFields(ty);
  24444             const needs_comptime = try sema.typeRequiresComptime(resolved_ty);
  24445             return !needs_comptime;
  24446         },
  24447     };
  24448 }
  24449 
  24450 const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void);
  24451 
  24452 fn explainWhyTypeIsComptime(
  24453     sema: *Sema,
  24454     msg: *Module.ErrorMsg,
  24455     src_loc: Module.SrcLoc,
  24456     ty: Type,
  24457 ) CompileError!void {
  24458     var type_set = TypeSet{};
  24459     defer type_set.deinit(sema.gpa);
  24460 
  24461     try sema.resolveTypeFully(ty);
  24462     return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set);
  24463 }
  24464 
  24465 fn explainWhyTypeIsComptimeInner(
  24466     sema: *Sema,
  24467     msg: *Module.ErrorMsg,
  24468     src_loc: Module.SrcLoc,
  24469     ty: Type,
  24470     type_set: *TypeSet,
  24471 ) CompileError!void {
  24472     const mod = sema.mod;
  24473     switch (ty.zigTypeTag(mod)) {
  24474         .Bool,
  24475         .Int,
  24476         .Float,
  24477         .ErrorSet,
  24478         .Enum,
  24479         .Frame,
  24480         .AnyFrame,
  24481         .Void,
  24482         => return,
  24483 
  24484         .Fn => {
  24485             try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{
  24486                 ty.fmt(sema.mod),
  24487             });
  24488         },
  24489 
  24490         .Type => {
  24491             try mod.errNoteNonLazy(src_loc, msg, "types are not available at runtime", .{});
  24492         },
  24493 
  24494         .ComptimeFloat,
  24495         .ComptimeInt,
  24496         .EnumLiteral,
  24497         .NoReturn,
  24498         .Undefined,
  24499         .Null,
  24500         => return,
  24501 
  24502         .Opaque => {
  24503             try mod.errNoteNonLazy(src_loc, msg, "opaque type '{}' has undefined size", .{ty.fmt(sema.mod)});
  24504         },
  24505 
  24506         .Array, .Vector => {
  24507             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set);
  24508         },
  24509         .Pointer => {
  24510             const elem_ty = ty.elemType2(mod);
  24511             if (elem_ty.zigTypeTag(mod) == .Fn) {
  24512                 const fn_info = mod.typeToFunc(elem_ty).?;
  24513                 if (fn_info.is_generic) {
  24514                     try mod.errNoteNonLazy(src_loc, msg, "function is generic", .{});
  24515                 }
  24516                 switch (fn_info.cc) {
  24517                     .Inline => try mod.errNoteNonLazy(src_loc, msg, "function has inline calling convention", .{}),
  24518                     else => {},
  24519                 }
  24520                 if (fn_info.return_type.toType().comptimeOnly(mod)) {
  24521                     try mod.errNoteNonLazy(src_loc, msg, "function has a comptime-only return type", .{});
  24522                 }
  24523                 return;
  24524             }
  24525             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set);
  24526         },
  24527 
  24528         .Optional => {
  24529             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(mod), type_set);
  24530         },
  24531         .ErrorUnion => {
  24532             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(mod), type_set);
  24533         },
  24534 
  24535         .Struct => {
  24536             if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
  24537 
  24538             if (mod.typeToStruct(ty)) |struct_obj| {
  24539                 for (struct_obj.fields.values(), 0..) |field, i| {
  24540                     const field_src_loc = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  24541                         .index = i,
  24542                         .range = .type,
  24543                     });
  24544 
  24545                     if (try sema.typeRequiresComptime(field.ty)) {
  24546                         try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{});
  24547                         try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field.ty, type_set);
  24548                     }
  24549                 }
  24550             }
  24551             // TODO tuples
  24552         },
  24553 
  24554         .Union => {
  24555             if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
  24556 
  24557             if (mod.typeToUnion(ty)) |union_obj| {
  24558                 for (union_obj.fields.values(), 0..) |field, i| {
  24559                     const field_src_loc = mod.fieldSrcLoc(union_obj.owner_decl, .{
  24560                         .index = i,
  24561                         .range = .type,
  24562                     });
  24563 
  24564                     if (try sema.typeRequiresComptime(field.ty)) {
  24565                         try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{});
  24566                         try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field.ty, type_set);
  24567                     }
  24568                 }
  24569             }
  24570         },
  24571     }
  24572 }
  24573 
  24574 const ExternPosition = enum {
  24575     ret_ty,
  24576     param_ty,
  24577     union_field,
  24578     struct_field,
  24579     element,
  24580     other,
  24581 };
  24582 
  24583 /// Returns true if `ty` is allowed in extern types.
  24584 /// Does *NOT* require `ty` to be resolved in any way.
  24585 /// Calls `resolveTypeLayout` for packed containers.
  24586 fn validateExternType(
  24587     sema: *Sema,
  24588     ty: Type,
  24589     position: ExternPosition,
  24590 ) !bool {
  24591     const mod = sema.mod;
  24592     switch (ty.zigTypeTag(mod)) {
  24593         .Type,
  24594         .ComptimeFloat,
  24595         .ComptimeInt,
  24596         .EnumLiteral,
  24597         .Undefined,
  24598         .Null,
  24599         .ErrorUnion,
  24600         .ErrorSet,
  24601         .Frame,
  24602         => return false,
  24603         .Void => return position == .union_field or position == .ret_ty,
  24604         .NoReturn => return position == .ret_ty,
  24605         .Opaque,
  24606         .Bool,
  24607         .Float,
  24608         .AnyFrame,
  24609         => return true,
  24610         .Pointer => return !(ty.isSlice(mod) or try sema.typeRequiresComptime(ty)),
  24611         .Int => switch (ty.intInfo(mod).bits) {
  24612             8, 16, 32, 64, 128 => return true,
  24613             else => return false,
  24614         },
  24615         .Fn => {
  24616             if (position != .other) return false;
  24617             const target = sema.mod.getTarget();
  24618             // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI.
  24619             // The goal is to experiment with more integrated CPU/GPU code.
  24620             if (ty.fnCallingConvention(mod) == .Kernel and (target.cpu.arch == .nvptx or target.cpu.arch == .nvptx64)) {
  24621                 return true;
  24622             }
  24623             return !target_util.fnCallConvAllowsZigTypes(target, ty.fnCallingConvention(mod));
  24624         },
  24625         .Enum => {
  24626             return sema.validateExternType(ty.intTagType(mod), position);
  24627         },
  24628         .Struct, .Union => switch (ty.containerLayout(mod)) {
  24629             .Extern => return true,
  24630             .Packed => {
  24631                 const bit_size = try ty.bitSizeAdvanced(mod, sema);
  24632                 switch (bit_size) {
  24633                     8, 16, 32, 64, 128 => return true,
  24634                     else => return false,
  24635                 }
  24636             },
  24637             .Auto => return false,
  24638         },
  24639         .Array => {
  24640             if (position == .ret_ty or position == .param_ty) return false;
  24641             return sema.validateExternType(ty.elemType2(mod), .element);
  24642         },
  24643         .Vector => return sema.validateExternType(ty.elemType2(mod), .element),
  24644         .Optional => return ty.isPtrLikeOptional(mod),
  24645     }
  24646 }
  24647 
  24648 fn explainWhyTypeIsNotExtern(
  24649     sema: *Sema,
  24650     msg: *Module.ErrorMsg,
  24651     src_loc: Module.SrcLoc,
  24652     ty: Type,
  24653     position: ExternPosition,
  24654 ) CompileError!void {
  24655     const mod = sema.mod;
  24656     switch (ty.zigTypeTag(mod)) {
  24657         .Opaque,
  24658         .Bool,
  24659         .Float,
  24660         .AnyFrame,
  24661         => return,
  24662 
  24663         .Type,
  24664         .ComptimeFloat,
  24665         .ComptimeInt,
  24666         .EnumLiteral,
  24667         .Undefined,
  24668         .Null,
  24669         .ErrorUnion,
  24670         .ErrorSet,
  24671         .Frame,
  24672         => return,
  24673 
  24674         .Pointer => {
  24675             if (ty.isSlice(mod)) {
  24676                 try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{});
  24677             } else {
  24678                 const pointee_ty = ty.childType(mod);
  24679                 try mod.errNoteNonLazy(src_loc, msg, "pointer to comptime-only type '{}'", .{pointee_ty.fmt(sema.mod)});
  24680                 try sema.explainWhyTypeIsComptime(msg, src_loc, pointee_ty);
  24681             }
  24682         },
  24683         .Void => try mod.errNoteNonLazy(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}),
  24684         .NoReturn => try mod.errNoteNonLazy(src_loc, msg, "'noreturn' is only allowed as a return type", .{}),
  24685         .Int => if (!std.math.isPowerOfTwo(ty.intInfo(mod).bits)) {
  24686             try mod.errNoteNonLazy(src_loc, msg, "only integers with power of two bits are extern compatible", .{});
  24687         } else {
  24688             try mod.errNoteNonLazy(src_loc, msg, "only integers with 8, 16, 32, 64 and 128 bits are extern compatible", .{});
  24689         },
  24690         .Fn => {
  24691             if (position != .other) {
  24692                 try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{});
  24693                 try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{});
  24694                 return;
  24695             }
  24696             switch (ty.fnCallingConvention(mod)) {
  24697                 .Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}),
  24698                 .Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}),
  24699                 .Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}),
  24700                 else => return,
  24701             }
  24702         },
  24703         .Enum => {
  24704             const tag_ty = ty.intTagType(mod);
  24705             try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)});
  24706             try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position);
  24707         },
  24708         .Struct => try mod.errNoteNonLazy(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}),
  24709         .Union => try mod.errNoteNonLazy(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}),
  24710         .Array => {
  24711             if (position == .ret_ty) {
  24712                 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{});
  24713             } else if (position == .param_ty) {
  24714                 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{});
  24715             }
  24716             try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element);
  24717         },
  24718         .Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element),
  24719         .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
  24720     }
  24721 }
  24722 
  24723 /// Returns true if `ty` is allowed in packed types.
  24724 /// Does *NOT* require `ty` to be resolved in any way.
  24725 fn validatePackedType(ty: Type, mod: *Module) bool {
  24726     switch (ty.zigTypeTag(mod)) {
  24727         .Type,
  24728         .ComptimeFloat,
  24729         .ComptimeInt,
  24730         .EnumLiteral,
  24731         .Undefined,
  24732         .Null,
  24733         .ErrorUnion,
  24734         .ErrorSet,
  24735         .Frame,
  24736         .NoReturn,
  24737         .Opaque,
  24738         .AnyFrame,
  24739         .Fn,
  24740         .Array,
  24741         => return false,
  24742         .Optional => return ty.isPtrLikeOptional(mod),
  24743         .Void,
  24744         .Bool,
  24745         .Float,
  24746         .Int,
  24747         .Vector,
  24748         .Enum,
  24749         => return true,
  24750         .Pointer => return !ty.isSlice(mod),
  24751         .Struct, .Union => return ty.containerLayout(mod) == .Packed,
  24752     }
  24753 }
  24754 
  24755 fn explainWhyTypeIsNotPacked(
  24756     sema: *Sema,
  24757     msg: *Module.ErrorMsg,
  24758     src_loc: Module.SrcLoc,
  24759     ty: Type,
  24760 ) CompileError!void {
  24761     const mod = sema.mod;
  24762     switch (ty.zigTypeTag(mod)) {
  24763         .Void,
  24764         .Bool,
  24765         .Float,
  24766         .Int,
  24767         .Vector,
  24768         .Enum,
  24769         => return,
  24770         .Type,
  24771         .ComptimeFloat,
  24772         .ComptimeInt,
  24773         .EnumLiteral,
  24774         .Undefined,
  24775         .Null,
  24776         .Frame,
  24777         .NoReturn,
  24778         .Opaque,
  24779         .ErrorUnion,
  24780         .ErrorSet,
  24781         .AnyFrame,
  24782         .Optional,
  24783         .Array,
  24784         => try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}),
  24785         .Pointer => try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{}),
  24786         .Fn => {
  24787             try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{});
  24788             try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{});
  24789         },
  24790         .Struct => try mod.errNoteNonLazy(src_loc, msg, "only packed structs layout are allowed in packed types", .{}),
  24791         .Union => try mod.errNoteNonLazy(src_loc, msg, "only packed unions layout are allowed in packed types", .{}),
  24792     }
  24793 }
  24794 
  24795 fn prepareSimplePanic(sema: *Sema, block: *Block) !void {
  24796     const mod = sema.mod;
  24797 
  24798     if (mod.panic_func_index == .none) {
  24799         const decl_index = (try sema.getBuiltinDecl(block, "panic"));
  24800         // decl_index may be an alias; we must find the decl that actually
  24801         // owns the function.
  24802         try sema.ensureDeclAnalyzed(decl_index);
  24803         const tv = try mod.declPtr(decl_index).typedValue();
  24804         assert(tv.ty.zigTypeTag(mod) == .Fn);
  24805         assert(try sema.fnHasRuntimeBits(tv.ty));
  24806         const func_index = mod.intern_pool.indexToFunc(tv.val.toIntern()).unwrap().?;
  24807         try mod.ensureFuncBodyAnalysisQueued(func_index);
  24808         mod.panic_func_index = func_index.toOptional();
  24809     }
  24810 
  24811     if (mod.null_stack_trace == .none) {
  24812         const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
  24813         const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
  24814         const target = mod.getTarget();
  24815         const ptr_stack_trace_ty = try mod.ptrType(.{
  24816             .child = stack_trace_ty.toIntern(),
  24817             .flags = .{
  24818                 .address_space = target_util.defaultAddressSpace(target, .global_constant),
  24819             },
  24820         });
  24821         const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern());
  24822         mod.null_stack_trace = try mod.intern(.{ .opt = .{
  24823             .ty = opt_ptr_stack_trace_ty.toIntern(),
  24824             .val = .none,
  24825         } });
  24826     }
  24827 }
  24828 
  24829 /// Backends depend on panic decls being available when lowering safety-checked
  24830 /// instructions. This function ensures the panic function will be available to
  24831 /// be called during that time.
  24832 fn preparePanicId(sema: *Sema, block: *Block, panic_id: Module.PanicId) !Module.Decl.Index {
  24833     const mod = sema.mod;
  24834     const gpa = sema.gpa;
  24835     if (mod.panic_messages[@intFromEnum(panic_id)].unwrap()) |x| return x;
  24836 
  24837     try sema.prepareSimplePanic(block);
  24838 
  24839     const panic_messages_ty = try sema.getBuiltinType("panic_messages");
  24840     const msg_decl_index = (try sema.namespaceLookup(
  24841         block,
  24842         sema.src,
  24843         panic_messages_ty.getNamespaceIndex(mod).unwrap().?,
  24844         try mod.intern_pool.getOrPutString(gpa, @tagName(panic_id)),
  24845     )).?;
  24846     try sema.ensureDeclAnalyzed(msg_decl_index);
  24847     mod.panic_messages[@intFromEnum(panic_id)] = msg_decl_index.toOptional();
  24848     return msg_decl_index;
  24849 }
  24850 
  24851 fn addSafetyCheck(
  24852     sema: *Sema,
  24853     parent_block: *Block,
  24854     ok: Air.Inst.Ref,
  24855     panic_id: Module.PanicId,
  24856 ) !void {
  24857     const gpa = sema.gpa;
  24858     assert(!parent_block.is_comptime);
  24859 
  24860     var fail_block: Block = .{
  24861         .parent = parent_block,
  24862         .sema = sema,
  24863         .src_decl = parent_block.src_decl,
  24864         .namespace = parent_block.namespace,
  24865         .wip_capture_scope = parent_block.wip_capture_scope,
  24866         .instructions = .{},
  24867         .inlining = parent_block.inlining,
  24868         .is_comptime = false,
  24869     };
  24870 
  24871     defer fail_block.instructions.deinit(gpa);
  24872 
  24873     try sema.safetyPanic(&fail_block, panic_id);
  24874     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  24875 }
  24876 
  24877 fn addSafetyCheckExtra(
  24878     sema: *Sema,
  24879     parent_block: *Block,
  24880     ok: Air.Inst.Ref,
  24881     fail_block: *Block,
  24882 ) !void {
  24883     const gpa = sema.gpa;
  24884 
  24885     try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
  24886 
  24887     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
  24888         1 + // The main block only needs space for the cond_br.
  24889         @typeInfo(Air.CondBr).Struct.fields.len +
  24890         1 + // The ok branch of the cond_br only needs space for the br.
  24891         fail_block.instructions.items.len);
  24892 
  24893     try sema.air_instructions.ensureUnusedCapacity(gpa, 3);
  24894     const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len));
  24895     const cond_br_inst = block_inst + 1;
  24896     const br_inst = cond_br_inst + 1;
  24897     sema.air_instructions.appendAssumeCapacity(.{
  24898         .tag = .block,
  24899         .data = .{ .ty_pl = .{
  24900             .ty = .void_type,
  24901             .payload = sema.addExtraAssumeCapacity(Air.Block{
  24902                 .body_len = 1,
  24903             }),
  24904         } },
  24905     });
  24906     sema.air_extra.appendAssumeCapacity(cond_br_inst);
  24907 
  24908     sema.air_instructions.appendAssumeCapacity(.{
  24909         .tag = .cond_br,
  24910         .data = .{ .pl_op = .{
  24911             .operand = ok,
  24912             .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  24913                 .then_body_len = 1,
  24914                 .else_body_len = @as(u32, @intCast(fail_block.instructions.items.len)),
  24915             }),
  24916         } },
  24917     });
  24918     sema.air_extra.appendAssumeCapacity(br_inst);
  24919     sema.air_extra.appendSliceAssumeCapacity(fail_block.instructions.items);
  24920 
  24921     sema.air_instructions.appendAssumeCapacity(.{
  24922         .tag = .br,
  24923         .data = .{ .br = .{
  24924             .block_inst = block_inst,
  24925             .operand = .void_value,
  24926         } },
  24927     });
  24928 
  24929     parent_block.instructions.appendAssumeCapacity(block_inst);
  24930 }
  24931 
  24932 fn panicWithMsg(sema: *Sema, block: *Block, msg_inst: Air.Inst.Ref) !void {
  24933     const mod = sema.mod;
  24934 
  24935     if (!mod.backendSupportsFeature(.panic_fn)) {
  24936         _ = try block.addNoOp(.trap);
  24937         return;
  24938     }
  24939 
  24940     try sema.prepareSimplePanic(block);
  24941 
  24942     const panic_func = mod.funcPtrUnwrap(mod.panic_func_index).?;
  24943     const panic_fn = try sema.analyzeDeclVal(block, .unneeded, panic_func.owner_decl);
  24944     const null_stack_trace = try sema.addConstant(mod.null_stack_trace.toValue());
  24945 
  24946     const opt_usize_ty = try mod.optionalType(.usize_type);
  24947     const null_ret_addr = try sema.addConstant((try mod.intern(.{ .opt = .{
  24948         .ty = opt_usize_ty.toIntern(),
  24949         .val = .none,
  24950     } })).toValue());
  24951     try sema.callBuiltin(block, panic_fn, .auto, &.{ msg_inst, null_stack_trace, null_ret_addr });
  24952 }
  24953 
  24954 fn panicUnwrapError(
  24955     sema: *Sema,
  24956     parent_block: *Block,
  24957     operand: Air.Inst.Ref,
  24958     unwrap_err_tag: Air.Inst.Tag,
  24959     is_non_err_tag: Air.Inst.Tag,
  24960 ) !void {
  24961     assert(!parent_block.is_comptime);
  24962     const ok = try parent_block.addUnOp(is_non_err_tag, operand);
  24963     if (!sema.mod.comp.formatted_panics) {
  24964         return sema.addSafetyCheck(parent_block, ok, .unwrap_error);
  24965     }
  24966     const gpa = sema.gpa;
  24967 
  24968     var fail_block: Block = .{
  24969         .parent = parent_block,
  24970         .sema = sema,
  24971         .src_decl = parent_block.src_decl,
  24972         .namespace = parent_block.namespace,
  24973         .wip_capture_scope = parent_block.wip_capture_scope,
  24974         .instructions = .{},
  24975         .inlining = parent_block.inlining,
  24976         .is_comptime = false,
  24977     };
  24978 
  24979     defer fail_block.instructions.deinit(gpa);
  24980 
  24981     {
  24982         if (!sema.mod.backendSupportsFeature(.panic_unwrap_error)) {
  24983             _ = try fail_block.addNoOp(.trap);
  24984         } else {
  24985             const panic_fn = try sema.getBuiltin("panicUnwrapError");
  24986             const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
  24987             const err_return_trace = try sema.getErrorReturnTrace(&fail_block);
  24988             const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
  24989             try sema.callBuiltin(&fail_block, panic_fn, .auto, &args);
  24990         }
  24991     }
  24992     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  24993 }
  24994 
  24995 fn panicIndexOutOfBounds(
  24996     sema: *Sema,
  24997     parent_block: *Block,
  24998     index: Air.Inst.Ref,
  24999     len: Air.Inst.Ref,
  25000     cmp_op: Air.Inst.Tag,
  25001 ) !void {
  25002     assert(!parent_block.is_comptime);
  25003     const ok = try parent_block.addBinOp(cmp_op, index, len);
  25004     if (!sema.mod.comp.formatted_panics) {
  25005         return sema.addSafetyCheck(parent_block, ok, .index_out_of_bounds);
  25006     }
  25007     try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });
  25008 }
  25009 
  25010 fn panicInactiveUnionField(
  25011     sema: *Sema,
  25012     parent_block: *Block,
  25013     active_tag: Air.Inst.Ref,
  25014     wanted_tag: Air.Inst.Ref,
  25015 ) !void {
  25016     assert(!parent_block.is_comptime);
  25017     const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
  25018     if (!sema.mod.comp.formatted_panics) {
  25019         return sema.addSafetyCheck(parent_block, ok, .inactive_union_field);
  25020     }
  25021     try sema.safetyCheckFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
  25022 }
  25023 
  25024 fn panicSentinelMismatch(
  25025     sema: *Sema,
  25026     parent_block: *Block,
  25027     maybe_sentinel: ?Value,
  25028     sentinel_ty: Type,
  25029     ptr: Air.Inst.Ref,
  25030     sentinel_index: Air.Inst.Ref,
  25031 ) !void {
  25032     assert(!parent_block.is_comptime);
  25033     const mod = sema.mod;
  25034     const expected_sentinel_val = maybe_sentinel orelse return;
  25035     const expected_sentinel = try sema.addConstant(expected_sentinel_val);
  25036 
  25037     const ptr_ty = sema.typeOf(ptr);
  25038     const actual_sentinel = if (ptr_ty.isSlice(mod))
  25039         try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index)
  25040     else blk: {
  25041         const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null);
  25042         const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty);
  25043         break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr);
  25044     };
  25045 
  25046     const ok = if (sentinel_ty.zigTypeTag(mod) == .Vector) ok: {
  25047         const eql =
  25048             try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq);
  25049         break :ok try parent_block.addInst(.{
  25050             .tag = .reduce,
  25051             .data = .{ .reduce = .{
  25052                 .operand = eql,
  25053                 .operation = .And,
  25054             } },
  25055         });
  25056     } else if (sentinel_ty.isSelfComparable(mod, true))
  25057         try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel)
  25058     else {
  25059         const panic_fn = try sema.getBuiltin("checkNonScalarSentinel");
  25060         const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
  25061         try sema.callBuiltin(parent_block, panic_fn, .auto, &args);
  25062         return;
  25063     };
  25064 
  25065     if (!sema.mod.comp.formatted_panics) {
  25066         return sema.addSafetyCheck(parent_block, ok, .sentinel_mismatch);
  25067     }
  25068     try sema.safetyCheckFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel });
  25069 }
  25070 
  25071 fn safetyCheckFormatted(
  25072     sema: *Sema,
  25073     parent_block: *Block,
  25074     ok: Air.Inst.Ref,
  25075     func: []const u8,
  25076     args: []const Air.Inst.Ref,
  25077 ) CompileError!void {
  25078     assert(sema.mod.comp.formatted_panics);
  25079     const gpa = sema.gpa;
  25080 
  25081     var fail_block: Block = .{
  25082         .parent = parent_block,
  25083         .sema = sema,
  25084         .src_decl = parent_block.src_decl,
  25085         .namespace = parent_block.namespace,
  25086         .wip_capture_scope = parent_block.wip_capture_scope,
  25087         .instructions = .{},
  25088         .inlining = parent_block.inlining,
  25089         .is_comptime = false,
  25090     };
  25091 
  25092     defer fail_block.instructions.deinit(gpa);
  25093 
  25094     if (!sema.mod.backendSupportsFeature(.safety_check_formatted)) {
  25095         _ = try fail_block.addNoOp(.trap);
  25096     } else {
  25097         const panic_fn = try sema.getBuiltin(func);
  25098         try sema.callBuiltin(&fail_block, panic_fn, .auto, args);
  25099     }
  25100     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  25101 }
  25102 
  25103 fn safetyPanic(sema: *Sema, block: *Block, panic_id: Module.PanicId) CompileError!void {
  25104     const msg_decl_index = try sema.preparePanicId(block, panic_id);
  25105     const msg_inst = try sema.analyzeDeclVal(block, sema.src, msg_decl_index);
  25106     try sema.panicWithMsg(block, msg_inst);
  25107 }
  25108 
  25109 fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
  25110     sema.branch_count += 1;
  25111     if (sema.branch_count > sema.branch_quota) {
  25112         const msg = try sema.errMsg(
  25113             block,
  25114             src,
  25115             "evaluation exceeded {d} backwards branches",
  25116             .{sema.branch_quota},
  25117         );
  25118         try sema.errNote(
  25119             block,
  25120             src,
  25121             msg,
  25122             "use @setEvalBranchQuota() to raise the branch limit from {d}",
  25123             .{sema.branch_quota},
  25124         );
  25125         return sema.failWithOwnedErrorMsg(msg);
  25126     }
  25127 }
  25128 
  25129 fn fieldVal(
  25130     sema: *Sema,
  25131     block: *Block,
  25132     src: LazySrcLoc,
  25133     object: Air.Inst.Ref,
  25134     field_name: InternPool.NullTerminatedString,
  25135     field_name_src: LazySrcLoc,
  25136 ) CompileError!Air.Inst.Ref {
  25137     // When editing this function, note that there is corresponding logic to be edited
  25138     // in `fieldPtr`. This function takes a value and returns a value.
  25139 
  25140     const mod = sema.mod;
  25141     const ip = &mod.intern_pool;
  25142     const object_src = src; // TODO better source location
  25143     const object_ty = sema.typeOf(object);
  25144 
  25145     // Zig allows dereferencing a single pointer during field lookup. Note that
  25146     // we don't actually need to generate the dereference some field lookups, like the
  25147     // length of arrays and other comptime operations.
  25148     const is_pointer_to = object_ty.isSinglePointer(mod);
  25149 
  25150     const inner_ty = if (is_pointer_to)
  25151         object_ty.childType(mod)
  25152     else
  25153         object_ty;
  25154 
  25155     switch (inner_ty.zigTypeTag(mod)) {
  25156         .Array => {
  25157             if (ip.stringEqlSlice(field_name, "len")) {
  25158                 return sema.addConstant(
  25159                     try mod.intValue(Type.usize, inner_ty.arrayLen(mod)),
  25160                 );
  25161             } else if (ip.stringEqlSlice(field_name, "ptr") and is_pointer_to) {
  25162                 const ptr_info = object_ty.ptrInfo(mod);
  25163                 const result_ty = try mod.ptrType(.{
  25164                     .child = ptr_info.child.toType().childType(mod).toIntern(),
  25165                     .sentinel = ptr_info.sentinel,
  25166                     .flags = .{
  25167                         .size = .Many,
  25168                         .alignment = ptr_info.flags.alignment,
  25169                         .is_const = ptr_info.flags.is_const,
  25170                         .is_volatile = ptr_info.flags.is_volatile,
  25171                         .is_allowzero = ptr_info.flags.is_allowzero,
  25172                         .address_space = ptr_info.flags.address_space,
  25173                         .vector_index = ptr_info.flags.vector_index,
  25174                     },
  25175                     .packed_offset = ptr_info.packed_offset,
  25176                 });
  25177                 return sema.coerce(block, result_ty, object, src);
  25178             } else {
  25179                 return sema.fail(
  25180                     block,
  25181                     field_name_src,
  25182                     "no member named '{}' in '{}'",
  25183                     .{ field_name.fmt(ip), object_ty.fmt(mod) },
  25184                 );
  25185             }
  25186         },
  25187         .Pointer => {
  25188             const ptr_info = inner_ty.ptrInfo(mod);
  25189             if (ptr_info.flags.size == .Slice) {
  25190                 if (ip.stringEqlSlice(field_name, "ptr")) {
  25191                     const slice = if (is_pointer_to)
  25192                         try sema.analyzeLoad(block, src, object, object_src)
  25193                     else
  25194                         object;
  25195                     return sema.analyzeSlicePtr(block, object_src, slice, inner_ty);
  25196                 } else if (ip.stringEqlSlice(field_name, "len")) {
  25197                     const slice = if (is_pointer_to)
  25198                         try sema.analyzeLoad(block, src, object, object_src)
  25199                     else
  25200                         object;
  25201                     return sema.analyzeSliceLen(block, src, slice);
  25202                 } else {
  25203                     return sema.fail(
  25204                         block,
  25205                         field_name_src,
  25206                         "no member named '{}' in '{}'",
  25207                         .{ field_name.fmt(ip), object_ty.fmt(mod) },
  25208                     );
  25209                 }
  25210             }
  25211         },
  25212         .Type => {
  25213             const dereffed_type = if (is_pointer_to)
  25214                 try sema.analyzeLoad(block, src, object, object_src)
  25215             else
  25216                 object;
  25217 
  25218             const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
  25219             const child_type = val.toType();
  25220 
  25221             switch (try child_type.zigTypeTagOrPoison(mod)) {
  25222                 .ErrorSet => {
  25223                     switch (ip.indexToKey(child_type.toIntern())) {
  25224                         .error_set_type => |error_set_type| blk: {
  25225                             if (error_set_type.nameIndex(ip, field_name) != null) break :blk;
  25226                             const msg = msg: {
  25227                                 const msg = try sema.errMsg(block, src, "no error named '{}' in '{}'", .{
  25228                                     field_name.fmt(ip), child_type.fmt(mod),
  25229                                 });
  25230                                 errdefer msg.destroy(sema.gpa);
  25231                                 try sema.addDeclaredHereNote(msg, child_type);
  25232                                 break :msg msg;
  25233                             };
  25234                             return sema.failWithOwnedErrorMsg(msg);
  25235                         },
  25236                         .inferred_error_set_type => {
  25237                             return sema.fail(block, src, "TODO handle inferred error sets here", .{});
  25238                         },
  25239                         .simple_type => |t| {
  25240                             assert(t == .anyerror);
  25241                             _ = try mod.getErrorValue(field_name);
  25242                         },
  25243                         else => unreachable,
  25244                     }
  25245 
  25246                     const error_set_type = if (!child_type.isAnyError(mod))
  25247                         child_type
  25248                     else
  25249                         try mod.singleErrorSetType(field_name);
  25250                     return sema.addConstant((try mod.intern(.{ .err = .{
  25251                         .ty = error_set_type.toIntern(),
  25252                         .name = field_name,
  25253                     } })).toValue());
  25254                 },
  25255                 .Union => {
  25256                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25257                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
  25258                             return inst;
  25259                         }
  25260                     }
  25261                     const union_ty = try sema.resolveTypeFields(child_type);
  25262                     if (union_ty.unionTagType(mod)) |enum_ty| {
  25263                         if (enum_ty.enumFieldIndex(field_name, mod)) |field_index_usize| {
  25264                             const field_index = @as(u32, @intCast(field_index_usize));
  25265                             return sema.addConstant(
  25266                                 try mod.enumValueFieldIndex(enum_ty, field_index),
  25267                             );
  25268                         }
  25269                     }
  25270                     return sema.failWithBadMemberAccess(block, union_ty, field_name_src, field_name);
  25271                 },
  25272                 .Enum => {
  25273                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25274                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
  25275                             return inst;
  25276                         }
  25277                     }
  25278                     const field_index_usize = child_type.enumFieldIndex(field_name, mod) orelse
  25279                         return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  25280                     const field_index = @as(u32, @intCast(field_index_usize));
  25281                     const enum_val = try mod.enumValueFieldIndex(child_type, field_index);
  25282                     return sema.addConstant(enum_val);
  25283                 },
  25284                 .Struct, .Opaque => {
  25285                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25286                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
  25287                             return inst;
  25288                         }
  25289                     }
  25290                     return sema.failWithBadMemberAccess(block, child_type, src, field_name);
  25291                 },
  25292                 else => {
  25293                     const msg = msg: {
  25294                         const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(mod)});
  25295                         errdefer msg.destroy(sema.gpa);
  25296                         if (child_type.isSlice(mod)) try sema.errNote(block, src, msg, "slice values have 'len' and 'ptr' members", .{});
  25297                         if (child_type.zigTypeTag(mod) == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{});
  25298                         break :msg msg;
  25299                     };
  25300                     return sema.failWithOwnedErrorMsg(msg);
  25301                 },
  25302             }
  25303         },
  25304         .Struct => if (is_pointer_to) {
  25305             // Avoid loading the entire struct by fetching a pointer and loading that
  25306             const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
  25307             return sema.analyzeLoad(block, src, field_ptr, object_src);
  25308         } else {
  25309             return sema.structFieldVal(block, src, object, field_name, field_name_src, inner_ty);
  25310         },
  25311         .Union => if (is_pointer_to) {
  25312             // Avoid loading the entire union by fetching a pointer and loading that
  25313             const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
  25314             return sema.analyzeLoad(block, src, field_ptr, object_src);
  25315         } else {
  25316             return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
  25317         },
  25318         else => {},
  25319     }
  25320     return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
  25321 }
  25322 
  25323 fn fieldPtr(
  25324     sema: *Sema,
  25325     block: *Block,
  25326     src: LazySrcLoc,
  25327     object_ptr: Air.Inst.Ref,
  25328     field_name: InternPool.NullTerminatedString,
  25329     field_name_src: LazySrcLoc,
  25330     initializing: bool,
  25331 ) CompileError!Air.Inst.Ref {
  25332     // When editing this function, note that there is corresponding logic to be edited
  25333     // in `fieldVal`. This function takes a pointer and returns a pointer.
  25334 
  25335     const mod = sema.mod;
  25336     const ip = &mod.intern_pool;
  25337     const object_ptr_src = src; // TODO better source location
  25338     const object_ptr_ty = sema.typeOf(object_ptr);
  25339     const object_ty = switch (object_ptr_ty.zigTypeTag(mod)) {
  25340         .Pointer => object_ptr_ty.childType(mod),
  25341         else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(mod)}),
  25342     };
  25343 
  25344     // Zig allows dereferencing a single pointer during field lookup. Note that
  25345     // we don't actually need to generate the dereference some field lookups, like the
  25346     // length of arrays and other comptime operations.
  25347     const is_pointer_to = object_ty.isSinglePointer(mod);
  25348 
  25349     const inner_ty = if (is_pointer_to)
  25350         object_ty.childType(mod)
  25351     else
  25352         object_ty;
  25353 
  25354     switch (inner_ty.zigTypeTag(mod)) {
  25355         .Array => {
  25356             if (ip.stringEqlSlice(field_name, "len")) {
  25357                 var anon_decl = try block.startAnonDecl();
  25358                 defer anon_decl.deinit();
  25359                 return sema.analyzeDeclRef(try anon_decl.finish(
  25360                     Type.usize,
  25361                     try mod.intValue(Type.usize, inner_ty.arrayLen(mod)),
  25362                     .none, // default alignment
  25363                 ));
  25364             } else {
  25365                 return sema.fail(
  25366                     block,
  25367                     field_name_src,
  25368                     "no member named '{}' in '{}'",
  25369                     .{ field_name.fmt(ip), object_ty.fmt(mod) },
  25370                 );
  25371             }
  25372         },
  25373         .Pointer => if (inner_ty.isSlice(mod)) {
  25374             const inner_ptr = if (is_pointer_to)
  25375                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  25376             else
  25377                 object_ptr;
  25378 
  25379             const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty;
  25380 
  25381             if (ip.stringEqlSlice(field_name, "ptr")) {
  25382                 const slice_ptr_ty = inner_ty.slicePtrFieldType(mod);
  25383 
  25384                 const result_ty = try mod.ptrType(.{
  25385                     .child = slice_ptr_ty.toIntern(),
  25386                     .flags = .{
  25387                         .is_const = !attr_ptr_ty.ptrIsMutable(mod),
  25388                         .is_volatile = attr_ptr_ty.isVolatilePtr(mod),
  25389                         .address_space = attr_ptr_ty.ptrAddressSpace(mod),
  25390                     },
  25391                 });
  25392 
  25393                 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
  25394                     return sema.addConstant((try mod.intern(.{ .ptr = .{
  25395                         .ty = result_ty.toIntern(),
  25396                         .addr = .{ .field = .{
  25397                             .base = val.toIntern(),
  25398                             .index = Value.slice_ptr_index,
  25399                         } },
  25400                     } })).toValue());
  25401                 }
  25402                 try sema.requireRuntimeBlock(block, src, null);
  25403 
  25404                 return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
  25405             } else if (ip.stringEqlSlice(field_name, "len")) {
  25406                 const result_ty = try mod.ptrType(.{
  25407                     .child = .usize_type,
  25408                     .flags = .{
  25409                         .is_const = !attr_ptr_ty.ptrIsMutable(mod),
  25410                         .is_volatile = attr_ptr_ty.isVolatilePtr(mod),
  25411                         .address_space = attr_ptr_ty.ptrAddressSpace(mod),
  25412                     },
  25413                 });
  25414 
  25415                 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
  25416                     return sema.addConstant((try mod.intern(.{ .ptr = .{
  25417                         .ty = result_ty.toIntern(),
  25418                         .addr = .{ .field = .{
  25419                             .base = val.toIntern(),
  25420                             .index = Value.slice_len_index,
  25421                         } },
  25422                     } })).toValue());
  25423                 }
  25424                 try sema.requireRuntimeBlock(block, src, null);
  25425 
  25426                 return block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr);
  25427             } else {
  25428                 return sema.fail(
  25429                     block,
  25430                     field_name_src,
  25431                     "no member named '{}' in '{}'",
  25432                     .{ field_name.fmt(ip), object_ty.fmt(mod) },
  25433                 );
  25434             }
  25435         },
  25436         .Type => {
  25437             _ = try sema.resolveConstValue(block, .unneeded, object_ptr, "");
  25438             const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
  25439             const inner = if (is_pointer_to)
  25440                 try sema.analyzeLoad(block, src, result, object_ptr_src)
  25441             else
  25442                 result;
  25443 
  25444             const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
  25445             const child_type = val.toType();
  25446 
  25447             switch (child_type.zigTypeTag(mod)) {
  25448                 .ErrorSet => {
  25449                     switch (ip.indexToKey(child_type.toIntern())) {
  25450                         .error_set_type => |error_set_type| blk: {
  25451                             if (error_set_type.nameIndex(ip, field_name) != null) {
  25452                                 break :blk;
  25453                             }
  25454                             return sema.fail(block, src, "no error named '{}' in '{}'", .{
  25455                                 field_name.fmt(ip), child_type.fmt(mod),
  25456                             });
  25457                         },
  25458                         .inferred_error_set_type => {
  25459                             return sema.fail(block, src, "TODO handle inferred error sets here", .{});
  25460                         },
  25461                         .simple_type => |t| {
  25462                             assert(t == .anyerror);
  25463                             _ = try mod.getErrorValue(field_name);
  25464                         },
  25465                         else => unreachable,
  25466                     }
  25467 
  25468                     var anon_decl = try block.startAnonDecl();
  25469                     defer anon_decl.deinit();
  25470                     const error_set_type = if (!child_type.isAnyError(mod))
  25471                         child_type
  25472                     else
  25473                         try mod.singleErrorSetType(field_name);
  25474                     return sema.analyzeDeclRef(try anon_decl.finish(
  25475                         error_set_type,
  25476                         (try mod.intern(.{ .err = .{
  25477                             .ty = error_set_type.toIntern(),
  25478                             .name = field_name,
  25479                         } })).toValue(),
  25480                         .none, // default alignment
  25481                     ));
  25482                 },
  25483                 .Union => {
  25484                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25485                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
  25486                             return inst;
  25487                         }
  25488                     }
  25489                     const union_ty = try sema.resolveTypeFields(child_type);
  25490                     if (union_ty.unionTagType(mod)) |enum_ty| {
  25491                         if (enum_ty.enumFieldIndex(field_name, mod)) |field_index| {
  25492                             const field_index_u32 = @as(u32, @intCast(field_index));
  25493                             var anon_decl = try block.startAnonDecl();
  25494                             defer anon_decl.deinit();
  25495                             return sema.analyzeDeclRef(try anon_decl.finish(
  25496                                 enum_ty,
  25497                                 try mod.enumValueFieldIndex(enum_ty, field_index_u32),
  25498                                 .none, // default alignment
  25499                             ));
  25500                         }
  25501                     }
  25502                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  25503                 },
  25504                 .Enum => {
  25505                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25506                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
  25507                             return inst;
  25508                         }
  25509                     }
  25510                     const field_index = child_type.enumFieldIndex(field_name, mod) orelse {
  25511                         return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  25512                     };
  25513                     const field_index_u32 = @as(u32, @intCast(field_index));
  25514                     var anon_decl = try block.startAnonDecl();
  25515                     defer anon_decl.deinit();
  25516                     return sema.analyzeDeclRef(try anon_decl.finish(
  25517                         child_type,
  25518                         try mod.enumValueFieldIndex(child_type, field_index_u32),
  25519                         .none, // default alignment
  25520                     ));
  25521                 },
  25522                 .Struct, .Opaque => {
  25523                     if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
  25524                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
  25525                             return inst;
  25526                         }
  25527                     }
  25528                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  25529                 },
  25530                 else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(mod)}),
  25531             }
  25532         },
  25533         .Struct => {
  25534             const inner_ptr = if (is_pointer_to)
  25535                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  25536             else
  25537                 object_ptr;
  25538             return sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
  25539         },
  25540         .Union => {
  25541             const inner_ptr = if (is_pointer_to)
  25542                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  25543             else
  25544                 object_ptr;
  25545             return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
  25546         },
  25547         else => {},
  25548     }
  25549     return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
  25550 }
  25551 
  25552 const ResolvedFieldCallee = union(enum) {
  25553     /// The LHS of the call was an actual field with this value.
  25554     direct: Air.Inst.Ref,
  25555     /// This is a method call, with the function and first argument given.
  25556     method: struct {
  25557         func_inst: Air.Inst.Ref,
  25558         arg0_inst: Air.Inst.Ref,
  25559     },
  25560 };
  25561 
  25562 fn fieldCallBind(
  25563     sema: *Sema,
  25564     block: *Block,
  25565     src: LazySrcLoc,
  25566     raw_ptr: Air.Inst.Ref,
  25567     field_name: InternPool.NullTerminatedString,
  25568     field_name_src: LazySrcLoc,
  25569 ) CompileError!ResolvedFieldCallee {
  25570     // When editing this function, note that there is corresponding logic to be edited
  25571     // in `fieldVal`. This function takes a pointer and returns a pointer.
  25572 
  25573     const mod = sema.mod;
  25574     const ip = &mod.intern_pool;
  25575     const raw_ptr_src = src; // TODO better source location
  25576     const raw_ptr_ty = sema.typeOf(raw_ptr);
  25577     const inner_ty = if (raw_ptr_ty.zigTypeTag(mod) == .Pointer and (raw_ptr_ty.ptrSize(mod) == .One or raw_ptr_ty.ptrSize(mod) == .C))
  25578         raw_ptr_ty.childType(mod)
  25579     else
  25580         return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(mod)});
  25581 
  25582     // Optionally dereference a second pointer to get the concrete type.
  25583     const is_double_ptr = inner_ty.zigTypeTag(mod) == .Pointer and inner_ty.ptrSize(mod) == .One;
  25584     const concrete_ty = if (is_double_ptr) inner_ty.childType(mod) else inner_ty;
  25585     const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty;
  25586     const object_ptr = if (is_double_ptr)
  25587         try sema.analyzeLoad(block, src, raw_ptr, src)
  25588     else
  25589         raw_ptr;
  25590 
  25591     find_field: {
  25592         switch (concrete_ty.zigTypeTag(mod)) {
  25593             .Struct => {
  25594                 const struct_ty = try sema.resolveTypeFields(concrete_ty);
  25595                 if (mod.typeToStruct(struct_ty)) |struct_obj| {
  25596                     const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
  25597                         break :find_field;
  25598                     const field_index = @as(u32, @intCast(field_index_usize));
  25599                     const field = struct_obj.fields.values()[field_index];
  25600 
  25601                     return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr);
  25602                 } else if (struct_ty.isTuple(mod)) {
  25603                     if (ip.stringEqlSlice(field_name, "len")) {
  25604                         return .{ .direct = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount(mod)) };
  25605                     }
  25606                     if (field_name.toUnsigned(ip)) |field_index| {
  25607                         if (field_index >= struct_ty.structFieldCount(mod)) break :find_field;
  25608                         return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(field_index, mod), field_index, object_ptr);
  25609                     }
  25610                 } else {
  25611                     const max = struct_ty.structFieldCount(mod);
  25612                     for (0..max) |i_usize| {
  25613                         const i = @as(u32, @intCast(i_usize));
  25614                         if (field_name == struct_ty.structFieldName(i, mod)) {
  25615                             return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(i, mod), i, object_ptr);
  25616                         }
  25617                     }
  25618                 }
  25619             },
  25620             .Union => {
  25621                 const union_ty = try sema.resolveTypeFields(concrete_ty);
  25622                 const fields = union_ty.unionFields(mod);
  25623                 const field_index_usize = fields.getIndex(field_name) orelse break :find_field;
  25624                 const field_index = @as(u32, @intCast(field_index_usize));
  25625                 const field = fields.values()[field_index];
  25626 
  25627                 return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr);
  25628             },
  25629             .Type => {
  25630                 const namespace = try sema.analyzeLoad(block, src, object_ptr, src);
  25631                 return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) };
  25632             },
  25633             else => {},
  25634         }
  25635     }
  25636 
  25637     // If we get here, we need to look for a decl in the struct type instead.
  25638     const found_decl = switch (concrete_ty.zigTypeTag(mod)) {
  25639         .Struct, .Opaque, .Union, .Enum => found_decl: {
  25640             if (concrete_ty.getNamespaceIndex(mod).unwrap()) |namespace| {
  25641                 if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| {
  25642                     try sema.addReferencedBy(block, src, decl_idx);
  25643                     const decl_val = try sema.analyzeDeclVal(block, src, decl_idx);
  25644                     const decl_type = sema.typeOf(decl_val);
  25645                     if (mod.typeToFunc(decl_type)) |func_type| f: {
  25646                         if (func_type.param_types.len == 0) break :f;
  25647 
  25648                         const first_param_type = func_type.param_types[0].toType();
  25649                         // zig fmt: off
  25650                         if (first_param_type.isGenericPoison() or (
  25651                                 first_param_type.zigTypeTag(mod) == .Pointer and
  25652                                 (first_param_type.ptrSize(mod) == .One or
  25653                                 first_param_type.ptrSize(mod) == .C) and
  25654                                 first_param_type.childType(mod).eql(concrete_ty, mod)))
  25655                         {
  25656                         // zig fmt: on
  25657                             // Note that if the param type is generic poison, we know that it must
  25658                             // specifically be `anytype` since it's the first parameter, meaning we
  25659                             // can safely assume it can be a pointer.
  25660                             // TODO: bound fn calls on rvalues should probably
  25661                             // generate a by-value argument somehow.
  25662                             return .{ .method = .{
  25663                                 .func_inst = decl_val,
  25664                                 .arg0_inst = object_ptr,
  25665                             } };
  25666                         } else if (first_param_type.eql(concrete_ty, mod)) {
  25667                             const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  25668                             return .{ .method = .{
  25669                                 .func_inst = decl_val,
  25670                                 .arg0_inst = deref,
  25671                             } };
  25672                         } else if (first_param_type.zigTypeTag(mod) == .Optional) {
  25673                             const child = first_param_type.optionalChild(mod);
  25674                             if (child.eql(concrete_ty, mod)) {
  25675                                 const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  25676                                 return .{ .method = .{
  25677                                     .func_inst = decl_val,
  25678                                     .arg0_inst = deref,
  25679                                 } };
  25680                             } else if (child.zigTypeTag(mod) == .Pointer and
  25681                                 child.ptrSize(mod) == .One and
  25682                                 child.childType(mod).eql(concrete_ty, mod))
  25683                             {
  25684                                 return .{ .method = .{
  25685                                     .func_inst = decl_val,
  25686                                     .arg0_inst = object_ptr,
  25687                                 } };
  25688                             }
  25689                         } else if (first_param_type.zigTypeTag(mod) == .ErrorUnion and
  25690                             first_param_type.errorUnionPayload(mod).eql(concrete_ty, mod))
  25691                         {
  25692                             const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  25693                             return .{ .method = .{
  25694                                 .func_inst = decl_val,
  25695                                 .arg0_inst = deref,
  25696                             } };
  25697                         }
  25698                     }
  25699                     break :found_decl decl_idx;
  25700                 }
  25701             }
  25702             break :found_decl null;
  25703         },
  25704         else => null,
  25705     };
  25706 
  25707     const msg = msg: {
  25708         const msg = try sema.errMsg(block, src, "no field or member function named '{}' in '{}'", .{
  25709             field_name.fmt(ip),
  25710             concrete_ty.fmt(mod),
  25711         });
  25712         errdefer msg.destroy(sema.gpa);
  25713         try sema.addDeclaredHereNote(msg, concrete_ty);
  25714         if (found_decl) |decl_idx| {
  25715             const decl = mod.declPtr(decl_idx);
  25716             try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "'{}' is not a member function", .{field_name.fmt(ip)});
  25717         }
  25718         break :msg msg;
  25719     };
  25720     return sema.failWithOwnedErrorMsg(msg);
  25721 }
  25722 
  25723 fn finishFieldCallBind(
  25724     sema: *Sema,
  25725     block: *Block,
  25726     src: LazySrcLoc,
  25727     ptr_ty: Type,
  25728     field_ty: Type,
  25729     field_index: u32,
  25730     object_ptr: Air.Inst.Ref,
  25731 ) CompileError!ResolvedFieldCallee {
  25732     const mod = sema.mod;
  25733     const ptr_field_ty = try mod.ptrType(.{
  25734         .child = field_ty.toIntern(),
  25735         .flags = .{
  25736             .is_const = !ptr_ty.ptrIsMutable(mod),
  25737             .address_space = ptr_ty.ptrAddressSpace(mod),
  25738         },
  25739     });
  25740 
  25741     const container_ty = ptr_ty.childType(mod);
  25742     if (container_ty.zigTypeTag(mod) == .Struct) {
  25743         if (try container_ty.structFieldValueComptime(mod, field_index)) |default_val| {
  25744             return .{ .direct = try sema.addConstant(default_val) };
  25745         }
  25746     }
  25747 
  25748     if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| {
  25749         const pointer = try sema.addConstant((try mod.intern(.{ .ptr = .{
  25750             .ty = ptr_field_ty.toIntern(),
  25751             .addr = .{ .field = .{
  25752                 .base = struct_ptr_val.toIntern(),
  25753                 .index = field_index,
  25754             } },
  25755         } })).toValue());
  25756         return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) };
  25757     }
  25758 
  25759     try sema.requireRuntimeBlock(block, src, null);
  25760     const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty);
  25761     return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) };
  25762 }
  25763 
  25764 fn namespaceLookup(
  25765     sema: *Sema,
  25766     block: *Block,
  25767     src: LazySrcLoc,
  25768     namespace: Namespace.Index,
  25769     decl_name: InternPool.NullTerminatedString,
  25770 ) CompileError!?Decl.Index {
  25771     const mod = sema.mod;
  25772     const gpa = sema.gpa;
  25773     if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
  25774         const decl = mod.declPtr(decl_index);
  25775         if (!decl.is_pub and decl.getFileScope(mod) != block.getFileScope(mod)) {
  25776             const msg = msg: {
  25777                 const msg = try sema.errMsg(block, src, "'{}' is not marked 'pub'", .{
  25778                     decl_name.fmt(&mod.intern_pool),
  25779                 });
  25780                 errdefer msg.destroy(gpa);
  25781                 try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "declared here", .{});
  25782                 break :msg msg;
  25783             };
  25784             return sema.failWithOwnedErrorMsg(msg);
  25785         }
  25786         return decl_index;
  25787     }
  25788     return null;
  25789 }
  25790 
  25791 fn namespaceLookupRef(
  25792     sema: *Sema,
  25793     block: *Block,
  25794     src: LazySrcLoc,
  25795     namespace: Namespace.Index,
  25796     decl_name: InternPool.NullTerminatedString,
  25797 ) CompileError!?Air.Inst.Ref {
  25798     const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
  25799     try sema.addReferencedBy(block, src, decl);
  25800     return try sema.analyzeDeclRef(decl);
  25801 }
  25802 
  25803 fn namespaceLookupVal(
  25804     sema: *Sema,
  25805     block: *Block,
  25806     src: LazySrcLoc,
  25807     namespace: Namespace.Index,
  25808     decl_name: InternPool.NullTerminatedString,
  25809 ) CompileError!?Air.Inst.Ref {
  25810     const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
  25811     return try sema.analyzeDeclVal(block, src, decl);
  25812 }
  25813 
  25814 fn structFieldPtr(
  25815     sema: *Sema,
  25816     block: *Block,
  25817     src: LazySrcLoc,
  25818     struct_ptr: Air.Inst.Ref,
  25819     field_name: InternPool.NullTerminatedString,
  25820     field_name_src: LazySrcLoc,
  25821     unresolved_struct_ty: Type,
  25822     initializing: bool,
  25823 ) CompileError!Air.Inst.Ref {
  25824     const mod = sema.mod;
  25825     assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct);
  25826 
  25827     const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty);
  25828     try sema.resolveStructLayout(struct_ty);
  25829 
  25830     if (struct_ty.isTuple(mod)) {
  25831         if (mod.intern_pool.stringEqlSlice(field_name, "len")) {
  25832             const len_inst = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount(mod));
  25833             return sema.analyzeRef(block, src, len_inst);
  25834         }
  25835         const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
  25836         return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing);
  25837     } else if (struct_ty.isAnonStruct(mod)) {
  25838         const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
  25839         return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing);
  25840     }
  25841 
  25842     const struct_obj = mod.typeToStruct(struct_ty).?;
  25843 
  25844     const field_index_big = struct_obj.fields.getIndex(field_name) orelse
  25845         return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
  25846     const field_index = @as(u32, @intCast(field_index_big));
  25847 
  25848     return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, field_name_src, struct_ty, initializing);
  25849 }
  25850 
  25851 fn structFieldPtrByIndex(
  25852     sema: *Sema,
  25853     block: *Block,
  25854     src: LazySrcLoc,
  25855     struct_ptr: Air.Inst.Ref,
  25856     field_index: u32,
  25857     field_src: LazySrcLoc,
  25858     struct_ty: Type,
  25859     initializing: bool,
  25860 ) CompileError!Air.Inst.Ref {
  25861     const mod = sema.mod;
  25862     if (struct_ty.isAnonStruct(mod)) {
  25863         return sema.tupleFieldPtr(block, src, struct_ptr, field_src, field_index, initializing);
  25864     }
  25865 
  25866     const struct_obj = mod.typeToStruct(struct_ty).?;
  25867     const field = struct_obj.fields.values()[field_index];
  25868     const struct_ptr_ty = sema.typeOf(struct_ptr);
  25869     const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod);
  25870 
  25871     var ptr_ty_data: InternPool.Key.PtrType = .{
  25872         .child = field.ty.toIntern(),
  25873         .flags = .{
  25874             .is_const = struct_ptr_ty_info.flags.is_const,
  25875             .is_volatile = struct_ptr_ty_info.flags.is_volatile,
  25876             .address_space = struct_ptr_ty_info.flags.address_space,
  25877         },
  25878     };
  25879 
  25880     const target = mod.getTarget();
  25881 
  25882     const parent_align = struct_ptr_ty_info.flags.alignment.toByteUnitsOptional() orelse
  25883         try sema.typeAbiAlignment(struct_ptr_ty_info.child.toType());
  25884 
  25885     if (struct_obj.layout == .Packed) {
  25886         comptime assert(Type.packed_struct_layout_version == 2);
  25887 
  25888         var running_bits: u16 = 0;
  25889         for (struct_obj.fields.values(), 0..) |f, i| {
  25890             if (!(try sema.typeHasRuntimeBits(f.ty))) continue;
  25891 
  25892             if (i == field_index) {
  25893                 ptr_ty_data.packed_offset.bit_offset = running_bits;
  25894             }
  25895             running_bits += @as(u16, @intCast(f.ty.bitSize(mod)));
  25896         }
  25897         ptr_ty_data.packed_offset.host_size = (running_bits + 7) / 8;
  25898 
  25899         // If this is a packed struct embedded in another one, we need to offset
  25900         // the bits against each other.
  25901         if (struct_ptr_ty_info.packed_offset.host_size != 0) {
  25902             ptr_ty_data.packed_offset.host_size = struct_ptr_ty_info.packed_offset.host_size;
  25903             ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset;
  25904         }
  25905 
  25906         ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align);
  25907 
  25908         // If the field happens to be byte-aligned, simplify the pointer type.
  25909         // The pointee type bit size must match its ABI byte size so that loads and stores
  25910         // do not interfere with the surrounding packed bits.
  25911         // We do not attempt this with big-endian targets yet because of nested
  25912         // structs and floats. I need to double-check the desired behavior for big endian
  25913         // targets before adding the necessary complications to this code. This will not
  25914         // cause miscompilations; it only means the field pointer uses bit masking when it
  25915         // might not be strictly necessary.
  25916         if (parent_align != 0 and ptr_ty_data.packed_offset.bit_offset % 8 == 0 and
  25917             target.cpu.arch.endian() == .Little)
  25918         {
  25919             const elem_size_bytes = ptr_ty_data.child.toType().abiSize(mod);
  25920             const elem_size_bits = ptr_ty_data.child.toType().bitSize(mod);
  25921             if (elem_size_bytes * 8 == elem_size_bits) {
  25922                 const byte_offset = ptr_ty_data.packed_offset.bit_offset / 8;
  25923                 const new_align = @as(Alignment, @enumFromInt(@ctz(byte_offset | parent_align)));
  25924                 assert(new_align != .none);
  25925                 ptr_ty_data.flags.alignment = new_align;
  25926                 ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 };
  25927             }
  25928         }
  25929     } else if (struct_obj.layout == .Extern) {
  25930         // For extern structs, field aligment might be bigger than type's natural alignment. Eg, in
  25931         // `extern struct { x: u32, y: u16 }` the second field is aligned as u32.
  25932         const field_offset = struct_ty.structFieldOffset(field_index, mod);
  25933         ptr_ty_data.flags.alignment = Alignment.fromByteUnits(
  25934             if (parent_align == 0) 0 else std.math.gcd(field_offset, parent_align),
  25935         );
  25936     } else {
  25937         // Our alignment is capped at the field alignment
  25938         const field_align = try sema.structFieldAlignment(field, struct_obj.layout);
  25939         ptr_ty_data.flags.alignment = Alignment.fromByteUnits(@min(field_align, parent_align));
  25940     }
  25941 
  25942     const ptr_field_ty = try mod.ptrType(ptr_ty_data);
  25943 
  25944     if (field.is_comptime) {
  25945         const val = try mod.intern(.{ .ptr = .{
  25946             .ty = ptr_field_ty.toIntern(),
  25947             .addr = .{ .comptime_field = field.default_val },
  25948         } });
  25949         return sema.addConstant(val.toValue());
  25950     }
  25951 
  25952     if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
  25953         const val = try mod.intern(.{ .ptr = .{
  25954             .ty = ptr_field_ty.toIntern(),
  25955             .addr = .{ .field = .{
  25956                 .base = try struct_ptr_val.intern(struct_ptr_ty, mod),
  25957                 .index = field_index,
  25958             } },
  25959         } });
  25960         return sema.addConstant(val.toValue());
  25961     }
  25962 
  25963     try sema.requireRuntimeBlock(block, src, null);
  25964     return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty);
  25965 }
  25966 
  25967 fn structFieldVal(
  25968     sema: *Sema,
  25969     block: *Block,
  25970     src: LazySrcLoc,
  25971     struct_byval: Air.Inst.Ref,
  25972     field_name: InternPool.NullTerminatedString,
  25973     field_name_src: LazySrcLoc,
  25974     unresolved_struct_ty: Type,
  25975 ) CompileError!Air.Inst.Ref {
  25976     const mod = sema.mod;
  25977     assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct);
  25978 
  25979     const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty);
  25980     switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) {
  25981         .struct_type => |struct_type| {
  25982             const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
  25983             if (struct_obj.is_tuple) return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty);
  25984 
  25985             const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
  25986                 return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
  25987             const field_index = @as(u32, @intCast(field_index_usize));
  25988             const field = struct_obj.fields.values()[field_index];
  25989 
  25990             if (field.is_comptime) {
  25991                 return sema.addConstant(field.default_val.toValue());
  25992             }
  25993 
  25994             if (try sema.resolveMaybeUndefVal(struct_byval)) |struct_val| {
  25995                 if (struct_val.isUndef(mod)) return sema.addConstUndef(field.ty);
  25996                 if ((try sema.typeHasOnePossibleValue(field.ty))) |opv| {
  25997                     return sema.addConstant(opv);
  25998                 }
  25999                 return sema.addConstant(try struct_val.fieldValue(mod, field_index));
  26000             }
  26001 
  26002             try sema.requireRuntimeBlock(block, src, null);
  26003             return block.addStructFieldVal(struct_byval, field_index, field.ty);
  26004         },
  26005         .anon_struct_type => |anon_struct| {
  26006             if (anon_struct.names.len == 0) {
  26007                 return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty);
  26008             } else {
  26009                 const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
  26010                 return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty);
  26011             }
  26012         },
  26013         else => unreachable,
  26014     }
  26015 }
  26016 
  26017 fn tupleFieldVal(
  26018     sema: *Sema,
  26019     block: *Block,
  26020     src: LazySrcLoc,
  26021     tuple_byval: Air.Inst.Ref,
  26022     field_name: InternPool.NullTerminatedString,
  26023     field_name_src: LazySrcLoc,
  26024     tuple_ty: Type,
  26025 ) CompileError!Air.Inst.Ref {
  26026     const mod = sema.mod;
  26027     if (mod.intern_pool.stringEqlSlice(field_name, "len")) {
  26028         return sema.addIntUnsigned(Type.usize, tuple_ty.structFieldCount(mod));
  26029     }
  26030     const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src);
  26031     return sema.tupleFieldValByIndex(block, src, tuple_byval, field_index, tuple_ty);
  26032 }
  26033 
  26034 /// Asserts that `field_name` is not "len".
  26035 fn tupleFieldIndex(
  26036     sema: *Sema,
  26037     block: *Block,
  26038     tuple_ty: Type,
  26039     field_name: InternPool.NullTerminatedString,
  26040     field_name_src: LazySrcLoc,
  26041 ) CompileError!u32 {
  26042     const mod = sema.mod;
  26043     assert(!mod.intern_pool.stringEqlSlice(field_name, "len"));
  26044     if (field_name.toUnsigned(&mod.intern_pool)) |field_index| {
  26045         if (field_index < tuple_ty.structFieldCount(mod)) return field_index;
  26046         return sema.fail(block, field_name_src, "index '{}' out of bounds of tuple '{}'", .{
  26047             field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod),
  26048         });
  26049     }
  26050 
  26051     return sema.fail(block, field_name_src, "no field named '{}' in tuple '{}'", .{
  26052         field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod),
  26053     });
  26054 }
  26055 
  26056 fn tupleFieldValByIndex(
  26057     sema: *Sema,
  26058     block: *Block,
  26059     src: LazySrcLoc,
  26060     tuple_byval: Air.Inst.Ref,
  26061     field_index: u32,
  26062     tuple_ty: Type,
  26063 ) CompileError!Air.Inst.Ref {
  26064     const mod = sema.mod;
  26065     const field_ty = tuple_ty.structFieldType(field_index, mod);
  26066 
  26067     if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| {
  26068         return sema.addConstant(default_value);
  26069     }
  26070 
  26071     if (try sema.resolveMaybeUndefVal(tuple_byval)) |tuple_val| {
  26072         if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| {
  26073             return sema.addConstant(opv);
  26074         }
  26075         return switch (mod.intern_pool.indexToKey(tuple_val.toIntern())) {
  26076             .undef => sema.addConstUndef(field_ty),
  26077             .aggregate => |aggregate| sema.addConstant(switch (aggregate.storage) {
  26078                 .bytes => |bytes| try mod.intValue(Type.u8, bytes[0]),
  26079                 .elems => |elems| elems[field_index].toValue(),
  26080                 .repeated_elem => |elem| elem.toValue(),
  26081             }),
  26082             else => unreachable,
  26083         };
  26084     }
  26085 
  26086     if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| {
  26087         return sema.addConstant(default_val);
  26088     }
  26089 
  26090     try sema.requireRuntimeBlock(block, src, null);
  26091     return block.addStructFieldVal(tuple_byval, field_index, field_ty);
  26092 }
  26093 
  26094 fn unionFieldPtr(
  26095     sema: *Sema,
  26096     block: *Block,
  26097     src: LazySrcLoc,
  26098     union_ptr: Air.Inst.Ref,
  26099     field_name: InternPool.NullTerminatedString,
  26100     field_name_src: LazySrcLoc,
  26101     unresolved_union_ty: Type,
  26102     initializing: bool,
  26103 ) CompileError!Air.Inst.Ref {
  26104     const mod = sema.mod;
  26105     const ip = &mod.intern_pool;
  26106 
  26107     assert(unresolved_union_ty.zigTypeTag(mod) == .Union);
  26108 
  26109     const union_ptr_ty = sema.typeOf(union_ptr);
  26110     const union_ptr_info = union_ptr_ty.ptrInfo(mod);
  26111     const union_ty = try sema.resolveTypeFields(unresolved_union_ty);
  26112     const union_obj = mod.typeToUnion(union_ty).?;
  26113     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
  26114     const field = union_obj.fields.values()[field_index];
  26115     const ptr_field_ty = try mod.ptrType(.{
  26116         .child = field.ty.toIntern(),
  26117         .flags = .{
  26118             .is_const = union_ptr_info.flags.is_const,
  26119             .is_volatile = union_ptr_info.flags.is_volatile,
  26120             .address_space = union_ptr_info.flags.address_space,
  26121             .alignment = if (union_obj.layout == .Auto) blk: {
  26122                 const union_align = union_ptr_info.flags.alignment.toByteUnitsOptional() orelse try sema.typeAbiAlignment(union_ty);
  26123                 const field_align = try sema.unionFieldAlignment(field);
  26124                 break :blk InternPool.Alignment.fromByteUnits(@min(union_align, field_align));
  26125             } else union_ptr_info.flags.alignment,
  26126         },
  26127         .packed_offset = union_ptr_info.packed_offset,
  26128     });
  26129     const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?));
  26130 
  26131     if (initializing and field.ty.zigTypeTag(mod) == .NoReturn) {
  26132         const msg = msg: {
  26133             const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{});
  26134             errdefer msg.destroy(sema.gpa);
  26135 
  26136             try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{
  26137                 field_name.fmt(ip),
  26138             });
  26139             try sema.addDeclaredHereNote(msg, union_ty);
  26140             break :msg msg;
  26141         };
  26142         return sema.failWithOwnedErrorMsg(msg);
  26143     }
  26144 
  26145     if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
  26146         switch (union_obj.layout) {
  26147             .Auto => if (!initializing) {
  26148                 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse
  26149                     break :ct;
  26150                 if (union_val.isUndef(mod)) {
  26151                     return sema.failWithUseOfUndef(block, src);
  26152                 }
  26153                 const un = ip.indexToKey(union_val.toIntern()).un;
  26154                 const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index);
  26155                 const tag_matches = un.tag == field_tag.toIntern();
  26156                 if (!tag_matches) {
  26157                     const msg = msg: {
  26158                         const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?;
  26159                         const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod);
  26160                         const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{
  26161                             field_name.fmt(ip),
  26162                             active_field_name.fmt(ip),
  26163                         });
  26164                         errdefer msg.destroy(sema.gpa);
  26165                         try sema.addDeclaredHereNote(msg, union_ty);
  26166                         break :msg msg;
  26167                     };
  26168                     return sema.failWithOwnedErrorMsg(msg);
  26169                 }
  26170             },
  26171             .Packed, .Extern => {},
  26172         }
  26173         return sema.addConstant((try mod.intern(.{ .ptr = .{
  26174             .ty = ptr_field_ty.toIntern(),
  26175             .addr = .{ .field = .{
  26176                 .base = union_ptr_val.toIntern(),
  26177                 .index = field_index,
  26178             } },
  26179         } })).toValue());
  26180     }
  26181 
  26182     try sema.requireRuntimeBlock(block, src, null);
  26183     if (!initializing and union_obj.layout == .Auto and block.wantSafety() and
  26184         union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1)
  26185     {
  26186         const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index);
  26187         const wanted_tag = try sema.addConstant(wanted_tag_val);
  26188         // TODO would it be better if get_union_tag supported pointers to unions?
  26189         const union_val = try block.addTyOp(.load, union_ty, union_ptr);
  26190         const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val);
  26191         try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
  26192     }
  26193     if (field.ty.zigTypeTag(mod) == .NoReturn) {
  26194         _ = try block.addNoOp(.unreach);
  26195         return Air.Inst.Ref.unreachable_value;
  26196     }
  26197     return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);
  26198 }
  26199 
  26200 fn unionFieldVal(
  26201     sema: *Sema,
  26202     block: *Block,
  26203     src: LazySrcLoc,
  26204     union_byval: Air.Inst.Ref,
  26205     field_name: InternPool.NullTerminatedString,
  26206     field_name_src: LazySrcLoc,
  26207     unresolved_union_ty: Type,
  26208 ) CompileError!Air.Inst.Ref {
  26209     const mod = sema.mod;
  26210     const ip = &mod.intern_pool;
  26211     assert(unresolved_union_ty.zigTypeTag(mod) == .Union);
  26212 
  26213     const union_ty = try sema.resolveTypeFields(unresolved_union_ty);
  26214     const union_obj = mod.typeToUnion(union_ty).?;
  26215     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
  26216     const field = union_obj.fields.values()[field_index];
  26217     const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?));
  26218 
  26219     if (try sema.resolveMaybeUndefVal(union_byval)) |union_val| {
  26220         if (union_val.isUndef(mod)) return sema.addConstUndef(field.ty);
  26221 
  26222         const un = ip.indexToKey(union_val.toIntern()).un;
  26223         const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index);
  26224         const tag_matches = un.tag == field_tag.toIntern();
  26225         switch (union_obj.layout) {
  26226             .Auto => {
  26227                 if (tag_matches) {
  26228                     return sema.addConstant(un.val.toValue());
  26229                 } else {
  26230                     const msg = msg: {
  26231                         const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?;
  26232                         const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod);
  26233                         const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{
  26234                             field_name.fmt(ip), active_field_name.fmt(ip),
  26235                         });
  26236                         errdefer msg.destroy(sema.gpa);
  26237                         try sema.addDeclaredHereNote(msg, union_ty);
  26238                         break :msg msg;
  26239                     };
  26240                     return sema.failWithOwnedErrorMsg(msg);
  26241                 }
  26242             },
  26243             .Packed, .Extern => {
  26244                 if (tag_matches) {
  26245                     return sema.addConstant(un.val.toValue());
  26246                 } else {
  26247                     const old_ty = union_ty.unionFieldType(un.tag.toValue(), mod);
  26248                     if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field.ty, 0)) |new_val| {
  26249                         return sema.addConstant(new_val);
  26250                     }
  26251                 }
  26252             },
  26253         }
  26254     }
  26255 
  26256     try sema.requireRuntimeBlock(block, src, null);
  26257     if (union_obj.layout == .Auto and block.wantSafety() and
  26258         union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1)
  26259     {
  26260         const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index);
  26261         const wanted_tag = try sema.addConstant(wanted_tag_val);
  26262         const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval);
  26263         try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
  26264     }
  26265     if (field.ty.zigTypeTag(mod) == .NoReturn) {
  26266         _ = try block.addNoOp(.unreach);
  26267         return Air.Inst.Ref.unreachable_value;
  26268     }
  26269     return block.addStructFieldVal(union_byval, field_index, field.ty);
  26270 }
  26271 
  26272 fn elemPtr(
  26273     sema: *Sema,
  26274     block: *Block,
  26275     src: LazySrcLoc,
  26276     indexable_ptr: Air.Inst.Ref,
  26277     elem_index: Air.Inst.Ref,
  26278     elem_index_src: LazySrcLoc,
  26279     init: bool,
  26280     oob_safety: bool,
  26281 ) CompileError!Air.Inst.Ref {
  26282     const mod = sema.mod;
  26283     const indexable_ptr_src = src; // TODO better source location
  26284     const indexable_ptr_ty = sema.typeOf(indexable_ptr);
  26285 
  26286     const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(mod)) {
  26287         .Pointer => indexable_ptr_ty.childType(mod),
  26288         else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(mod)}),
  26289     };
  26290     try checkIndexable(sema, block, src, indexable_ty);
  26291 
  26292     switch (indexable_ty.zigTypeTag(mod)) {
  26293         .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
  26294         .Struct => {
  26295             // Tuple field access.
  26296             const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
  26297             const index = @as(u32, @intCast(index_val.toUnsignedInt(mod)));
  26298             return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
  26299         },
  26300         else => {
  26301             const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
  26302             return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety);
  26303         },
  26304     }
  26305 }
  26306 
  26307 /// Asserts that the type of indexable is pointer.
  26308 fn elemPtrOneLayerOnly(
  26309     sema: *Sema,
  26310     block: *Block,
  26311     src: LazySrcLoc,
  26312     indexable: Air.Inst.Ref,
  26313     elem_index: Air.Inst.Ref,
  26314     elem_index_src: LazySrcLoc,
  26315     init: bool,
  26316     oob_safety: bool,
  26317 ) CompileError!Air.Inst.Ref {
  26318     const indexable_src = src; // TODO better source location
  26319     const indexable_ty = sema.typeOf(indexable);
  26320     const mod = sema.mod;
  26321 
  26322     try checkIndexable(sema, block, src, indexable_ty);
  26323 
  26324     switch (indexable_ty.ptrSize(mod)) {
  26325         .Slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  26326         .Many, .C => {
  26327             const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
  26328             const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  26329             const runtime_src = rs: {
  26330                 const ptr_val = maybe_ptr_val orelse break :rs indexable_src;
  26331                 const index_val = maybe_index_val orelse break :rs elem_index_src;
  26332                 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod)));
  26333                 const result_ty = try sema.elemPtrType(indexable_ty, index);
  26334                 const elem_ptr = try ptr_val.elemPtr(result_ty, index, mod);
  26335                 return sema.addConstant(elem_ptr);
  26336             };
  26337             const result_ty = try sema.elemPtrType(indexable_ty, null);
  26338 
  26339             try sema.requireRuntimeBlock(block, src, runtime_src);
  26340             return block.addPtrElemPtr(indexable, elem_index, result_ty);
  26341         },
  26342         .One => {
  26343             const child_ty = indexable_ty.childType(mod);
  26344             switch (child_ty.zigTypeTag(mod)) {
  26345                 .Array, .Vector => {
  26346                     return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety);
  26347                 },
  26348                 .Struct => {
  26349                     assert(child_ty.isTuple(mod));
  26350                     const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
  26351                     const index = @as(u32, @intCast(index_val.toUnsignedInt(mod)));
  26352                     return sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false);
  26353                 },
  26354                 else => unreachable, // Guaranteed by checkIndexable
  26355             }
  26356         },
  26357     }
  26358 }
  26359 
  26360 fn elemVal(
  26361     sema: *Sema,
  26362     block: *Block,
  26363     src: LazySrcLoc,
  26364     indexable: Air.Inst.Ref,
  26365     elem_index_uncasted: Air.Inst.Ref,
  26366     elem_index_src: LazySrcLoc,
  26367     oob_safety: bool,
  26368 ) CompileError!Air.Inst.Ref {
  26369     const indexable_src = src; // TODO better source location
  26370     const indexable_ty = sema.typeOf(indexable);
  26371     const mod = sema.mod;
  26372 
  26373     try checkIndexable(sema, block, src, indexable_ty);
  26374 
  26375     // TODO in case of a vector of pointers, we need to detect whether the element
  26376     // index is a scalar or vector instead of unconditionally casting to usize.
  26377     const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src);
  26378 
  26379     switch (indexable_ty.zigTypeTag(mod)) {
  26380         .Pointer => switch (indexable_ty.ptrSize(mod)) {
  26381             .Slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  26382             .Many, .C => {
  26383                 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
  26384                 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  26385 
  26386                 const runtime_src = rs: {
  26387                     const indexable_val = maybe_indexable_val orelse break :rs indexable_src;
  26388                     const index_val = maybe_index_val orelse break :rs elem_index_src;
  26389                     const index = @as(usize, @intCast(index_val.toUnsignedInt(mod)));
  26390                     const elem_ty = indexable_ty.elemType2(mod);
  26391                     const many_ptr_ty = try mod.manyConstPtrType(elem_ty);
  26392                     const many_ptr_val = try mod.getCoerced(indexable_val, many_ptr_ty);
  26393                     const elem_ptr_ty = try mod.singleConstPtrType(elem_ty);
  26394                     const elem_ptr_val = try many_ptr_val.elemPtr(elem_ptr_ty, index, mod);
  26395                     if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty)) |elem_val| {
  26396                         return sema.addConstant(try mod.getCoerced(elem_val, elem_ty));
  26397                     }
  26398                     break :rs indexable_src;
  26399                 };
  26400 
  26401                 try sema.requireRuntimeBlock(block, src, runtime_src);
  26402                 return block.addBinOp(.ptr_elem_val, indexable, elem_index);
  26403             },
  26404             .One => {
  26405                 arr_sent: {
  26406                     const inner_ty = indexable_ty.childType(mod);
  26407                     if (inner_ty.zigTypeTag(mod) != .Array) break :arr_sent;
  26408                     const sentinel = inner_ty.sentinel(mod) orelse break :arr_sent;
  26409                     const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent;
  26410                     const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(mod));
  26411                     if (index != inner_ty.arrayLen(mod)) break :arr_sent;
  26412                     return sema.addConstant(sentinel);
  26413                 }
  26414                 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);
  26415                 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
  26416             },
  26417         },
  26418         .Array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  26419         .Vector => {
  26420             // TODO: If the index is a vector, the result should be a vector.
  26421             return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety);
  26422         },
  26423         .Struct => {
  26424             // Tuple field access.
  26425             const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
  26426             const index = @as(u32, @intCast(index_val.toUnsignedInt(mod)));
  26427             return sema.tupleField(block, indexable_src, indexable, elem_index_src, index);
  26428         },
  26429         else => unreachable,
  26430     }
  26431 }
  26432 
  26433 fn validateRuntimeElemAccess(
  26434     sema: *Sema,
  26435     block: *Block,
  26436     elem_index_src: LazySrcLoc,
  26437     elem_ty: Type,
  26438     parent_ty: Type,
  26439     parent_src: LazySrcLoc,
  26440 ) CompileError!void {
  26441     const mod = sema.mod;
  26442     const valid_rt = try sema.validateRunTimeType(elem_ty, false);
  26443     if (!valid_rt) {
  26444         const msg = msg: {
  26445             const msg = try sema.errMsg(
  26446                 block,
  26447                 elem_index_src,
  26448                 "values of type '{}' must be comptime-known, but index value is runtime-known",
  26449                 .{parent_ty.fmt(mod)},
  26450             );
  26451             errdefer msg.destroy(sema.gpa);
  26452 
  26453             const src_decl = mod.declPtr(block.src_decl);
  26454             try sema.explainWhyTypeIsComptime(msg, parent_src.toSrcLoc(src_decl, mod), parent_ty);
  26455 
  26456             break :msg msg;
  26457         };
  26458         return sema.failWithOwnedErrorMsg(msg);
  26459     }
  26460 }
  26461 
  26462 fn tupleFieldPtr(
  26463     sema: *Sema,
  26464     block: *Block,
  26465     tuple_ptr_src: LazySrcLoc,
  26466     tuple_ptr: Air.Inst.Ref,
  26467     field_index_src: LazySrcLoc,
  26468     field_index: u32,
  26469     init: bool,
  26470 ) CompileError!Air.Inst.Ref {
  26471     const mod = sema.mod;
  26472     const tuple_ptr_ty = sema.typeOf(tuple_ptr);
  26473     const tuple_ty = tuple_ptr_ty.childType(mod);
  26474     _ = try sema.resolveTypeFields(tuple_ty);
  26475     const field_count = tuple_ty.structFieldCount(mod);
  26476 
  26477     if (field_count == 0) {
  26478         return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});
  26479     }
  26480 
  26481     if (field_index >= field_count) {
  26482         return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
  26483             field_index, field_count,
  26484         });
  26485     }
  26486 
  26487     const field_ty = tuple_ty.structFieldType(field_index, mod);
  26488     const ptr_field_ty = try mod.ptrType(.{
  26489         .child = field_ty.toIntern(),
  26490         .flags = .{
  26491             .is_const = !tuple_ptr_ty.ptrIsMutable(mod),
  26492             .is_volatile = tuple_ptr_ty.isVolatilePtr(mod),
  26493             .address_space = tuple_ptr_ty.ptrAddressSpace(mod),
  26494         },
  26495     });
  26496 
  26497     if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| {
  26498         return sema.addConstant((try mod.intern(.{ .ptr = .{
  26499             .ty = ptr_field_ty.toIntern(),
  26500             .addr = .{ .comptime_field = default_val.toIntern() },
  26501         } })).toValue());
  26502     }
  26503 
  26504     if (try sema.resolveMaybeUndefVal(tuple_ptr)) |tuple_ptr_val| {
  26505         return sema.addConstant((try mod.intern(.{ .ptr = .{
  26506             .ty = ptr_field_ty.toIntern(),
  26507             .addr = .{ .field = .{
  26508                 .base = tuple_ptr_val.toIntern(),
  26509                 .index = field_index,
  26510             } },
  26511         } })).toValue());
  26512     }
  26513 
  26514     if (!init) {
  26515         try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src);
  26516     }
  26517 
  26518     try sema.requireRuntimeBlock(block, tuple_ptr_src, null);
  26519     return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);
  26520 }
  26521 
  26522 fn tupleField(
  26523     sema: *Sema,
  26524     block: *Block,
  26525     tuple_src: LazySrcLoc,
  26526     tuple: Air.Inst.Ref,
  26527     field_index_src: LazySrcLoc,
  26528     field_index: u32,
  26529 ) CompileError!Air.Inst.Ref {
  26530     const mod = sema.mod;
  26531     const tuple_ty = try sema.resolveTypeFields(sema.typeOf(tuple));
  26532     const field_count = tuple_ty.structFieldCount(mod);
  26533 
  26534     if (field_count == 0) {
  26535         return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{});
  26536     }
  26537 
  26538     if (field_index >= field_count) {
  26539         return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
  26540             field_index, field_count,
  26541         });
  26542     }
  26543 
  26544     const field_ty = tuple_ty.structFieldType(field_index, mod);
  26545 
  26546     if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| {
  26547         return sema.addConstant(default_value); // comptime field
  26548     }
  26549 
  26550     if (try sema.resolveMaybeUndefVal(tuple)) |tuple_val| {
  26551         if (tuple_val.isUndef(mod)) return sema.addConstUndef(field_ty);
  26552         return sema.addConstant(try tuple_val.fieldValue(mod, field_index));
  26553     }
  26554 
  26555     try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src);
  26556 
  26557     try sema.requireRuntimeBlock(block, tuple_src, null);
  26558     return block.addStructFieldVal(tuple, field_index, field_ty);
  26559 }
  26560 
  26561 fn elemValArray(
  26562     sema: *Sema,
  26563     block: *Block,
  26564     src: LazySrcLoc,
  26565     array_src: LazySrcLoc,
  26566     array: Air.Inst.Ref,
  26567     elem_index_src: LazySrcLoc,
  26568     elem_index: Air.Inst.Ref,
  26569     oob_safety: bool,
  26570 ) CompileError!Air.Inst.Ref {
  26571     const mod = sema.mod;
  26572     const array_ty = sema.typeOf(array);
  26573     const array_sent = array_ty.sentinel(mod);
  26574     const array_len = array_ty.arrayLen(mod);
  26575     const array_len_s = array_len + @intFromBool(array_sent != null);
  26576     const elem_ty = array_ty.childType(mod);
  26577 
  26578     if (array_len_s == 0) {
  26579         return sema.fail(block, array_src, "indexing into empty array is not allowed", .{});
  26580     }
  26581 
  26582     const maybe_undef_array_val = try sema.resolveMaybeUndefVal(array);
  26583     // index must be defined since it can access out of bounds
  26584     const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  26585 
  26586     if (maybe_index_val) |index_val| {
  26587         const index = @as(usize, @intCast(index_val.toUnsignedInt(mod)));
  26588         if (array_sent) |s| {
  26589             if (index == array_len) {
  26590                 return sema.addConstant(s);
  26591             }
  26592         }
  26593         if (index >= array_len_s) {
  26594             const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else "";
  26595             return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
  26596         }
  26597     }
  26598     if (maybe_undef_array_val) |array_val| {
  26599         if (array_val.isUndef(mod)) {
  26600             return sema.addConstUndef(elem_ty);
  26601         }
  26602         if (maybe_index_val) |index_val| {
  26603             const index = @as(usize, @intCast(index_val.toUnsignedInt(mod)));
  26604             const elem_val = try array_val.elemValue(mod, index);
  26605             return sema.addConstant(elem_val);
  26606         }
  26607     }
  26608 
  26609     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src);
  26610 
  26611     const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src;
  26612     try sema.requireRuntimeBlock(block, src, runtime_src);
  26613     if (oob_safety and block.wantSafety()) {
  26614         // Runtime check is only needed if unable to comptime check
  26615         if (maybe_index_val == null) {
  26616             const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
  26617             const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
  26618             try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
  26619         }
  26620     }
  26621     return block.addBinOp(.array_elem_val, array, elem_index);
  26622 }
  26623 
  26624 fn elemPtrArray(
  26625     sema: *Sema,
  26626     block: *Block,
  26627     src: LazySrcLoc,
  26628     array_ptr_src: LazySrcLoc,
  26629     array_ptr: Air.Inst.Ref,
  26630     elem_index_src: LazySrcLoc,
  26631     elem_index: Air.Inst.Ref,
  26632     init: bool,
  26633     oob_safety: bool,
  26634 ) CompileError!Air.Inst.Ref {
  26635     const mod = sema.mod;
  26636     const array_ptr_ty = sema.typeOf(array_ptr);
  26637     const array_ty = array_ptr_ty.childType(mod);
  26638     const array_sent = array_ty.sentinel(mod) != null;
  26639     const array_len = array_ty.arrayLen(mod);
  26640     const array_len_s = array_len + @intFromBool(array_sent);
  26641 
  26642     if (array_len_s == 0) {
  26643         return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{});
  26644     }
  26645 
  26646     const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(array_ptr);
  26647     // The index must not be undefined since it can be out of bounds.
  26648     const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
  26649         const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod));
  26650         if (index >= array_len_s) {
  26651             const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
  26652             return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
  26653         }
  26654         break :o index;
  26655     } else null;
  26656 
  26657     const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset);
  26658 
  26659     if (maybe_undef_array_ptr_val) |array_ptr_val| {
  26660         if (array_ptr_val.isUndef(mod)) {
  26661             return sema.addConstUndef(elem_ptr_ty);
  26662         }
  26663         if (offset) |index| {
  26664             const elem_ptr = try array_ptr_val.elemPtr(elem_ptr_ty, index, mod);
  26665             return sema.addConstant(elem_ptr);
  26666         }
  26667     }
  26668 
  26669     if (!init) {
  26670         try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(mod), array_ty, array_ptr_src);
  26671     }
  26672 
  26673     const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src;
  26674     try sema.requireRuntimeBlock(block, src, runtime_src);
  26675 
  26676     // Runtime check is only needed if unable to comptime check.
  26677     if (oob_safety and block.wantSafety() and offset == null) {
  26678         const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
  26679         const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
  26680         try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
  26681     }
  26682 
  26683     return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
  26684 }
  26685 
  26686 fn elemValSlice(
  26687     sema: *Sema,
  26688     block: *Block,
  26689     src: LazySrcLoc,
  26690     slice_src: LazySrcLoc,
  26691     slice: Air.Inst.Ref,
  26692     elem_index_src: LazySrcLoc,
  26693     elem_index: Air.Inst.Ref,
  26694     oob_safety: bool,
  26695 ) CompileError!Air.Inst.Ref {
  26696     const mod = sema.mod;
  26697     const slice_ty = sema.typeOf(slice);
  26698     const slice_sent = slice_ty.sentinel(mod) != null;
  26699     const elem_ty = slice_ty.elemType2(mod);
  26700     var runtime_src = slice_src;
  26701 
  26702     // slice must be defined since it can dereferenced as null
  26703     const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice);
  26704     // index must be defined since it can index out of bounds
  26705     const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  26706 
  26707     if (maybe_slice_val) |slice_val| {
  26708         runtime_src = elem_index_src;
  26709         const slice_len = slice_val.sliceLen(mod);
  26710         const slice_len_s = slice_len + @intFromBool(slice_sent);
  26711         if (slice_len_s == 0) {
  26712             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
  26713         }
  26714         if (maybe_index_val) |index_val| {
  26715             const index = @as(usize, @intCast(index_val.toUnsignedInt(mod)));
  26716             if (index >= slice_len_s) {
  26717                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
  26718                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
  26719             }
  26720             const elem_ptr_ty = try sema.elemPtrType(slice_ty, index);
  26721             const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod);
  26722             if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| {
  26723                 return sema.addConstant(elem_val);
  26724             }
  26725             runtime_src = slice_src;
  26726         }
  26727     }
  26728 
  26729     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src);
  26730 
  26731     try sema.requireRuntimeBlock(block, src, runtime_src);
  26732     if (oob_safety and block.wantSafety()) {
  26733         const len_inst = if (maybe_slice_val) |slice_val|
  26734             try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod))
  26735         else
  26736             try block.addTyOp(.slice_len, Type.usize, slice);
  26737         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
  26738         try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
  26739     }
  26740     try sema.queueFullTypeResolution(sema.typeOf(slice));
  26741     return block.addBinOp(.slice_elem_val, slice, elem_index);
  26742 }
  26743 
  26744 fn elemPtrSlice(
  26745     sema: *Sema,
  26746     block: *Block,
  26747     src: LazySrcLoc,
  26748     slice_src: LazySrcLoc,
  26749     slice: Air.Inst.Ref,
  26750     elem_index_src: LazySrcLoc,
  26751     elem_index: Air.Inst.Ref,
  26752     oob_safety: bool,
  26753 ) CompileError!Air.Inst.Ref {
  26754     const mod = sema.mod;
  26755     const slice_ty = sema.typeOf(slice);
  26756     const slice_sent = slice_ty.sentinel(mod) != null;
  26757 
  26758     const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(slice);
  26759     // The index must not be undefined since it can be out of bounds.
  26760     const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
  26761         const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod));
  26762         break :o index;
  26763     } else null;
  26764 
  26765     const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset);
  26766 
  26767     if (maybe_undef_slice_val) |slice_val| {
  26768         if (slice_val.isUndef(mod)) {
  26769             return sema.addConstUndef(elem_ptr_ty);
  26770         }
  26771         const slice_len = slice_val.sliceLen(mod);
  26772         const slice_len_s = slice_len + @intFromBool(slice_sent);
  26773         if (slice_len_s == 0) {
  26774             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
  26775         }
  26776         if (offset) |index| {
  26777             if (index >= slice_len_s) {
  26778                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
  26779                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
  26780             }
  26781             const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod);
  26782             return sema.addConstant(elem_ptr_val);
  26783         }
  26784     }
  26785 
  26786     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src);
  26787 
  26788     const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src;
  26789     try sema.requireRuntimeBlock(block, src, runtime_src);
  26790     if (oob_safety and block.wantSafety()) {
  26791         const len_inst = len: {
  26792             if (maybe_undef_slice_val) |slice_val|
  26793                 if (!slice_val.isUndef(mod))
  26794                     break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod));
  26795             break :len try block.addTyOp(.slice_len, Type.usize, slice);
  26796         };
  26797         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
  26798         try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
  26799     }
  26800     return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
  26801 }
  26802 
  26803 fn coerce(
  26804     sema: *Sema,
  26805     block: *Block,
  26806     dest_ty_unresolved: Type,
  26807     inst: Air.Inst.Ref,
  26808     inst_src: LazySrcLoc,
  26809 ) CompileError!Air.Inst.Ref {
  26810     return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) {
  26811         error.NotCoercible => unreachable,
  26812         else => |e| return e,
  26813     };
  26814 }
  26815 
  26816 const CoersionError = CompileError || error{
  26817     /// When coerce is called recursively, this error should be returned instead of using `fail`
  26818     /// to ensure correct types in compile errors.
  26819     NotCoercible,
  26820 };
  26821 
  26822 const CoerceOpts = struct {
  26823     /// Should coerceExtra emit error messages.
  26824     report_err: bool = true,
  26825     /// Ignored if `report_err == false`.
  26826     is_ret: bool = false,
  26827     /// Should coercion to comptime_int ermit an error message.
  26828     no_cast_to_comptime_int: bool = false,
  26829 
  26830     param_src: struct {
  26831         func_inst: Air.Inst.Ref = .none,
  26832         param_i: u32 = undefined,
  26833 
  26834         fn get(info: @This(), sema: *Sema) !?Module.SrcLoc {
  26835             if (info.func_inst == .none) return null;
  26836             const mod = sema.mod;
  26837             const fn_decl = (try sema.funcDeclSrc(info.func_inst)) orelse return null;
  26838             const param_src = Module.paramSrc(0, mod, fn_decl, info.param_i);
  26839             if (param_src == .node_offset_param) {
  26840                 return Module.SrcLoc{
  26841                     .file_scope = fn_decl.getFileScope(mod),
  26842                     .parent_decl_node = fn_decl.src_node,
  26843                     .lazy = LazySrcLoc.nodeOffset(param_src.node_offset_param),
  26844                 };
  26845             }
  26846             return param_src.toSrcLoc(fn_decl, mod);
  26847         }
  26848     } = .{},
  26849 };
  26850 
  26851 fn coerceExtra(
  26852     sema: *Sema,
  26853     block: *Block,
  26854     dest_ty_unresolved: Type,
  26855     inst: Air.Inst.Ref,
  26856     inst_src: LazySrcLoc,
  26857     opts: CoerceOpts,
  26858 ) CoersionError!Air.Inst.Ref {
  26859     if (dest_ty_unresolved.isGenericPoison()) return inst;
  26860     const mod = sema.mod;
  26861     const dest_ty_src = inst_src; // TODO better source location
  26862     const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved);
  26863     const inst_ty = try sema.resolveTypeFields(sema.typeOf(inst));
  26864     const target = mod.getTarget();
  26865     // If the types are the same, we can return the operand.
  26866     if (dest_ty.eql(inst_ty, mod))
  26867         return inst;
  26868 
  26869     const maybe_inst_val = try sema.resolveMaybeUndefVal(inst);
  26870 
  26871     var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
  26872     if (in_memory_result == .ok) {
  26873         if (maybe_inst_val) |val| {
  26874             return sema.coerceInMemory(val, dest_ty);
  26875         }
  26876         try sema.requireRuntimeBlock(block, inst_src, null);
  26877         return block.addBitCast(dest_ty, inst);
  26878     }
  26879 
  26880     const is_undef = inst_ty.zigTypeTag(mod) == .Undefined;
  26881 
  26882     switch (dest_ty.zigTypeTag(mod)) {
  26883         .Optional => optional: {
  26884             // undefined sets the optional bit also to undefined.
  26885             if (is_undef) {
  26886                 return sema.addConstUndef(dest_ty);
  26887             }
  26888 
  26889             // null to ?T
  26890             if (inst_ty.zigTypeTag(mod) == .Null) {
  26891                 return sema.addConstant((try mod.intern(.{ .opt = .{
  26892                     .ty = dest_ty.toIntern(),
  26893                     .val = .none,
  26894                 } })).toValue());
  26895             }
  26896 
  26897             // cast from ?*T and ?[*]T to ?*anyopaque
  26898             // but don't do it if the source type is a double pointer
  26899             if (dest_ty.isPtrLikeOptional(mod) and
  26900                 dest_ty.elemType2(mod).toIntern() == .anyopaque_type and
  26901                 inst_ty.isPtrAtRuntime(mod))
  26902             anyopaque_check: {
  26903                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional;
  26904                 const elem_ty = inst_ty.elemType2(mod);
  26905                 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) {
  26906                     in_memory_result = .{ .double_ptr_to_anyopaque = .{
  26907                         .actual = inst_ty,
  26908                         .wanted = dest_ty,
  26909                     } };
  26910                     break :optional;
  26911                 }
  26912                 // Let the logic below handle wrapping the optional now that
  26913                 // it has been checked to correctly coerce.
  26914                 if (!inst_ty.isPtrLikeOptional(mod)) break :anyopaque_check;
  26915                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  26916             }
  26917 
  26918             // T to ?T
  26919             const child_type = dest_ty.optionalChild(mod);
  26920             const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  26921                 error.NotCoercible => {
  26922                     if (in_memory_result == .no_match) {
  26923                         // Try to give more useful notes
  26924                         in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src);
  26925                     }
  26926                     break :optional;
  26927                 },
  26928                 else => |e| return e,
  26929             };
  26930             return try sema.wrapOptional(block, dest_ty, intermediate, inst_src);
  26931         },
  26932         .Pointer => pointer: {
  26933             const dest_info = dest_ty.ptrInfo(mod);
  26934 
  26935             // Function body to function pointer.
  26936             if (inst_ty.zigTypeTag(mod) == .Fn) {
  26937                 const fn_val = try sema.resolveConstValue(block, .unneeded, inst, "");
  26938                 const fn_decl = fn_val.pointerDecl(mod).?;
  26939                 const inst_as_ptr = try sema.analyzeDeclRef(fn_decl);
  26940                 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);
  26941             }
  26942 
  26943             // *T to *[1]T
  26944             single_item: {
  26945                 if (dest_info.flags.size != .One) break :single_item;
  26946                 if (!inst_ty.isSinglePointer(mod)) break :single_item;
  26947                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  26948                 const ptr_elem_ty = inst_ty.childType(mod);
  26949                 const array_ty = dest_info.child.toType();
  26950                 if (array_ty.zigTypeTag(mod) != .Array) break :single_item;
  26951                 const array_elem_ty = array_ty.childType(mod);
  26952                 if (array_ty.arrayLen(mod) != 1) break :single_item;
  26953                 const dest_is_mut = !dest_info.flags.is_const;
  26954                 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
  26955                     .ok => {},
  26956                     else => break :single_item,
  26957                 }
  26958                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  26959             }
  26960 
  26961             // Coercions where the source is a single pointer to an array.
  26962             src_array_ptr: {
  26963                 if (!inst_ty.isSinglePointer(mod)) break :src_array_ptr;
  26964                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  26965                 const array_ty = inst_ty.childType(mod);
  26966                 if (array_ty.zigTypeTag(mod) != .Array) break :src_array_ptr;
  26967                 const array_elem_type = array_ty.childType(mod);
  26968                 const dest_is_mut = !dest_info.flags.is_const;
  26969 
  26970                 const dst_elem_type = dest_info.child.toType();
  26971                 const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src);
  26972                 switch (elem_res) {
  26973                     .ok => {},
  26974                     else => {
  26975                         in_memory_result = .{ .ptr_child = .{
  26976                             .child = try elem_res.dupe(sema.arena),
  26977                             .actual = array_elem_type,
  26978                             .wanted = dst_elem_type,
  26979                         } };
  26980                         break :src_array_ptr;
  26981                     },
  26982                 }
  26983 
  26984                 if (dest_info.sentinel != .none) {
  26985                     if (array_ty.sentinel(mod)) |inst_sent| {
  26986                         if (dest_info.sentinel != (try mod.getCoerced(inst_sent, dst_elem_type)).toIntern()) {
  26987                             in_memory_result = .{ .ptr_sentinel = .{
  26988                                 .actual = inst_sent,
  26989                                 .wanted = dest_info.sentinel.toValue(),
  26990                                 .ty = dst_elem_type,
  26991                             } };
  26992                             break :src_array_ptr;
  26993                         }
  26994                     } else {
  26995                         in_memory_result = .{ .ptr_sentinel = .{
  26996                             .actual = Value.@"unreachable",
  26997                             .wanted = dest_info.sentinel.toValue(),
  26998                             .ty = dst_elem_type,
  26999                         } };
  27000                         break :src_array_ptr;
  27001                     }
  27002                 }
  27003 
  27004                 switch (dest_info.flags.size) {
  27005                     .Slice => {
  27006                         // *[N]T to []T
  27007                         return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);
  27008                     },
  27009                     .C => {
  27010                         // *[N]T to [*c]T
  27011                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27012                     },
  27013                     .Many => {
  27014                         // *[N]T to [*]T
  27015                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27016                     },
  27017                     .One => {},
  27018                 }
  27019             }
  27020 
  27021             // coercion from C pointer
  27022             if (inst_ty.isCPtr(mod)) src_c_ptr: {
  27023                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr;
  27024                 // In this case we must add a safety check because the C pointer
  27025                 // could be null.
  27026                 const src_elem_ty = inst_ty.childType(mod);
  27027                 const dest_is_mut = !dest_info.flags.is_const;
  27028                 const dst_elem_type = dest_info.child.toType();
  27029                 switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
  27030                     .ok => {},
  27031                     else => break :src_c_ptr,
  27032                 }
  27033                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27034             }
  27035 
  27036             // cast from *T and [*]T to *anyopaque
  27037             // but don't do it if the source type is a double pointer
  27038             if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(mod) == .Pointer) to_anyopaque: {
  27039                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  27040                 const elem_ty = inst_ty.elemType2(mod);
  27041                 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) {
  27042                     in_memory_result = .{ .double_ptr_to_anyopaque = .{
  27043                         .actual = inst_ty,
  27044                         .wanted = dest_ty,
  27045                     } };
  27046                     break :pointer;
  27047                 }
  27048                 if (dest_ty.isSlice(mod)) break :to_anyopaque;
  27049                 if (inst_ty.isSlice(mod)) {
  27050                     in_memory_result = .{ .slice_to_anyopaque = .{
  27051                         .actual = inst_ty,
  27052                         .wanted = dest_ty,
  27053                     } };
  27054                     break :pointer;
  27055                 }
  27056                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27057             }
  27058 
  27059             switch (dest_info.flags.size) {
  27060                 // coercion to C pointer
  27061                 .C => switch (inst_ty.zigTypeTag(mod)) {
  27062                     .Null => {
  27063                         return sema.addConstant(try mod.getCoerced(Value.null, dest_ty));
  27064                     },
  27065                     .ComptimeInt => {
  27066                         const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  27067                             error.NotCoercible => break :pointer,
  27068                             else => |e| return e,
  27069                         };
  27070                         return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
  27071                     },
  27072                     .Int => {
  27073                         const ptr_size_ty = switch (inst_ty.intInfo(mod).signedness) {
  27074                             .signed => Type.isize,
  27075                             .unsigned => Type.usize,
  27076                         };
  27077                         const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  27078                             error.NotCoercible => {
  27079                                 // Try to give more useful notes
  27080                                 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src);
  27081                                 break :pointer;
  27082                             },
  27083                             else => |e| return e,
  27084                         };
  27085                         return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
  27086                     },
  27087                     .Pointer => p: {
  27088                         if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
  27089                         const inst_info = inst_ty.ptrInfo(mod);
  27090                         switch (try sema.coerceInMemoryAllowed(
  27091                             block,
  27092                             dest_info.child.toType(),
  27093                             inst_info.child.toType(),
  27094                             !dest_info.flags.is_const,
  27095                             target,
  27096                             dest_ty_src,
  27097                             inst_src,
  27098                         )) {
  27099                             .ok => {},
  27100                             else => break :p,
  27101                         }
  27102                         if (inst_info.flags.size == .Slice) {
  27103                             assert(dest_info.sentinel == .none);
  27104                             if (inst_info.sentinel == .none or
  27105                                 inst_info.sentinel != (try mod.intValue(inst_info.child.toType(), 0)).toIntern())
  27106                                 break :p;
  27107 
  27108                             const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
  27109                             return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
  27110                         }
  27111                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  27112                     },
  27113                     else => {},
  27114                 },
  27115                 .One => switch (dest_info.child.toType().zigTypeTag(mod)) {
  27116                     .Union => {
  27117                         // pointer to anonymous struct to pointer to union
  27118                         if (inst_ty.isSinglePointer(mod) and
  27119                             inst_ty.childType(mod).isAnonStruct(mod) and
  27120                             sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))
  27121                         {
  27122                             return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
  27123                         }
  27124                     },
  27125                     .Struct => {
  27126                         // pointer to anonymous struct to pointer to struct
  27127                         if (inst_ty.isSinglePointer(mod) and
  27128                             inst_ty.childType(mod).isAnonStruct(mod) and
  27129                             sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))
  27130                         {
  27131                             return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src) catch |err| switch (err) {
  27132                                 error.NotCoercible => break :pointer,
  27133                                 else => |e| return e,
  27134                             };
  27135                         }
  27136                     },
  27137                     .Array => {
  27138                         // pointer to tuple to pointer to array
  27139                         if (inst_ty.isSinglePointer(mod) and
  27140                             inst_ty.childType(mod).isTuple(mod) and
  27141                             sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))
  27142                         {
  27143                             return sema.coerceTupleToArrayPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
  27144                         }
  27145                     },
  27146                     else => {},
  27147                 },
  27148                 .Slice => to_slice: {
  27149                     if (inst_ty.zigTypeTag(mod) == .Array) {
  27150                         return sema.fail(
  27151                             block,
  27152                             inst_src,
  27153                             "array literal requires address-of operator (&) to coerce to slice type '{}'",
  27154                             .{dest_ty.fmt(mod)},
  27155                         );
  27156                     }
  27157 
  27158                     if (!inst_ty.isSinglePointer(mod)) break :to_slice;
  27159                     const inst_child_ty = inst_ty.childType(mod);
  27160                     if (!inst_child_ty.isTuple(mod)) break :to_slice;
  27161 
  27162                     // empty tuple to zero-length slice
  27163                     // note that this allows coercing to a mutable slice.
  27164                     if (inst_child_ty.structFieldCount(mod) == 0) {
  27165                         // Optional slice is represented with a null pointer so
  27166                         // we use a dummy pointer value with the required alignment.
  27167                         return sema.addConstant((try mod.intern(.{ .ptr = .{
  27168                             .ty = dest_ty.toIntern(),
  27169                             .addr = .{ .int = (if (dest_info.flags.alignment != .none)
  27170                                 try mod.intValue(Type.usize, dest_info.flags.alignment.toByteUnitsOptional().?)
  27171                             else
  27172                                 try mod.getCoerced(try dest_info.child.toType().lazyAbiAlignment(mod), Type.usize)).toIntern() },
  27173                             .len = (try mod.intValue(Type.usize, 0)).toIntern(),
  27174                         } })).toValue());
  27175                     }
  27176 
  27177                     // pointer to tuple to slice
  27178                     if (!dest_info.flags.is_const) {
  27179                         const err_msg = err_msg: {
  27180                             const err_msg = try sema.errMsg(block, inst_src, "cannot cast pointer to tuple to '{}'", .{dest_ty.fmt(mod)});
  27181                             errdefer err_msg.deinit(sema.gpa);
  27182                             try sema.errNote(block, dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{});
  27183                             break :err_msg err_msg;
  27184                         };
  27185                         return sema.failWithOwnedErrorMsg(err_msg);
  27186                     }
  27187                     return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);
  27188                 },
  27189                 .Many => p: {
  27190                     if (!inst_ty.isSlice(mod)) break :p;
  27191                     if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
  27192                     const inst_info = inst_ty.ptrInfo(mod);
  27193 
  27194                     switch (try sema.coerceInMemoryAllowed(
  27195                         block,
  27196                         dest_info.child.toType(),
  27197                         inst_info.child.toType(),
  27198                         !dest_info.flags.is_const,
  27199                         target,
  27200                         dest_ty_src,
  27201                         inst_src,
  27202                     )) {
  27203                         .ok => {},
  27204                         else => break :p,
  27205                     }
  27206 
  27207                     if (dest_info.sentinel == .none or inst_info.sentinel == .none or
  27208                         dest_info.sentinel !=
  27209                         try mod.intern_pool.getCoerced(sema.gpa, inst_info.sentinel, dest_info.child))
  27210                         break :p;
  27211 
  27212                     const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
  27213                     return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
  27214                 },
  27215             }
  27216         },
  27217         .Int, .ComptimeInt => switch (inst_ty.zigTypeTag(mod)) {
  27218             .Float, .ComptimeFloat => float: {
  27219                 if (is_undef) {
  27220                     return sema.addConstUndef(dest_ty);
  27221                 }
  27222                 const val = (try sema.resolveMaybeUndefVal(inst)) orelse {
  27223                     if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
  27224                         if (!opts.report_err) return error.NotCoercible;
  27225                         return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime-known");
  27226                     }
  27227                     break :float;
  27228                 };
  27229 
  27230                 if (val.floatHasFraction(mod)) {
  27231                     return sema.fail(
  27232                         block,
  27233                         inst_src,
  27234                         "fractional component prevents float value '{}' from coercion to type '{}'",
  27235                         .{ val.fmtValue(inst_ty, mod), dest_ty.fmt(mod) },
  27236                     );
  27237                 }
  27238                 const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty);
  27239                 return try sema.addConstant(result_val);
  27240             },
  27241             .Int, .ComptimeInt => {
  27242                 if (is_undef) {
  27243                     return sema.addConstUndef(dest_ty);
  27244                 }
  27245                 if (try sema.resolveMaybeUndefVal(inst)) |val| {
  27246                     // comptime-known integer to other number
  27247                     if (!(try sema.intFitsInType(val, dest_ty, null))) {
  27248                         if (!opts.report_err) return error.NotCoercible;
  27249                         return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) });
  27250                     }
  27251                     return try sema.addConstant(try mod.getCoerced(val, dest_ty));
  27252                 }
  27253                 if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
  27254                     if (!opts.report_err) return error.NotCoercible;
  27255                     if (opts.no_cast_to_comptime_int) return inst;
  27256                     return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime-known");
  27257                 }
  27258 
  27259                 // integer widening
  27260                 const dst_info = dest_ty.intInfo(mod);
  27261                 const src_info = inst_ty.intInfo(mod);
  27262                 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
  27263                     // small enough unsigned ints can get casted to large enough signed ints
  27264                     (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
  27265                 {
  27266                     try sema.requireRuntimeBlock(block, inst_src, null);
  27267                     return block.addTyOp(.intcast, dest_ty, inst);
  27268                 }
  27269             },
  27270             .Undefined => {
  27271                 return sema.addConstUndef(dest_ty);
  27272             },
  27273             else => {},
  27274         },
  27275         .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) {
  27276             .ComptimeFloat => {
  27277                 const val = try sema.resolveConstValue(block, .unneeded, inst, "");
  27278                 const result_val = try val.floatCast(dest_ty, mod);
  27279                 return try sema.addConstant(result_val);
  27280             },
  27281             .Float => {
  27282                 if (is_undef) {
  27283                     return sema.addConstUndef(dest_ty);
  27284                 }
  27285                 if (try sema.resolveMaybeUndefVal(inst)) |val| {
  27286                     const result_val = try val.floatCast(dest_ty, mod);
  27287                     if (!val.eql(try result_val.floatCast(inst_ty, mod), inst_ty, mod)) {
  27288                         return sema.fail(
  27289                             block,
  27290                             inst_src,
  27291                             "type '{}' cannot represent float value '{}'",
  27292                             .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) },
  27293                         );
  27294                     }
  27295                     return try sema.addConstant(result_val);
  27296                 } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) {
  27297                     if (!opts.report_err) return error.NotCoercible;
  27298                     return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime-known");
  27299                 }
  27300 
  27301                 // float widening
  27302                 const src_bits = inst_ty.floatBits(target);
  27303                 const dst_bits = dest_ty.floatBits(target);
  27304                 if (dst_bits >= src_bits) {
  27305                     try sema.requireRuntimeBlock(block, inst_src, null);
  27306                     return block.addTyOp(.fpext, dest_ty, inst);
  27307                 }
  27308             },
  27309             .Int, .ComptimeInt => int: {
  27310                 if (is_undef) {
  27311                     return sema.addConstUndef(dest_ty);
  27312                 }
  27313                 const val = (try sema.resolveMaybeUndefVal(inst)) orelse {
  27314                     if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) {
  27315                         if (!opts.report_err) return error.NotCoercible;
  27316                         return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime-known");
  27317                     }
  27318                     break :int;
  27319                 };
  27320                 const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, mod, sema);
  27321                 // TODO implement this compile error
  27322                 //const int_again_val = try result_val.intFromFloat(sema.arena, inst_ty);
  27323                 //if (!int_again_val.eql(val, inst_ty, mod)) {
  27324                 //    return sema.fail(
  27325                 //        block,
  27326                 //        inst_src,
  27327                 //        "type '{}' cannot represent integer value '{}'",
  27328                 //        .{ dest_ty.fmt(mod), val },
  27329                 //    );
  27330                 //}
  27331                 return try sema.addConstant(result_val);
  27332             },
  27333             .Undefined => {
  27334                 return sema.addConstUndef(dest_ty);
  27335             },
  27336             else => {},
  27337         },
  27338         .Enum => switch (inst_ty.zigTypeTag(mod)) {
  27339             .EnumLiteral => {
  27340                 // enum literal to enum
  27341                 const val = try sema.resolveConstValue(block, .unneeded, inst, "");
  27342                 const string = mod.intern_pool.indexToKey(val.toIntern()).enum_literal;
  27343                 const field_index = dest_ty.enumFieldIndex(string, mod) orelse {
  27344                     const msg = msg: {
  27345                         const msg = try sema.errMsg(
  27346                             block,
  27347                             inst_src,
  27348                             "no field named '{}' in enum '{}'",
  27349                             .{ string.fmt(&mod.intern_pool), dest_ty.fmt(mod) },
  27350                         );
  27351                         errdefer msg.destroy(sema.gpa);
  27352                         try sema.addDeclaredHereNote(msg, dest_ty);
  27353                         break :msg msg;
  27354                     };
  27355                     return sema.failWithOwnedErrorMsg(msg);
  27356                 };
  27357                 return sema.addConstant(
  27358                     try mod.enumValueFieldIndex(dest_ty, @as(u32, @intCast(field_index))),
  27359                 );
  27360             },
  27361             .Union => blk: {
  27362                 // union to its own tag type
  27363                 const union_tag_ty = inst_ty.unionTagType(mod) orelse break :blk;
  27364                 if (union_tag_ty.eql(dest_ty, mod)) {
  27365                     return sema.unionToTag(block, dest_ty, inst, inst_src);
  27366                 }
  27367             },
  27368             .Undefined => {
  27369                 return sema.addConstUndef(dest_ty);
  27370             },
  27371             else => {},
  27372         },
  27373         .ErrorUnion => switch (inst_ty.zigTypeTag(mod)) {
  27374             .ErrorUnion => eu: {
  27375                 if (maybe_inst_val) |inst_val| {
  27376                     switch (inst_val.toIntern()) {
  27377                         .undef => return sema.addConstUndef(dest_ty),
  27378                         else => switch (mod.intern_pool.indexToKey(inst_val.toIntern())) {
  27379                             .error_union => |error_union| switch (error_union.val) {
  27380                                 .err_name => |err_name| {
  27381                                     const error_set_ty = inst_ty.errorUnionSet(mod);
  27382                                     const error_set_val = try sema.addConstant((try mod.intern(.{ .err = .{
  27383                                         .ty = error_set_ty.toIntern(),
  27384                                         .name = err_name,
  27385                                     } })).toValue());
  27386                                     return sema.wrapErrorUnionSet(block, dest_ty, error_set_val, inst_src);
  27387                                 },
  27388                                 .payload => |payload| {
  27389                                     const payload_val = try sema.addConstant(
  27390                                         payload.toValue(),
  27391                                     );
  27392                                     return sema.wrapErrorUnionPayload(block, dest_ty, payload_val, inst_src) catch |err| switch (err) {
  27393                                         error.NotCoercible => break :eu,
  27394                                         else => |e| return e,
  27395                                     };
  27396                                 },
  27397                             },
  27398                             else => unreachable,
  27399                         },
  27400                     }
  27401                 }
  27402             },
  27403             .ErrorSet => {
  27404                 // E to E!T
  27405                 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);
  27406             },
  27407             .Undefined => {
  27408                 return sema.addConstUndef(dest_ty);
  27409             },
  27410             else => eu: {
  27411                 // T to E!T
  27412                 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) {
  27413                     error.NotCoercible => break :eu,
  27414                     else => |e| return e,
  27415                 };
  27416             },
  27417         },
  27418         .Union => switch (inst_ty.zigTypeTag(mod)) {
  27419             .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
  27420             .Struct => {
  27421                 if (inst_ty.isAnonStruct(mod)) {
  27422                     return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src);
  27423                 }
  27424             },
  27425             .Undefined => {
  27426                 return sema.addConstUndef(dest_ty);
  27427             },
  27428             else => {},
  27429         },
  27430         .Array => switch (inst_ty.zigTypeTag(mod)) {
  27431             .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
  27432             .Struct => {
  27433                 if (inst == .empty_struct) {
  27434                     return sema.arrayInitEmpty(block, inst_src, dest_ty);
  27435                 }
  27436                 if (inst_ty.isTuple(mod)) {
  27437                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
  27438                 }
  27439             },
  27440             .Undefined => {
  27441                 return sema.addConstUndef(dest_ty);
  27442             },
  27443             else => {},
  27444         },
  27445         .Vector => switch (inst_ty.zigTypeTag(mod)) {
  27446             .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
  27447             .Struct => {
  27448                 if (inst_ty.isTuple(mod)) {
  27449                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
  27450                 }
  27451             },
  27452             .Undefined => {
  27453                 return sema.addConstUndef(dest_ty);
  27454             },
  27455             else => {},
  27456         },
  27457         .Struct => blk: {
  27458             if (inst == .empty_struct) {
  27459                 return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src);
  27460             }
  27461             if (inst_ty.isTupleOrAnonStruct(mod)) {
  27462                 return sema.coerceTupleToStruct(block, dest_ty, inst, inst_src) catch |err| switch (err) {
  27463                     error.NotCoercible => break :blk,
  27464                     else => |e| return e,
  27465                 };
  27466             }
  27467         },
  27468         else => {},
  27469     }
  27470 
  27471     // undefined to anything. We do this after the big switch above so that
  27472     // special logic has a chance to run first, such as `*[N]T` to `[]T` which
  27473     // should initialize the length field of the slice.
  27474     if (is_undef) {
  27475         return sema.addConstUndef(dest_ty);
  27476     }
  27477 
  27478     if (!opts.report_err) return error.NotCoercible;
  27479 
  27480     if (opts.is_ret and dest_ty.zigTypeTag(mod) == .NoReturn) {
  27481         const msg = msg: {
  27482             const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{});
  27483             errdefer msg.destroy(sema.gpa);
  27484 
  27485             const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
  27486             const src_decl = mod.declPtr(sema.func.?.owner_decl);
  27487             try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "'noreturn' declared here", .{});
  27488             break :msg msg;
  27489         };
  27490         return sema.failWithOwnedErrorMsg(msg);
  27491     }
  27492 
  27493     const msg = msg: {
  27494         const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), inst_ty.fmt(mod) });
  27495         errdefer msg.destroy(sema.gpa);
  27496 
  27497         // E!T to T
  27498         if (inst_ty.zigTypeTag(mod) == .ErrorUnion and
  27499             (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
  27500         {
  27501             try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{});
  27502             try sema.errNote(block, inst_src, msg, "consider using 'try', 'catch', or 'if'", .{});
  27503         }
  27504 
  27505         // ?T to T
  27506         if (inst_ty.zigTypeTag(mod) == .Optional and
  27507             (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
  27508         {
  27509             try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{});
  27510             try sema.errNote(block, inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{});
  27511         }
  27512 
  27513         try in_memory_result.report(sema, block, inst_src, msg);
  27514 
  27515         // Add notes about function return type
  27516         if (opts.is_ret and mod.test_functions.get(sema.func.?.owner_decl) == null) {
  27517             const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
  27518             const src_decl = mod.declPtr(sema.func.?.owner_decl);
  27519             if (inst_ty.isError(mod) and !dest_ty.isError(mod)) {
  27520                 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function cannot return an error", .{});
  27521             } else {
  27522                 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function return type declared here", .{});
  27523             }
  27524         }
  27525 
  27526         if (try opts.param_src.get(sema)) |param_src| {
  27527             try mod.errNoteNonLazy(param_src, msg, "parameter type declared here", .{});
  27528         }
  27529 
  27530         // TODO maybe add "cannot store an error in type '{}'" note
  27531 
  27532         break :msg msg;
  27533     };
  27534     return sema.failWithOwnedErrorMsg(msg);
  27535 }
  27536 
  27537 fn coerceInMemory(
  27538     sema: *Sema,
  27539     val: Value,
  27540     dst_ty: Type,
  27541 ) CompileError!Air.Inst.Ref {
  27542     return sema.addConstant(try sema.mod.getCoerced(val, dst_ty));
  27543 }
  27544 
  27545 const InMemoryCoercionResult = union(enum) {
  27546     ok,
  27547     no_match: Pair,
  27548     int_not_coercible: Int,
  27549     error_union_payload: PairAndChild,
  27550     array_len: IntPair,
  27551     array_sentinel: Sentinel,
  27552     array_elem: PairAndChild,
  27553     vector_len: IntPair,
  27554     vector_elem: PairAndChild,
  27555     optional_shape: Pair,
  27556     optional_child: PairAndChild,
  27557     from_anyerror,
  27558     missing_error: []const InternPool.NullTerminatedString,
  27559     /// true if wanted is var args
  27560     fn_var_args: bool,
  27561     /// true if wanted is generic
  27562     fn_generic: bool,
  27563     fn_param_count: IntPair,
  27564     fn_param_noalias: IntPair,
  27565     fn_param_comptime: ComptimeParam,
  27566     fn_param: Param,
  27567     fn_cc: CC,
  27568     fn_return_type: PairAndChild,
  27569     ptr_child: PairAndChild,
  27570     ptr_addrspace: AddressSpace,
  27571     ptr_sentinel: Sentinel,
  27572     ptr_size: Size,
  27573     ptr_qualifiers: Qualifiers,
  27574     ptr_allowzero: Pair,
  27575     ptr_bit_range: BitRange,
  27576     ptr_alignment: IntPair,
  27577     double_ptr_to_anyopaque: Pair,
  27578     slice_to_anyopaque: Pair,
  27579 
  27580     const Pair = struct {
  27581         actual: Type,
  27582         wanted: Type,
  27583     };
  27584 
  27585     const PairAndChild = struct {
  27586         child: *InMemoryCoercionResult,
  27587         actual: Type,
  27588         wanted: Type,
  27589     };
  27590 
  27591     const Param = struct {
  27592         child: *InMemoryCoercionResult,
  27593         actual: Type,
  27594         wanted: Type,
  27595         index: u64,
  27596     };
  27597 
  27598     const ComptimeParam = struct {
  27599         index: u64,
  27600         wanted: bool,
  27601     };
  27602 
  27603     const Sentinel = struct {
  27604         // unreachable_value indicates no sentinel
  27605         actual: Value,
  27606         wanted: Value,
  27607         ty: Type,
  27608     };
  27609 
  27610     const Int = struct {
  27611         actual_signedness: std.builtin.Signedness,
  27612         wanted_signedness: std.builtin.Signedness,
  27613         actual_bits: u16,
  27614         wanted_bits: u16,
  27615     };
  27616 
  27617     const IntPair = struct {
  27618         actual: u64,
  27619         wanted: u64,
  27620     };
  27621 
  27622     const Size = struct {
  27623         actual: std.builtin.Type.Pointer.Size,
  27624         wanted: std.builtin.Type.Pointer.Size,
  27625     };
  27626 
  27627     const Qualifiers = struct {
  27628         actual_const: bool,
  27629         wanted_const: bool,
  27630         actual_volatile: bool,
  27631         wanted_volatile: bool,
  27632     };
  27633 
  27634     const AddressSpace = struct {
  27635         actual: std.builtin.AddressSpace,
  27636         wanted: std.builtin.AddressSpace,
  27637     };
  27638 
  27639     const CC = struct {
  27640         actual: std.builtin.CallingConvention,
  27641         wanted: std.builtin.CallingConvention,
  27642     };
  27643 
  27644     const BitRange = struct {
  27645         actual_host: u16,
  27646         wanted_host: u16,
  27647         actual_offset: u16,
  27648         wanted_offset: u16,
  27649     };
  27650 
  27651     fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult {
  27652         const res = try arena.create(InMemoryCoercionResult);
  27653         res.* = child.*;
  27654         return res;
  27655     }
  27656 
  27657     fn report(res: *const InMemoryCoercionResult, sema: *Sema, block: *Block, src: LazySrcLoc, msg: *Module.ErrorMsg) !void {
  27658         const mod = sema.mod;
  27659         var cur = res;
  27660         while (true) switch (cur.*) {
  27661             .ok => unreachable,
  27662             .no_match => |types| {
  27663                 try sema.addDeclaredHereNote(msg, types.wanted);
  27664                 try sema.addDeclaredHereNote(msg, types.actual);
  27665                 break;
  27666             },
  27667             .int_not_coercible => |int| {
  27668                 try sema.errNote(block, src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{
  27669                     @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits,
  27670                 });
  27671                 break;
  27672             },
  27673             .error_union_payload => |pair| {
  27674                 try sema.errNote(block, src, msg, "error union payload '{}' cannot cast into error union payload '{}'", .{
  27675                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27676                 });
  27677                 cur = pair.child;
  27678             },
  27679             .array_len => |lens| {
  27680                 try sema.errNote(block, src, msg, "array of length {d} cannot cast into an array of length {d}", .{
  27681                     lens.actual, lens.wanted,
  27682                 });
  27683                 break;
  27684             },
  27685             .array_sentinel => |sentinel| {
  27686                 if (sentinel.actual.toIntern() != .unreachable_value) {
  27687                     try sema.errNote(block, src, msg, "array sentinel '{}' cannot cast into array sentinel '{}'", .{
  27688                         sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod),
  27689                     });
  27690                 } else {
  27691                     try sema.errNote(block, src, msg, "destination array requires '{}' sentinel", .{
  27692                         sentinel.wanted.fmtValue(sentinel.ty, mod),
  27693                     });
  27694                 }
  27695                 break;
  27696             },
  27697             .array_elem => |pair| {
  27698                 try sema.errNote(block, src, msg, "array element type '{}' cannot cast into array element type '{}'", .{
  27699                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27700                 });
  27701                 cur = pair.child;
  27702             },
  27703             .vector_len => |lens| {
  27704                 try sema.errNote(block, src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{
  27705                     lens.actual, lens.wanted,
  27706                 });
  27707                 break;
  27708             },
  27709             .vector_elem => |pair| {
  27710                 try sema.errNote(block, src, msg, "vector element type '{}' cannot cast into vector element type '{}'", .{
  27711                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27712                 });
  27713                 cur = pair.child;
  27714             },
  27715             .optional_shape => |pair| {
  27716                 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{
  27717                     pair.actual.optionalChild(mod).fmt(mod), pair.wanted.optionalChild(mod).fmt(mod),
  27718                 });
  27719                 break;
  27720             },
  27721             .optional_child => |pair| {
  27722                 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{
  27723                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27724                 });
  27725                 cur = pair.child;
  27726             },
  27727             .from_anyerror => {
  27728                 try sema.errNote(block, src, msg, "global error set cannot cast into a smaller set", .{});
  27729                 break;
  27730             },
  27731             .missing_error => |missing_errors| {
  27732                 for (missing_errors) |err| {
  27733                     try sema.errNote(block, src, msg, "'error.{}' not a member of destination error set", .{err.fmt(&mod.intern_pool)});
  27734                 }
  27735                 break;
  27736             },
  27737             .fn_var_args => |wanted_var_args| {
  27738                 if (wanted_var_args) {
  27739                     try sema.errNote(block, src, msg, "non-variadic function cannot cast into a variadic function", .{});
  27740                 } else {
  27741                     try sema.errNote(block, src, msg, "variadic function cannot cast into a non-variadic function", .{});
  27742                 }
  27743                 break;
  27744             },
  27745             .fn_generic => |wanted_generic| {
  27746                 if (wanted_generic) {
  27747                     try sema.errNote(block, src, msg, "non-generic function cannot cast into a generic function", .{});
  27748                 } else {
  27749                     try sema.errNote(block, src, msg, "generic function cannot cast into a non-generic function", .{});
  27750                 }
  27751                 break;
  27752             },
  27753             .fn_param_count => |lens| {
  27754                 try sema.errNote(block, src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{
  27755                     lens.actual, lens.wanted,
  27756                 });
  27757                 break;
  27758             },
  27759             .fn_param_noalias => |param| {
  27760                 var index: u6 = 0;
  27761                 var actual_noalias = false;
  27762                 while (true) : (index += 1) {
  27763                     const actual = @as(u1, @truncate(param.actual >> index));
  27764                     const wanted = @as(u1, @truncate(param.wanted >> index));
  27765                     if (actual != wanted) {
  27766                         actual_noalias = actual == 1;
  27767                         break;
  27768                     }
  27769                 }
  27770                 if (!actual_noalias) {
  27771                     try sema.errNote(block, src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index});
  27772                 } else {
  27773                     try sema.errNote(block, src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index});
  27774                 }
  27775                 break;
  27776             },
  27777             .fn_param_comptime => |param| {
  27778                 if (param.wanted) {
  27779                     try sema.errNote(block, src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index});
  27780                 } else {
  27781                     try sema.errNote(block, src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index});
  27782                 }
  27783                 break;
  27784             },
  27785             .fn_param => |param| {
  27786                 try sema.errNote(block, src, msg, "parameter {d} '{}' cannot cast into '{}'", .{
  27787                     param.index, param.actual.fmt(mod), param.wanted.fmt(mod),
  27788                 });
  27789                 cur = param.child;
  27790             },
  27791             .fn_cc => |cc| {
  27792                 try sema.errNote(block, src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) });
  27793                 break;
  27794             },
  27795             .fn_return_type => |pair| {
  27796                 try sema.errNote(block, src, msg, "return type '{}' cannot cast into return type '{}'", .{
  27797                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27798                 });
  27799                 cur = pair.child;
  27800             },
  27801             .ptr_child => |pair| {
  27802                 try sema.errNote(block, src, msg, "pointer type child '{}' cannot cast into pointer type child '{}'", .{
  27803                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27804                 });
  27805                 cur = pair.child;
  27806             },
  27807             .ptr_addrspace => |@"addrspace"| {
  27808                 try sema.errNote(block, src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) });
  27809                 break;
  27810             },
  27811             .ptr_sentinel => |sentinel| {
  27812                 if (sentinel.actual.toIntern() != .unreachable_value) {
  27813                     try sema.errNote(block, src, msg, "pointer sentinel '{}' cannot cast into pointer sentinel '{}'", .{
  27814                         sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod),
  27815                     });
  27816                 } else {
  27817                     try sema.errNote(block, src, msg, "destination pointer requires '{}' sentinel", .{
  27818                         sentinel.wanted.fmtValue(sentinel.ty, mod),
  27819                     });
  27820                 }
  27821                 break;
  27822             },
  27823             .ptr_size => |size| {
  27824                 try sema.errNote(block, src, msg, "a {s} pointer cannot cast into a {s} pointer", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) });
  27825                 break;
  27826             },
  27827             .ptr_qualifiers => |qualifiers| {
  27828                 const ok_const = !qualifiers.actual_const or qualifiers.wanted_const;
  27829                 const ok_volatile = !qualifiers.actual_volatile or qualifiers.wanted_volatile;
  27830                 if (!ok_const) {
  27831                     try sema.errNote(block, src, msg, "cast discards const qualifier", .{});
  27832                 } else if (!ok_volatile) {
  27833                     try sema.errNote(block, src, msg, "cast discards volatile qualifier", .{});
  27834                 }
  27835                 break;
  27836             },
  27837             .ptr_allowzero => |pair| {
  27838                 const wanted_allow_zero = pair.wanted.ptrAllowsZero(mod);
  27839                 const actual_allow_zero = pair.actual.ptrAllowsZero(mod);
  27840                 if (actual_allow_zero and !wanted_allow_zero) {
  27841                     try sema.errNote(block, src, msg, "'{}' could have null values which are illegal in type '{}'", .{
  27842                         pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27843                     });
  27844                 } else {
  27845                     try sema.errNote(block, src, msg, "mutable '{}' allows illegal null values stored to type '{}'", .{
  27846                         pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27847                     });
  27848                 }
  27849                 break;
  27850             },
  27851             .ptr_bit_range => |bit_range| {
  27852                 if (bit_range.actual_host != bit_range.wanted_host) {
  27853                     try sema.errNote(block, src, msg, "pointer host size '{}' cannot cast into pointer host size '{}'", .{
  27854                         bit_range.actual_host, bit_range.wanted_host,
  27855                     });
  27856                 }
  27857                 if (bit_range.actual_offset != bit_range.wanted_offset) {
  27858                     try sema.errNote(block, src, msg, "pointer bit offset '{}' cannot cast into pointer bit offset '{}'", .{
  27859                         bit_range.actual_offset, bit_range.wanted_offset,
  27860                     });
  27861                 }
  27862                 break;
  27863             },
  27864             .ptr_alignment => |pair| {
  27865                 try sema.errNote(block, src, msg, "pointer alignment '{}' cannot cast into pointer alignment '{}'", .{
  27866                     pair.actual, pair.wanted,
  27867                 });
  27868                 break;
  27869             },
  27870             .double_ptr_to_anyopaque => |pair| {
  27871                 try sema.errNote(block, src, msg, "cannot implicitly cast double pointer '{}' to anyopaque pointer '{}'", .{
  27872                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27873                 });
  27874                 break;
  27875             },
  27876             .slice_to_anyopaque => |pair| {
  27877                 try sema.errNote(block, src, msg, "cannot implicitly cast slice '{}' to anyopaque pointer '{}'", .{
  27878                     pair.actual.fmt(mod), pair.wanted.fmt(mod),
  27879                 });
  27880                 try sema.errNote(block, src, msg, "consider using '.ptr'", .{});
  27881                 break;
  27882             },
  27883         };
  27884     }
  27885 };
  27886 
  27887 fn pointerSizeString(size: std.builtin.Type.Pointer.Size) []const u8 {
  27888     return switch (size) {
  27889         .One => "single",
  27890         .Many => "many",
  27891         .C => "C",
  27892         .Slice => unreachable,
  27893     };
  27894 }
  27895 
  27896 /// If pointers have the same representation in runtime memory, a bitcast AIR instruction
  27897 /// may be used for the coercion.
  27898 /// * `const` attribute can be gained
  27899 /// * `volatile` attribute can be gained
  27900 /// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if !dest_is_mut
  27901 /// * alignment can be decreased
  27902 /// * bit offset attributes must match exactly
  27903 /// * `*`/`[*]` must match exactly, but `[*c]` matches either one
  27904 /// * sentinel-terminated pointers can coerce into `[*]`
  27905 fn coerceInMemoryAllowed(
  27906     sema: *Sema,
  27907     block: *Block,
  27908     dest_ty: Type,
  27909     src_ty: Type,
  27910     dest_is_mut: bool,
  27911     target: std.Target,
  27912     dest_src: LazySrcLoc,
  27913     src_src: LazySrcLoc,
  27914 ) CompileError!InMemoryCoercionResult {
  27915     const mod = sema.mod;
  27916 
  27917     if (dest_ty.eql(src_ty, mod))
  27918         return .ok;
  27919 
  27920     const dest_tag = dest_ty.zigTypeTag(mod);
  27921     const src_tag = src_ty.zigTypeTag(mod);
  27922 
  27923     // Differently-named integers with the same number of bits.
  27924     if (dest_tag == .Int and src_tag == .Int) {
  27925         const dest_info = dest_ty.intInfo(mod);
  27926         const src_info = src_ty.intInfo(mod);
  27927 
  27928         if (dest_info.signedness == src_info.signedness and
  27929             dest_info.bits == src_info.bits)
  27930         {
  27931             return .ok;
  27932         }
  27933 
  27934         if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or
  27935             // small enough unsigned ints can get casted to large enough signed ints
  27936             (dest_info.signedness == .signed and (src_info.signedness == .unsigned or dest_info.bits <= src_info.bits)) or
  27937             (dest_info.signedness == .unsigned and src_info.signedness == .signed))
  27938         {
  27939             return InMemoryCoercionResult{ .int_not_coercible = .{
  27940                 .actual_signedness = src_info.signedness,
  27941                 .wanted_signedness = dest_info.signedness,
  27942                 .actual_bits = src_info.bits,
  27943                 .wanted_bits = dest_info.bits,
  27944             } };
  27945         }
  27946     }
  27947 
  27948     // Differently-named floats with the same number of bits.
  27949     if (dest_tag == .Float and src_tag == .Float) {
  27950         const dest_bits = dest_ty.floatBits(target);
  27951         const src_bits = src_ty.floatBits(target);
  27952         if (dest_bits == src_bits) {
  27953             return .ok;
  27954         }
  27955     }
  27956 
  27957     // Pointers / Pointer-like Optionals
  27958     const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty);
  27959     const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty);
  27960     if (maybe_dest_ptr_ty) |dest_ptr_ty| {
  27961         if (maybe_src_ptr_ty) |src_ptr_ty| {
  27962             return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src);
  27963         }
  27964     }
  27965 
  27966     // Slices
  27967     if (dest_ty.isSlice(mod) and src_ty.isSlice(mod)) {
  27968         return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
  27969     }
  27970 
  27971     // Functions
  27972     if (dest_tag == .Fn and src_tag == .Fn) {
  27973         return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, target, dest_src, src_src);
  27974     }
  27975 
  27976     // Error Unions
  27977     if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) {
  27978         const dest_payload = dest_ty.errorUnionPayload(mod);
  27979         const src_payload = src_ty.errorUnionPayload(mod);
  27980         const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src);
  27981         if (child != .ok) {
  27982             return InMemoryCoercionResult{ .error_union_payload = .{
  27983                 .child = try child.dupe(sema.arena),
  27984                 .actual = src_payload,
  27985                 .wanted = dest_payload,
  27986             } };
  27987         }
  27988         return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(mod), src_ty.errorUnionSet(mod), dest_is_mut, target, dest_src, src_src);
  27989     }
  27990 
  27991     // Error Sets
  27992     if (dest_tag == .ErrorSet and src_tag == .ErrorSet) {
  27993         return try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src);
  27994     }
  27995 
  27996     // Arrays
  27997     if (dest_tag == .Array and src_tag == .Array) {
  27998         const dest_info = dest_ty.arrayInfo(mod);
  27999         const src_info = src_ty.arrayInfo(mod);
  28000         if (dest_info.len != src_info.len) {
  28001             return InMemoryCoercionResult{ .array_len = .{
  28002                 .actual = src_info.len,
  28003                 .wanted = dest_info.len,
  28004             } };
  28005         }
  28006 
  28007         const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src);
  28008         if (child != .ok) {
  28009             return InMemoryCoercionResult{ .array_elem = .{
  28010                 .child = try child.dupe(sema.arena),
  28011                 .actual = src_info.elem_type,
  28012                 .wanted = dest_info.elem_type,
  28013             } };
  28014         }
  28015         const ok_sent = dest_info.sentinel == null or
  28016             (src_info.sentinel != null and
  28017             dest_info.sentinel.?.eql(
  28018             try mod.getCoerced(src_info.sentinel.?, dest_info.elem_type),
  28019             dest_info.elem_type,
  28020             mod,
  28021         ));
  28022         if (!ok_sent) {
  28023             return InMemoryCoercionResult{ .array_sentinel = .{
  28024                 .actual = src_info.sentinel orelse Value.@"unreachable",
  28025                 .wanted = dest_info.sentinel orelse Value.@"unreachable",
  28026                 .ty = dest_info.elem_type,
  28027             } };
  28028         }
  28029         return .ok;
  28030     }
  28031 
  28032     // Vectors
  28033     if (dest_tag == .Vector and src_tag == .Vector) {
  28034         const dest_len = dest_ty.vectorLen(mod);
  28035         const src_len = src_ty.vectorLen(mod);
  28036         if (dest_len != src_len) {
  28037             return InMemoryCoercionResult{ .vector_len = .{
  28038                 .actual = src_len,
  28039                 .wanted = dest_len,
  28040             } };
  28041         }
  28042 
  28043         const dest_elem_ty = dest_ty.scalarType(mod);
  28044         const src_elem_ty = src_ty.scalarType(mod);
  28045         const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src);
  28046         if (child != .ok) {
  28047             return InMemoryCoercionResult{ .vector_elem = .{
  28048                 .child = try child.dupe(sema.arena),
  28049                 .actual = src_elem_ty,
  28050                 .wanted = dest_elem_ty,
  28051             } };
  28052         }
  28053 
  28054         return .ok;
  28055     }
  28056 
  28057     // Optionals
  28058     if (dest_tag == .Optional and src_tag == .Optional) {
  28059         if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
  28060             return InMemoryCoercionResult{ .optional_shape = .{
  28061                 .actual = src_ty,
  28062                 .wanted = dest_ty,
  28063             } };
  28064         }
  28065         const dest_child_type = dest_ty.optionalChild(mod);
  28066         const src_child_type = src_ty.optionalChild(mod);
  28067 
  28068         const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src);
  28069         if (child != .ok) {
  28070             return InMemoryCoercionResult{ .optional_child = .{
  28071                 .child = try child.dupe(sema.arena),
  28072                 .actual = src_child_type,
  28073                 .wanted = dest_child_type,
  28074             } };
  28075         }
  28076 
  28077         return .ok;
  28078     }
  28079 
  28080     // Tuples (with in-memory-coercible fields)
  28081     if (dest_ty.isTuple(mod) and src_ty.isTuple(mod)) tuple: {
  28082         if (dest_ty.containerLayout(mod) != src_ty.containerLayout(mod)) break :tuple;
  28083         if (dest_ty.structFieldCount(mod) != src_ty.structFieldCount(mod)) break :tuple;
  28084         const field_count = dest_ty.structFieldCount(mod);
  28085         for (0..field_count) |field_idx| {
  28086             if (dest_ty.structFieldIsComptime(field_idx, mod) != src_ty.structFieldIsComptime(field_idx, mod)) break :tuple;
  28087             if (dest_ty.structFieldAlign(field_idx, mod) != src_ty.structFieldAlign(field_idx, mod)) break :tuple;
  28088             const dest_field_ty = dest_ty.structFieldType(field_idx, mod);
  28089             const src_field_ty = src_ty.structFieldType(field_idx, mod);
  28090             const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src);
  28091             if (field != .ok) break :tuple;
  28092         }
  28093         return .ok;
  28094     }
  28095 
  28096     return InMemoryCoercionResult{ .no_match = .{
  28097         .actual = dest_ty,
  28098         .wanted = src_ty,
  28099     } };
  28100 }
  28101 
  28102 fn coerceInMemoryAllowedErrorSets(
  28103     sema: *Sema,
  28104     block: *Block,
  28105     dest_ty: Type,
  28106     src_ty: Type,
  28107     dest_src: LazySrcLoc,
  28108     src_src: LazySrcLoc,
  28109 ) !InMemoryCoercionResult {
  28110     const mod = sema.mod;
  28111     const gpa = sema.gpa;
  28112     const ip = &mod.intern_pool;
  28113 
  28114     // Coercion to `anyerror`. Note that this check can return false negatives
  28115     // in case the error sets did not get resolved.
  28116     if (dest_ty.isAnyError(mod)) {
  28117         return .ok;
  28118     }
  28119 
  28120     if (mod.typeToInferredErrorSetIndex(dest_ty).unwrap()) |dst_ies_index| {
  28121         const dst_ies = mod.inferredErrorSetPtr(dst_ies_index);
  28122         // We will make an effort to return `ok` without resolving either error set, to
  28123         // avoid unnecessary "unable to resolve error set" dependency loop errors.
  28124         switch (src_ty.toIntern()) {
  28125             .anyerror_type => {},
  28126             else => switch (ip.indexToKey(src_ty.toIntern())) {
  28127                 .inferred_error_set_type => |src_index| {
  28128                     // If both are inferred error sets of functions, and
  28129                     // the dest includes the source function, the coercion is OK.
  28130                     // This check is important because it works without forcing a full resolution
  28131                     // of inferred error sets.
  28132                     if (dst_ies.inferred_error_sets.contains(src_index)) {
  28133                         return .ok;
  28134                     }
  28135                 },
  28136                 .error_set_type => |error_set_type| {
  28137                     for (error_set_type.names) |name| {
  28138                         if (!dst_ies.errors.contains(name)) break;
  28139                     } else return .ok;
  28140                 },
  28141                 else => unreachable,
  28142             },
  28143         }
  28144 
  28145         if (dst_ies.func == sema.owner_func_index.unwrap()) {
  28146             // We are trying to coerce an error set to the current function's
  28147             // inferred error set.
  28148             try dst_ies.addErrorSet(src_ty, ip, gpa);
  28149             return .ok;
  28150         }
  28151 
  28152         try sema.resolveInferredErrorSet(block, dest_src, dst_ies_index);
  28153         // isAnyError might have changed from a false negative to a true positive after resolution.
  28154         if (dest_ty.isAnyError(mod)) {
  28155             return .ok;
  28156         }
  28157     }
  28158 
  28159     var missing_error_buf = std.ArrayList(InternPool.NullTerminatedString).init(gpa);
  28160     defer missing_error_buf.deinit();
  28161 
  28162     switch (src_ty.toIntern()) {
  28163         .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) {
  28164             .simple_type => unreachable, // filtered out above
  28165             .error_set_type, .inferred_error_set_type => return .from_anyerror,
  28166             else => unreachable,
  28167         },
  28168 
  28169         else => switch (ip.indexToKey(src_ty.toIntern())) {
  28170             .inferred_error_set_type => |src_index| {
  28171                 const src_data = mod.inferredErrorSetPtr(src_index);
  28172 
  28173                 try sema.resolveInferredErrorSet(block, src_src, src_index);
  28174                 // src anyerror status might have changed after the resolution.
  28175                 if (src_ty.isAnyError(mod)) {
  28176                     // dest_ty.isAnyError(mod) == true is already checked for at this point.
  28177                     return .from_anyerror;
  28178                 }
  28179 
  28180                 for (src_data.errors.keys()) |key| {
  28181                     if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) {
  28182                         try missing_error_buf.append(key);
  28183                     }
  28184                 }
  28185 
  28186                 if (missing_error_buf.items.len != 0) {
  28187                     return InMemoryCoercionResult{
  28188                         .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
  28189                     };
  28190                 }
  28191 
  28192                 return .ok;
  28193             },
  28194             .error_set_type => |error_set_type| {
  28195                 for (error_set_type.names) |name| {
  28196                     if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) {
  28197                         try missing_error_buf.append(name);
  28198                     }
  28199                 }
  28200 
  28201                 if (missing_error_buf.items.len != 0) {
  28202                     return InMemoryCoercionResult{
  28203                         .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
  28204                     };
  28205                 }
  28206 
  28207                 return .ok;
  28208             },
  28209             else => unreachable,
  28210         },
  28211     }
  28212 }
  28213 
  28214 fn coerceInMemoryAllowedFns(
  28215     sema: *Sema,
  28216     block: *Block,
  28217     dest_ty: Type,
  28218     src_ty: Type,
  28219     target: std.Target,
  28220     dest_src: LazySrcLoc,
  28221     src_src: LazySrcLoc,
  28222 ) !InMemoryCoercionResult {
  28223     const mod = sema.mod;
  28224 
  28225     {
  28226         const dest_info = mod.typeToFunc(dest_ty).?;
  28227         const src_info = mod.typeToFunc(src_ty).?;
  28228 
  28229         if (dest_info.is_var_args != src_info.is_var_args) {
  28230             return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args };
  28231         }
  28232 
  28233         if (dest_info.is_generic != src_info.is_generic) {
  28234             return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic };
  28235         }
  28236 
  28237         if (dest_info.cc != src_info.cc) {
  28238             return InMemoryCoercionResult{ .fn_cc = .{
  28239                 .actual = src_info.cc,
  28240                 .wanted = dest_info.cc,
  28241             } };
  28242         }
  28243 
  28244         switch (src_info.return_type) {
  28245             .noreturn_type, .generic_poison_type => {},
  28246             else => {
  28247                 const dest_return_type = dest_info.return_type.toType();
  28248                 const src_return_type = src_info.return_type.toType();
  28249                 const rt = try sema.coerceInMemoryAllowed(block, dest_return_type, src_return_type, false, target, dest_src, src_src);
  28250                 if (rt != .ok) {
  28251                     return InMemoryCoercionResult{ .fn_return_type = .{
  28252                         .child = try rt.dupe(sema.arena),
  28253                         .actual = src_return_type,
  28254                         .wanted = dest_return_type,
  28255                     } };
  28256                 }
  28257             },
  28258         }
  28259     }
  28260 
  28261     const params_len = params_len: {
  28262         const dest_info = mod.typeToFunc(dest_ty).?;
  28263         const src_info = mod.typeToFunc(src_ty).?;
  28264 
  28265         if (dest_info.param_types.len != src_info.param_types.len) {
  28266             return InMemoryCoercionResult{ .fn_param_count = .{
  28267                 .actual = src_info.param_types.len,
  28268                 .wanted = dest_info.param_types.len,
  28269             } };
  28270         }
  28271 
  28272         if (dest_info.noalias_bits != src_info.noalias_bits) {
  28273             return InMemoryCoercionResult{ .fn_param_noalias = .{
  28274                 .actual = src_info.noalias_bits,
  28275                 .wanted = dest_info.noalias_bits,
  28276             } };
  28277         }
  28278 
  28279         break :params_len dest_info.param_types.len;
  28280     };
  28281 
  28282     for (0..params_len) |param_i| {
  28283         const dest_info = mod.typeToFunc(dest_ty).?;
  28284         const src_info = mod.typeToFunc(src_ty).?;
  28285 
  28286         const dest_param_ty = dest_info.param_types[param_i].toType();
  28287         const src_param_ty = src_info.param_types[param_i].toType();
  28288 
  28289         const param_i_small = @as(u5, @intCast(param_i));
  28290         if (dest_info.paramIsComptime(param_i_small) != src_info.paramIsComptime(param_i_small)) {
  28291             return InMemoryCoercionResult{ .fn_param_comptime = .{
  28292                 .index = param_i,
  28293                 .wanted = dest_info.paramIsComptime(param_i_small),
  28294             } };
  28295         }
  28296 
  28297         switch (src_param_ty.toIntern()) {
  28298             .generic_poison_type => {},
  28299             else => {
  28300                 // Note: Cast direction is reversed here.
  28301                 const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src);
  28302                 if (param != .ok) {
  28303                     return InMemoryCoercionResult{ .fn_param = .{
  28304                         .child = try param.dupe(sema.arena),
  28305                         .actual = src_param_ty,
  28306                         .wanted = dest_param_ty,
  28307                         .index = param_i,
  28308                     } };
  28309                 }
  28310             },
  28311         }
  28312     }
  28313 
  28314     return .ok;
  28315 }
  28316 
  28317 fn coerceInMemoryAllowedPtrs(
  28318     sema: *Sema,
  28319     block: *Block,
  28320     dest_ty: Type,
  28321     src_ty: Type,
  28322     dest_ptr_ty: Type,
  28323     src_ptr_ty: Type,
  28324     dest_is_mut: bool,
  28325     target: std.Target,
  28326     dest_src: LazySrcLoc,
  28327     src_src: LazySrcLoc,
  28328 ) !InMemoryCoercionResult {
  28329     const mod = sema.mod;
  28330     const dest_info = dest_ptr_ty.ptrInfo(mod);
  28331     const src_info = src_ptr_ty.ptrInfo(mod);
  28332 
  28333     const ok_ptr_size = src_info.flags.size == dest_info.flags.size or
  28334         src_info.flags.size == .C or dest_info.flags.size == .C;
  28335     if (!ok_ptr_size) {
  28336         return InMemoryCoercionResult{ .ptr_size = .{
  28337             .actual = src_info.flags.size,
  28338             .wanted = dest_info.flags.size,
  28339         } };
  28340     }
  28341 
  28342     const ok_cv_qualifiers =
  28343         (!src_info.flags.is_const or dest_info.flags.is_const) and
  28344         (!src_info.flags.is_volatile or dest_info.flags.is_volatile);
  28345 
  28346     if (!ok_cv_qualifiers) {
  28347         return InMemoryCoercionResult{ .ptr_qualifiers = .{
  28348             .actual_const = src_info.flags.is_const,
  28349             .wanted_const = dest_info.flags.is_const,
  28350             .actual_volatile = src_info.flags.is_volatile,
  28351             .wanted_volatile = dest_info.flags.is_volatile,
  28352         } };
  28353     }
  28354 
  28355     if (dest_info.flags.address_space != src_info.flags.address_space) {
  28356         return InMemoryCoercionResult{ .ptr_addrspace = .{
  28357             .actual = src_info.flags.address_space,
  28358             .wanted = dest_info.flags.address_space,
  28359         } };
  28360     }
  28361 
  28362     const child = try sema.coerceInMemoryAllowed(block, dest_info.child.toType(), src_info.child.toType(), !dest_info.flags.is_const, target, dest_src, src_src);
  28363     if (child != .ok) {
  28364         return InMemoryCoercionResult{ .ptr_child = .{
  28365             .child = try child.dupe(sema.arena),
  28366             .actual = src_info.child.toType(),
  28367             .wanted = dest_info.child.toType(),
  28368         } };
  28369     }
  28370 
  28371     const dest_allow_zero = dest_ty.ptrAllowsZero(mod);
  28372     const src_allow_zero = src_ty.ptrAllowsZero(mod);
  28373 
  28374     const ok_allows_zero = (dest_allow_zero and
  28375         (src_allow_zero or !dest_is_mut)) or
  28376         (!dest_allow_zero and !src_allow_zero);
  28377     if (!ok_allows_zero) {
  28378         return InMemoryCoercionResult{ .ptr_allowzero = .{
  28379             .actual = src_ty,
  28380             .wanted = dest_ty,
  28381         } };
  28382     }
  28383 
  28384     if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or
  28385         src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset)
  28386     {
  28387         return InMemoryCoercionResult{ .ptr_bit_range = .{
  28388             .actual_host = src_info.packed_offset.host_size,
  28389             .wanted_host = dest_info.packed_offset.host_size,
  28390             .actual_offset = src_info.packed_offset.bit_offset,
  28391             .wanted_offset = dest_info.packed_offset.bit_offset,
  28392         } };
  28393     }
  28394 
  28395     const ok_sent = dest_info.sentinel == .none or src_info.flags.size == .C or
  28396         (src_info.sentinel != .none and
  28397         dest_info.sentinel == try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child));
  28398     if (!ok_sent) {
  28399         return InMemoryCoercionResult{ .ptr_sentinel = .{
  28400             .actual = switch (src_info.sentinel) {
  28401                 .none => Value.@"unreachable",
  28402                 else => src_info.sentinel.toValue(),
  28403             },
  28404             .wanted = switch (dest_info.sentinel) {
  28405                 .none => Value.@"unreachable",
  28406                 else => dest_info.sentinel.toValue(),
  28407             },
  28408             .ty = dest_info.child.toType(),
  28409         } };
  28410     }
  28411 
  28412     // If both pointers have alignment 0, it means they both want ABI alignment.
  28413     // In this case, if they share the same child type, no need to resolve
  28414     // pointee type alignment. Otherwise both pointee types must have their alignment
  28415     // resolved and we compare the alignment numerically.
  28416     if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or
  28417         dest_info.child != src_info.child)
  28418     {
  28419         const src_align = src_info.flags.alignment.toByteUnitsOptional() orelse
  28420             src_info.child.toType().abiAlignment(mod);
  28421 
  28422         const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse
  28423             dest_info.child.toType().abiAlignment(mod);
  28424 
  28425         if (dest_align > src_align) {
  28426             return InMemoryCoercionResult{ .ptr_alignment = .{
  28427                 .actual = src_align,
  28428                 .wanted = dest_align,
  28429             } };
  28430         }
  28431     }
  28432 
  28433     return .ok;
  28434 }
  28435 
  28436 fn coerceVarArgParam(
  28437     sema: *Sema,
  28438     block: *Block,
  28439     inst: Air.Inst.Ref,
  28440     inst_src: LazySrcLoc,
  28441 ) !Air.Inst.Ref {
  28442     if (block.is_typeof) return inst;
  28443 
  28444     const mod = sema.mod;
  28445     const uncasted_ty = sema.typeOf(inst);
  28446     const coerced = switch (uncasted_ty.zigTypeTag(mod)) {
  28447         // TODO consider casting to c_int/f64 if they fit
  28448         .ComptimeInt, .ComptimeFloat => return sema.fail(
  28449             block,
  28450             inst_src,
  28451             "integer and float literals passed to variadic function must be casted to a fixed-size number type",
  28452             .{},
  28453         ),
  28454         .Fn => blk: {
  28455             const fn_val = try sema.resolveConstValue(block, .unneeded, inst, "");
  28456             const fn_decl = fn_val.pointerDecl(mod).?;
  28457             break :blk try sema.analyzeDeclRef(fn_decl);
  28458         },
  28459         .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
  28460         .Float => float: {
  28461             const target = sema.mod.getTarget();
  28462             const double_bits = target.c_type_bit_size(.double);
  28463             const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget());
  28464             if (inst_bits >= double_bits) break :float inst;
  28465             switch (double_bits) {
  28466                 32 => break :float try sema.coerce(block, Type.f32, inst, inst_src),
  28467                 64 => break :float try sema.coerce(block, Type.f64, inst, inst_src),
  28468                 else => unreachable,
  28469             }
  28470         },
  28471         else => inst,
  28472     };
  28473 
  28474     const coerced_ty = sema.typeOf(coerced);
  28475     if (!try sema.validateExternType(coerced_ty, .param_ty)) {
  28476         const msg = msg: {
  28477             const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)});
  28478             errdefer msg.destroy(sema.gpa);
  28479 
  28480             const src_decl = sema.mod.declPtr(block.src_decl);
  28481             try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl, mod), coerced_ty, .param_ty);
  28482 
  28483             try sema.addDeclaredHereNote(msg, coerced_ty);
  28484             break :msg msg;
  28485         };
  28486         return sema.failWithOwnedErrorMsg(msg);
  28487     }
  28488     return coerced;
  28489 }
  28490 
  28491 // TODO migrate callsites to use storePtr2 instead.
  28492 fn storePtr(
  28493     sema: *Sema,
  28494     block: *Block,
  28495     src: LazySrcLoc,
  28496     ptr: Air.Inst.Ref,
  28497     uncasted_operand: Air.Inst.Ref,
  28498 ) CompileError!void {
  28499     const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store;
  28500     return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag);
  28501 }
  28502 
  28503 fn storePtr2(
  28504     sema: *Sema,
  28505     block: *Block,
  28506     src: LazySrcLoc,
  28507     ptr: Air.Inst.Ref,
  28508     ptr_src: LazySrcLoc,
  28509     uncasted_operand: Air.Inst.Ref,
  28510     operand_src: LazySrcLoc,
  28511     air_tag: Air.Inst.Tag,
  28512 ) CompileError!void {
  28513     const mod = sema.mod;
  28514     const ptr_ty = sema.typeOf(ptr);
  28515     if (ptr_ty.isConstPtr(mod))
  28516         return sema.fail(block, ptr_src, "cannot assign to constant", .{});
  28517 
  28518     const elem_ty = ptr_ty.childType(mod);
  28519 
  28520     // To generate better code for tuples, we detect a tuple operand here, and
  28521     // analyze field loads and stores directly. This avoids an extra allocation + memcpy
  28522     // which would occur if we used `coerce`.
  28523     // However, we avoid this mechanism if the destination element type is a tuple,
  28524     // because the regular store will be better for this case.
  28525     // If the destination type is a struct we don't want this mechanism to trigger, because
  28526     // this code does not handle tuple-to-struct coercion which requires dealing with missing
  28527     // fields.
  28528     const operand_ty = sema.typeOf(uncasted_operand);
  28529     if (operand_ty.isTuple(mod) and elem_ty.zigTypeTag(mod) == .Array) {
  28530         const field_count = operand_ty.structFieldCount(mod);
  28531         var i: u32 = 0;
  28532         while (i < field_count) : (i += 1) {
  28533             const elem_src = operand_src; // TODO better source location
  28534             const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i);
  28535             const elem_index = try sema.addIntUnsigned(Type.usize, i);
  28536             const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true);
  28537             try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
  28538         }
  28539         return;
  28540     }
  28541 
  28542     // TODO do the same thing for anon structs as for tuples above.
  28543     // However, beware of the need to handle missing/extra fields.
  28544 
  28545     const is_ret = air_tag == .ret_ptr;
  28546 
  28547     // Detect if we are storing an array operand to a bitcasted vector pointer.
  28548     // If so, we instead reach through the bitcasted pointer to the vector pointer,
  28549     // bitcast the array operand to a vector, and then lower this as a store of
  28550     // a vector value to a vector pointer. This generally results in better code,
  28551     // as well as working around an LLVM bug:
  28552     // https://github.com/ziglang/zig/issues/11154
  28553     if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
  28554         const vector_ty = sema.typeOf(vector_ptr).childType(mod);
  28555         const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
  28556             error.NotCoercible => unreachable,
  28557             else => |e| return e,
  28558         };
  28559         try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store);
  28560         return;
  28561     }
  28562 
  28563     const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
  28564         error.NotCoercible => unreachable,
  28565         else => |e| return e,
  28566     };
  28567     const maybe_operand_val = try sema.resolveMaybeUndefVal(operand);
  28568 
  28569     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  28570         const operand_val = maybe_operand_val orelse {
  28571             try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src);
  28572             break :rs operand_src;
  28573         };
  28574         if (ptr_val.isComptimeMutablePtr(mod)) {
  28575             try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
  28576             return;
  28577         } else break :rs ptr_src;
  28578     } else ptr_src;
  28579 
  28580     // We do this after the possible comptime store above, for the case of field_ptr stores
  28581     // to unions because we want the comptime tag to be set, even if the field type is void.
  28582     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null)
  28583         return;
  28584 
  28585     if (air_tag == .bitcast) {
  28586         // `air_tag == .bitcast` is used as a special case for `zirCoerceResultPtr`
  28587         // to avoid calling `requireRuntimeBlock` for the dummy block.
  28588         _ = try block.addBinOp(.store, ptr, operand);
  28589         return;
  28590     }
  28591 
  28592     try sema.requireRuntimeBlock(block, src, runtime_src);
  28593     try sema.queueFullTypeResolution(elem_ty);
  28594 
  28595     if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) {
  28596         const ptr_inst = Air.refToIndex(ptr).?;
  28597         const air_tags = sema.air_instructions.items(.tag);
  28598         if (air_tags[ptr_inst] == .ptr_elem_ptr) {
  28599             const ty_pl = sema.air_instructions.items(.data)[ptr_inst].ty_pl;
  28600             const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data;
  28601             _ = try block.addInst(.{
  28602                 .tag = .vector_store_elem,
  28603                 .data = .{ .vector_store_elem = .{
  28604                     .vector_ptr = bin_op.lhs,
  28605                     .payload = try block.sema.addExtra(Air.Bin{
  28606                         .lhs = bin_op.rhs,
  28607                         .rhs = operand,
  28608                     }),
  28609                 } },
  28610             });
  28611             return;
  28612         }
  28613         return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{
  28614             ptr_ty.fmt(sema.mod),
  28615         });
  28616     }
  28617 
  28618     if (is_ret) {
  28619         _ = try block.addBinOp(.store, ptr, operand);
  28620     } else {
  28621         _ = try block.addBinOp(air_tag, ptr, operand);
  28622     }
  28623 }
  28624 
  28625 /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector
  28626 /// pointer. Only if the final element type matches the vector element type, and the
  28627 /// lengths match.
  28628 fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref {
  28629     const mod = sema.mod;
  28630     const array_ty = sema.typeOf(ptr).childType(mod);
  28631     if (array_ty.zigTypeTag(mod) != .Array) return null;
  28632     var ptr_ref = ptr;
  28633     var ptr_inst = Air.refToIndex(ptr_ref) orelse return null;
  28634     const air_datas = sema.air_instructions.items(.data);
  28635     const air_tags = sema.air_instructions.items(.tag);
  28636     const vector_ty = while (air_tags[ptr_inst] == .bitcast) {
  28637         ptr_ref = air_datas[ptr_inst].ty_op.operand;
  28638         if (!sema.isKnownZigType(ptr_ref, .Pointer)) return null;
  28639         const child_ty = sema.typeOf(ptr_ref).childType(mod);
  28640         if (child_ty.zigTypeTag(mod) == .Vector) break child_ty;
  28641         ptr_inst = Air.refToIndex(ptr_ref) orelse return null;
  28642     } else return null;
  28643 
  28644     // We have a pointer-to-array and a pointer-to-vector. If the elements and
  28645     // lengths match, return the result.
  28646     if (array_ty.childType(mod).eql(vector_ty.childType(mod), sema.mod) and
  28647         array_ty.arrayLen(mod) == vector_ty.vectorLen(mod))
  28648     {
  28649         return ptr_ref;
  28650     } else {
  28651         return null;
  28652     }
  28653 }
  28654 
  28655 /// Call when you have Value objects rather than Air instructions, and you want to
  28656 /// assert the store must be done at comptime.
  28657 fn storePtrVal(
  28658     sema: *Sema,
  28659     block: *Block,
  28660     src: LazySrcLoc,
  28661     ptr_val: Value,
  28662     operand_val: Value,
  28663     operand_ty: Type,
  28664 ) !void {
  28665     const mod = sema.mod;
  28666     var mut_kit = try sema.beginComptimePtrMutation(block, src, ptr_val, operand_ty);
  28667     try sema.checkComptimeVarStore(block, src, mut_kit.mut_decl);
  28668 
  28669     switch (mut_kit.pointee) {
  28670         .direct => |val_ptr| {
  28671             if (mut_kit.mut_decl.runtime_index == .comptime_field_ptr) {
  28672                 if (!operand_val.eql(val_ptr.*, operand_ty, mod)) {
  28673                     // TODO use failWithInvalidComptimeFieldStore
  28674                     return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});
  28675                 }
  28676                 return;
  28677             }
  28678             val_ptr.* = (try operand_val.intern(operand_ty, mod)).toValue();
  28679         },
  28680         .reinterpret => |reinterpret| {
  28681             const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(mod));
  28682             const buffer = try sema.gpa.alloc(u8, abi_size);
  28683             defer sema.gpa.free(buffer);
  28684             reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, mod, buffer) catch |err| switch (err) {
  28685                 error.OutOfMemory => return error.OutOfMemory,
  28686                 error.ReinterpretDeclRef => unreachable,
  28687                 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
  28688                 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
  28689             };
  28690             operand_val.writeToMemory(operand_ty, mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) {
  28691                 error.OutOfMemory => return error.OutOfMemory,
  28692                 error.ReinterpretDeclRef => unreachable,
  28693                 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
  28694                 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
  28695             };
  28696 
  28697             reinterpret.val_ptr.* = (try (try Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena)).intern(mut_kit.ty, mod)).toValue();
  28698         },
  28699         .bad_decl_ty, .bad_ptr_ty => {
  28700             // TODO show the decl declaration site in a note and explain whether the decl
  28701             // or the pointer is the problematic type
  28702             return sema.fail(
  28703                 block,
  28704                 src,
  28705                 "comptime mutation of a reinterpreted pointer requires type '{}' to have a well-defined memory layout",
  28706                 .{mut_kit.ty.fmt(mod)},
  28707             );
  28708         },
  28709     }
  28710 }
  28711 
  28712 const ComptimePtrMutationKit = struct {
  28713     mut_decl: InternPool.Key.Ptr.Addr.MutDecl,
  28714     pointee: union(enum) {
  28715         /// The pointer type matches the actual comptime Value so a direct
  28716         /// modification is possible.
  28717         direct: *Value,
  28718         /// The largest parent Value containing pointee and having a well-defined memory layout.
  28719         /// This is used for bitcasting, if direct dereferencing failed.
  28720         reinterpret: struct {
  28721             val_ptr: *Value,
  28722             byte_offset: usize,
  28723         },
  28724         /// If the root decl could not be used as parent, this means `ty` is the type that
  28725         /// caused that by not having a well-defined layout.
  28726         /// This one means the Decl that owns the value trying to be modified does not
  28727         /// have a well defined memory layout.
  28728         bad_decl_ty,
  28729         /// If the root decl could not be used as parent, this means `ty` is the type that
  28730         /// caused that by not having a well-defined layout.
  28731         /// This one means the pointer type that is being stored through does not
  28732         /// have a well defined memory layout.
  28733         bad_ptr_ty,
  28734     },
  28735     ty: Type,
  28736 };
  28737 
  28738 fn beginComptimePtrMutation(
  28739     sema: *Sema,
  28740     block: *Block,
  28741     src: LazySrcLoc,
  28742     ptr_val: Value,
  28743     ptr_elem_ty: Type,
  28744 ) CompileError!ComptimePtrMutationKit {
  28745     const mod = sema.mod;
  28746     const ptr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr;
  28747     switch (ptr.addr) {
  28748         .decl, .int => unreachable, // isComptimeMutablePtr has been checked already
  28749         .mut_decl => |mut_decl| {
  28750             const decl = mod.declPtr(mut_decl.decl);
  28751             return sema.beginComptimePtrMutationInner(block, src, decl.ty, &decl.val, ptr_elem_ty, mut_decl);
  28752         },
  28753         .comptime_field => |comptime_field| {
  28754             const duped = try sema.arena.create(Value);
  28755             duped.* = comptime_field.toValue();
  28756             return sema.beginComptimePtrMutationInner(block, src, mod.intern_pool.typeOf(comptime_field).toType(), duped, ptr_elem_ty, .{
  28757                 .decl = undefined,
  28758                 .runtime_index = .comptime_field_ptr,
  28759             });
  28760         },
  28761         .eu_payload => |eu_ptr| {
  28762             const eu_ty = mod.intern_pool.typeOf(eu_ptr).toType().childType(mod);
  28763             var parent = try sema.beginComptimePtrMutation(block, src, eu_ptr.toValue(), eu_ty);
  28764             switch (parent.pointee) {
  28765                 .direct => |val_ptr| {
  28766                     const payload_ty = parent.ty.errorUnionPayload(mod);
  28767                     if (val_ptr.ip_index == .none and val_ptr.tag() == .eu_payload) {
  28768                         return ComptimePtrMutationKit{
  28769                             .mut_decl = parent.mut_decl,
  28770                             .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data },
  28771                             .ty = payload_ty,
  28772                         };
  28773                     } else {
  28774                         // An error union has been initialized to undefined at comptime and now we
  28775                         // are for the first time setting the payload. We must change the
  28776                         // representation of the error union from `undef` to `opt_payload`.
  28777 
  28778                         const payload = try sema.arena.create(Value.Payload.SubValue);
  28779                         payload.* = .{
  28780                             .base = .{ .tag = .eu_payload },
  28781                             .data = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(),
  28782                         };
  28783 
  28784                         val_ptr.* = Value.initPayload(&payload.base);
  28785 
  28786                         return ComptimePtrMutationKit{
  28787                             .mut_decl = parent.mut_decl,
  28788                             .pointee = .{ .direct = &payload.data },
  28789                             .ty = payload_ty,
  28790                         };
  28791                     }
  28792                 },
  28793                 .bad_decl_ty, .bad_ptr_ty => return parent,
  28794                 // Even though the parent value type has well-defined memory layout, our
  28795                 // pointer type does not.
  28796                 .reinterpret => return ComptimePtrMutationKit{
  28797                     .mut_decl = parent.mut_decl,
  28798                     .pointee = .bad_ptr_ty,
  28799                     .ty = eu_ty,
  28800                 },
  28801             }
  28802         },
  28803         .opt_payload => |opt_ptr| {
  28804             const opt_ty = mod.intern_pool.typeOf(opt_ptr).toType().childType(mod);
  28805             var parent = try sema.beginComptimePtrMutation(block, src, opt_ptr.toValue(), opt_ty);
  28806             switch (parent.pointee) {
  28807                 .direct => |val_ptr| {
  28808                     const payload_ty = parent.ty.optionalChild(mod);
  28809                     switch (val_ptr.ip_index) {
  28810                         .none => return ComptimePtrMutationKit{
  28811                             .mut_decl = parent.mut_decl,
  28812                             .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data },
  28813                             .ty = payload_ty,
  28814                         },
  28815                         else => {
  28816                             const payload_val = switch (mod.intern_pool.indexToKey(val_ptr.ip_index)) {
  28817                                 .undef => try mod.intern(.{ .undef = payload_ty.toIntern() }),
  28818                                 .opt => |opt| switch (opt.val) {
  28819                                     .none => try mod.intern(.{ .undef = payload_ty.toIntern() }),
  28820                                     else => |payload| payload,
  28821                                 },
  28822                                 else => unreachable,
  28823                             };
  28824 
  28825                             // An optional has been initialized to undefined at comptime and now we
  28826                             // are for the first time setting the payload. We must change the
  28827                             // representation of the optional from `undef` to `opt_payload`.
  28828 
  28829                             const payload = try sema.arena.create(Value.Payload.SubValue);
  28830                             payload.* = .{
  28831                                 .base = .{ .tag = .opt_payload },
  28832                                 .data = payload_val.toValue(),
  28833                             };
  28834 
  28835                             val_ptr.* = Value.initPayload(&payload.base);
  28836 
  28837                             return ComptimePtrMutationKit{
  28838                                 .mut_decl = parent.mut_decl,
  28839                                 .pointee = .{ .direct = &payload.data },
  28840                                 .ty = payload_ty,
  28841                             };
  28842                         },
  28843                     }
  28844                 },
  28845                 .bad_decl_ty, .bad_ptr_ty => return parent,
  28846                 // Even though the parent value type has well-defined memory layout, our
  28847                 // pointer type does not.
  28848                 .reinterpret => return ComptimePtrMutationKit{
  28849                     .mut_decl = parent.mut_decl,
  28850                     .pointee = .bad_ptr_ty,
  28851                     .ty = opt_ty,
  28852                 },
  28853             }
  28854         },
  28855         .elem => |elem_ptr| {
  28856             const base_elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod);
  28857             var parent = try sema.beginComptimePtrMutation(block, src, elem_ptr.base.toValue(), base_elem_ty);
  28858 
  28859             switch (parent.pointee) {
  28860                 .direct => |val_ptr| switch (parent.ty.zigTypeTag(mod)) {
  28861                     .Array, .Vector => {
  28862                         const check_len = parent.ty.arrayLenIncludingSentinel(mod);
  28863                         if (elem_ptr.index >= check_len) {
  28864                             // TODO have the parent include the decl so we can say "declared here"
  28865                             return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
  28866                                 elem_ptr.index, check_len,
  28867                             });
  28868                         }
  28869                         const elem_ty = parent.ty.childType(mod);
  28870 
  28871                         // We might have a pointer to multiple elements of the array (e.g. a pointer
  28872                         // to a sub-array). In this case, we just have to reinterpret the relevant
  28873                         // bytes of the whole array rather than any single element.
  28874                         const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty);
  28875                         if (elem_abi_size_u64 < try sema.typeAbiSize(ptr_elem_ty)) {
  28876                             const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
  28877                             const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
  28878                             return .{
  28879                                 .mut_decl = parent.mut_decl,
  28880                                 .pointee = .{ .reinterpret = .{
  28881                                     .val_ptr = val_ptr,
  28882                                     .byte_offset = elem_abi_size * elem_idx,
  28883                                 } },
  28884                                 .ty = parent.ty,
  28885                             };
  28886                         }
  28887 
  28888                         switch (val_ptr.ip_index) {
  28889                             .none => switch (val_ptr.tag()) {
  28890                                 .bytes => {
  28891                                     // An array is memory-optimized to store a slice of bytes, but we are about
  28892                                     // to modify an individual field and the representation has to change.
  28893                                     // If we wanted to avoid this, there would need to be special detection
  28894                                     // elsewhere to identify when writing a value to an array element that is stored
  28895                                     // using the `bytes` tag, and handle it without making a call to this function.
  28896                                     const arena = mod.tmp_hack_arena.allocator();
  28897 
  28898                                     const bytes = val_ptr.castTag(.bytes).?.data;
  28899                                     const dest_len = parent.ty.arrayLenIncludingSentinel(mod);
  28900                                     // bytes.len may be one greater than dest_len because of the case when
  28901                                     // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
  28902                                     assert(bytes.len >= dest_len);
  28903                                     const elems = try arena.alloc(Value, @as(usize, @intCast(dest_len)));
  28904                                     for (elems, 0..) |*elem, i| {
  28905                                         elem.* = try mod.intValue(elem_ty, bytes[i]);
  28906                                     }
  28907 
  28908                                     val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  28909 
  28910                                     return beginComptimePtrMutationInner(
  28911                                         sema,
  28912                                         block,
  28913                                         src,
  28914                                         elem_ty,
  28915                                         &elems[@as(usize, @intCast(elem_ptr.index))],
  28916                                         ptr_elem_ty,
  28917                                         parent.mut_decl,
  28918                                     );
  28919                                 },
  28920                                 .repeated => {
  28921                                     // An array is memory-optimized to store only a single element value, and
  28922                                     // that value is understood to be the same for the entire length of the array.
  28923                                     // However, now we want to modify an individual field and so the
  28924                                     // representation has to change.  If we wanted to avoid this, there would
  28925                                     // need to be special detection elsewhere to identify when writing a value to an
  28926                                     // array element that is stored using the `repeated` tag, and handle it
  28927                                     // without making a call to this function.
  28928                                     const arena = mod.tmp_hack_arena.allocator();
  28929 
  28930                                     const repeated_val = try val_ptr.castTag(.repeated).?.data.intern(parent.ty.childType(mod), mod);
  28931                                     const array_len_including_sentinel =
  28932                                         try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
  28933                                     const elems = try arena.alloc(Value, array_len_including_sentinel);
  28934                                     @memset(elems, repeated_val.toValue());
  28935 
  28936                                     val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  28937 
  28938                                     return beginComptimePtrMutationInner(
  28939                                         sema,
  28940                                         block,
  28941                                         src,
  28942                                         elem_ty,
  28943                                         &elems[@as(usize, @intCast(elem_ptr.index))],
  28944                                         ptr_elem_ty,
  28945                                         parent.mut_decl,
  28946                                     );
  28947                                 },
  28948 
  28949                                 .aggregate => return beginComptimePtrMutationInner(
  28950                                     sema,
  28951                                     block,
  28952                                     src,
  28953                                     elem_ty,
  28954                                     &val_ptr.castTag(.aggregate).?.data[@as(usize, @intCast(elem_ptr.index))],
  28955                                     ptr_elem_ty,
  28956                                     parent.mut_decl,
  28957                                 ),
  28958 
  28959                                 else => unreachable,
  28960                             },
  28961                             else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
  28962                                 .undef => {
  28963                                     // An array has been initialized to undefined at comptime and now we
  28964                                     // are for the first time setting an element. We must change the representation
  28965                                     // of the array from `undef` to `array`.
  28966                                     const arena = mod.tmp_hack_arena.allocator();
  28967 
  28968                                     const array_len_including_sentinel =
  28969                                         try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
  28970                                     const elems = try arena.alloc(Value, array_len_including_sentinel);
  28971                                     @memset(elems, (try mod.intern(.{ .undef = elem_ty.toIntern() })).toValue());
  28972 
  28973                                     val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  28974 
  28975                                     return beginComptimePtrMutationInner(
  28976                                         sema,
  28977                                         block,
  28978                                         src,
  28979                                         elem_ty,
  28980                                         &elems[@as(usize, @intCast(elem_ptr.index))],
  28981                                         ptr_elem_ty,
  28982                                         parent.mut_decl,
  28983                                     );
  28984                                 },
  28985                                 else => unreachable,
  28986                             },
  28987                         }
  28988                     },
  28989                     else => {
  28990                         if (elem_ptr.index != 0) {
  28991                             // TODO include a "declared here" note for the decl
  28992                             return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
  28993                                 elem_ptr.index,
  28994                             });
  28995                         }
  28996                         return beginComptimePtrMutationInner(
  28997                             sema,
  28998                             block,
  28999                             src,
  29000                             parent.ty,
  29001                             val_ptr,
  29002                             ptr_elem_ty,
  29003                             parent.mut_decl,
  29004                         );
  29005                     },
  29006                 },
  29007                 .reinterpret => |reinterpret| {
  29008                     if (!base_elem_ty.hasWellDefinedLayout(mod)) {
  29009                         // Even though the parent value type has well-defined memory layout, our
  29010                         // pointer type does not.
  29011                         return ComptimePtrMutationKit{
  29012                             .mut_decl = parent.mut_decl,
  29013                             .pointee = .bad_ptr_ty,
  29014                             .ty = base_elem_ty,
  29015                         };
  29016                     }
  29017 
  29018                     const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty);
  29019                     const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
  29020                     const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
  29021                     return ComptimePtrMutationKit{
  29022                         .mut_decl = parent.mut_decl,
  29023                         .pointee = .{ .reinterpret = .{
  29024                             .val_ptr = reinterpret.val_ptr,
  29025                             .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_idx,
  29026                         } },
  29027                         .ty = parent.ty,
  29028                     };
  29029                 },
  29030                 .bad_decl_ty, .bad_ptr_ty => return parent,
  29031             }
  29032         },
  29033         .field => |field_ptr| {
  29034             const base_child_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod);
  29035             const field_index = @as(u32, @intCast(field_ptr.index));
  29036 
  29037             var parent = try sema.beginComptimePtrMutation(block, src, field_ptr.base.toValue(), base_child_ty);
  29038             switch (parent.pointee) {
  29039                 .direct => |val_ptr| switch (val_ptr.ip_index) {
  29040                     .empty_struct => {
  29041                         const duped = try sema.arena.create(Value);
  29042                         duped.* = val_ptr.*;
  29043                         return beginComptimePtrMutationInner(
  29044                             sema,
  29045                             block,
  29046                             src,
  29047                             parent.ty.structFieldType(field_index, mod),
  29048                             duped,
  29049                             ptr_elem_ty,
  29050                             parent.mut_decl,
  29051                         );
  29052                     },
  29053                     .none => switch (val_ptr.tag()) {
  29054                         .aggregate => return beginComptimePtrMutationInner(
  29055                             sema,
  29056                             block,
  29057                             src,
  29058                             parent.ty.structFieldType(field_index, mod),
  29059                             &val_ptr.castTag(.aggregate).?.data[field_index],
  29060                             ptr_elem_ty,
  29061                             parent.mut_decl,
  29062                         ),
  29063                         .repeated => {
  29064                             const arena = mod.tmp_hack_arena.allocator();
  29065 
  29066                             const elems = try arena.alloc(Value, parent.ty.structFieldCount(mod));
  29067                             @memset(elems, val_ptr.castTag(.repeated).?.data);
  29068                             val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
  29069 
  29070                             return beginComptimePtrMutationInner(
  29071                                 sema,
  29072                                 block,
  29073                                 src,
  29074                                 parent.ty.structFieldType(field_index, mod),
  29075                                 &elems[field_index],
  29076                                 ptr_elem_ty,
  29077                                 parent.mut_decl,
  29078                             );
  29079                         },
  29080                         .@"union" => {
  29081                             // We need to set the active field of the union.
  29082                             const union_tag_ty = base_child_ty.unionTagTypeHypothetical(mod);
  29083 
  29084                             const payload = &val_ptr.castTag(.@"union").?.data;
  29085                             payload.tag = try mod.enumValueFieldIndex(union_tag_ty, field_index);
  29086 
  29087                             return beginComptimePtrMutationInner(
  29088                                 sema,
  29089                                 block,
  29090                                 src,
  29091                                 parent.ty.structFieldType(field_index, mod),
  29092                                 &payload.val,
  29093                                 ptr_elem_ty,
  29094                                 parent.mut_decl,
  29095                             );
  29096                         },
  29097                         .slice => switch (field_index) {
  29098                             Value.slice_ptr_index => return beginComptimePtrMutationInner(
  29099                                 sema,
  29100                                 block,
  29101                                 src,
  29102                                 parent.ty.slicePtrFieldType(mod),
  29103                                 &val_ptr.castTag(.slice).?.data.ptr,
  29104                                 ptr_elem_ty,
  29105                                 parent.mut_decl,
  29106                             ),
  29107 
  29108                             Value.slice_len_index => return beginComptimePtrMutationInner(
  29109                                 sema,
  29110                                 block,
  29111                                 src,
  29112                                 Type.usize,
  29113                                 &val_ptr.castTag(.slice).?.data.len,
  29114                                 ptr_elem_ty,
  29115                                 parent.mut_decl,
  29116                             ),
  29117 
  29118                             else => unreachable,
  29119                         },
  29120                         else => unreachable,
  29121                     },
  29122                     else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
  29123                         .undef => {
  29124                             // A struct or union has been initialized to undefined at comptime and now we
  29125                             // are for the first time setting a field. We must change the representation
  29126                             // of the struct/union from `undef` to `struct`/`union`.
  29127                             const arena = mod.tmp_hack_arena.allocator();
  29128 
  29129                             switch (parent.ty.zigTypeTag(mod)) {
  29130                                 .Struct => {
  29131                                     const fields = try arena.alloc(Value, parent.ty.structFieldCount(mod));
  29132                                     for (fields, 0..) |*field, i| field.* = (try mod.intern(.{
  29133                                         .undef = parent.ty.structFieldType(i, mod).toIntern(),
  29134                                     })).toValue();
  29135 
  29136                                     val_ptr.* = try Value.Tag.aggregate.create(arena, fields);
  29137 
  29138                                     return beginComptimePtrMutationInner(
  29139                                         sema,
  29140                                         block,
  29141                                         src,
  29142                                         parent.ty.structFieldType(field_index, mod),
  29143                                         &fields[field_index],
  29144                                         ptr_elem_ty,
  29145                                         parent.mut_decl,
  29146                                     );
  29147                                 },
  29148                                 .Union => {
  29149                                     const payload = try arena.create(Value.Payload.Union);
  29150                                     const tag_ty = parent.ty.unionTagTypeHypothetical(mod);
  29151                                     const payload_ty = parent.ty.structFieldType(field_index, mod);
  29152                                     payload.* = .{ .data = .{
  29153                                         .tag = try mod.enumValueFieldIndex(tag_ty, field_index),
  29154                                         .val = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(),
  29155                                     } };
  29156 
  29157                                     val_ptr.* = Value.initPayload(&payload.base);
  29158 
  29159                                     return beginComptimePtrMutationInner(
  29160                                         sema,
  29161                                         block,
  29162                                         src,
  29163                                         payload_ty,
  29164                                         &payload.data.val,
  29165                                         ptr_elem_ty,
  29166                                         parent.mut_decl,
  29167                                     );
  29168                                 },
  29169                                 .Pointer => {
  29170                                     assert(parent.ty.isSlice(mod));
  29171                                     const ptr_ty = parent.ty.slicePtrFieldType(mod);
  29172                                     val_ptr.* = try Value.Tag.slice.create(arena, .{
  29173                                         .ptr = (try mod.intern(.{ .undef = ptr_ty.toIntern() })).toValue(),
  29174                                         .len = (try mod.intern(.{ .undef = .usize_type })).toValue(),
  29175                                     });
  29176 
  29177                                     switch (field_index) {
  29178                                         Value.slice_ptr_index => return beginComptimePtrMutationInner(
  29179                                             sema,
  29180                                             block,
  29181                                             src,
  29182                                             ptr_ty,
  29183                                             &val_ptr.castTag(.slice).?.data.ptr,
  29184                                             ptr_elem_ty,
  29185                                             parent.mut_decl,
  29186                                         ),
  29187                                         Value.slice_len_index => return beginComptimePtrMutationInner(
  29188                                             sema,
  29189                                             block,
  29190                                             src,
  29191                                             Type.usize,
  29192                                             &val_ptr.castTag(.slice).?.data.len,
  29193                                             ptr_elem_ty,
  29194                                             parent.mut_decl,
  29195                                         ),
  29196 
  29197                                         else => unreachable,
  29198                                     }
  29199                                 },
  29200                                 else => unreachable,
  29201                             }
  29202                         },
  29203                         else => unreachable,
  29204                     },
  29205                 },
  29206                 .reinterpret => |reinterpret| {
  29207                     const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod);
  29208                     const field_offset = try sema.usizeCast(block, src, field_offset_u64);
  29209                     return ComptimePtrMutationKit{
  29210                         .mut_decl = parent.mut_decl,
  29211                         .pointee = .{ .reinterpret = .{
  29212                             .val_ptr = reinterpret.val_ptr,
  29213                             .byte_offset = reinterpret.byte_offset + field_offset,
  29214                         } },
  29215                         .ty = parent.ty,
  29216                     };
  29217                 },
  29218                 .bad_decl_ty, .bad_ptr_ty => return parent,
  29219             }
  29220         },
  29221     }
  29222 }
  29223 
  29224 fn beginComptimePtrMutationInner(
  29225     sema: *Sema,
  29226     block: *Block,
  29227     src: LazySrcLoc,
  29228     decl_ty: Type,
  29229     decl_val: *Value,
  29230     ptr_elem_ty: Type,
  29231     mut_decl: InternPool.Key.Ptr.Addr.MutDecl,
  29232 ) CompileError!ComptimePtrMutationKit {
  29233     const mod = sema.mod;
  29234     const target = mod.getTarget();
  29235     const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok;
  29236 
  29237     decl_val.* = try decl_val.unintern(sema.arena, mod);
  29238 
  29239     if (coerce_ok) {
  29240         return ComptimePtrMutationKit{
  29241             .mut_decl = mut_decl,
  29242             .pointee = .{ .direct = decl_val },
  29243             .ty = decl_ty,
  29244         };
  29245     }
  29246 
  29247     // Handle the case that the decl is an array and we're actually trying to point to an element.
  29248     if (decl_ty.isArrayOrVector(mod)) {
  29249         const decl_elem_ty = decl_ty.childType(mod);
  29250         if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) {
  29251             return ComptimePtrMutationKit{
  29252                 .mut_decl = mut_decl,
  29253                 .pointee = .{ .direct = decl_val },
  29254                 .ty = decl_ty,
  29255             };
  29256         }
  29257     }
  29258 
  29259     if (!decl_ty.hasWellDefinedLayout(mod)) {
  29260         return ComptimePtrMutationKit{
  29261             .mut_decl = mut_decl,
  29262             .pointee = .bad_decl_ty,
  29263             .ty = decl_ty,
  29264         };
  29265     }
  29266     if (!ptr_elem_ty.hasWellDefinedLayout(mod)) {
  29267         return ComptimePtrMutationKit{
  29268             .mut_decl = mut_decl,
  29269             .pointee = .bad_ptr_ty,
  29270             .ty = ptr_elem_ty,
  29271         };
  29272     }
  29273     return ComptimePtrMutationKit{
  29274         .mut_decl = mut_decl,
  29275         .pointee = .{ .reinterpret = .{
  29276             .val_ptr = decl_val,
  29277             .byte_offset = 0,
  29278         } },
  29279         .ty = decl_ty,
  29280     };
  29281 }
  29282 
  29283 const TypedValueAndOffset = struct {
  29284     tv: TypedValue,
  29285     byte_offset: usize,
  29286 };
  29287 
  29288 const ComptimePtrLoadKit = struct {
  29289     /// The Value and Type corresponding to the pointee of the provided pointer.
  29290     /// If a direct dereference is not possible, this is null.
  29291     pointee: ?TypedValue,
  29292     /// The largest parent Value containing `pointee` and having a well-defined memory layout.
  29293     /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null).
  29294     parent: ?TypedValueAndOffset,
  29295     /// Whether the `pointee` could be mutated by further
  29296     /// semantic analysis and a copy must be performed.
  29297     is_mutable: bool,
  29298     /// If the root decl could not be used as `parent`, this is the type that
  29299     /// caused that by not having a well-defined layout
  29300     ty_without_well_defined_layout: ?Type,
  29301 };
  29302 
  29303 const ComptimePtrLoadError = CompileError || error{
  29304     RuntimeLoad,
  29305 };
  29306 
  29307 /// If `maybe_array_ty` is provided, it will be used to directly dereference an
  29308 /// .elem_ptr of type T to a value of [N]T, if necessary.
  29309 fn beginComptimePtrLoad(
  29310     sema: *Sema,
  29311     block: *Block,
  29312     src: LazySrcLoc,
  29313     ptr_val: Value,
  29314     maybe_array_ty: ?Type,
  29315 ) ComptimePtrLoadError!ComptimePtrLoadKit {
  29316     const mod = sema.mod;
  29317     const target = mod.getTarget();
  29318 
  29319     var deref: ComptimePtrLoadKit = switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) {
  29320         .ptr => |ptr| switch (ptr.addr) {
  29321             .decl, .mut_decl => blk: {
  29322                 const decl_index = switch (ptr.addr) {
  29323                     .decl => |decl| decl,
  29324                     .mut_decl => |mut_decl| mut_decl.decl,
  29325                     else => unreachable,
  29326                 };
  29327                 const is_mutable = ptr.addr == .mut_decl;
  29328                 const decl = mod.declPtr(decl_index);
  29329                 const decl_tv = try decl.typedValue();
  29330                 if (decl.val.getVariable(mod) != null) return error.RuntimeLoad;
  29331 
  29332                 const layout_defined = decl.ty.hasWellDefinedLayout(mod);
  29333                 break :blk ComptimePtrLoadKit{
  29334                     .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
  29335                     .pointee = decl_tv,
  29336                     .is_mutable = is_mutable,
  29337                     .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null,
  29338                 };
  29339             },
  29340             .int => return error.RuntimeLoad,
  29341             .eu_payload, .opt_payload => |container_ptr| blk: {
  29342                 const container_ty = mod.intern_pool.typeOf(container_ptr).toType().childType(mod);
  29343                 const payload_ty = switch (ptr.addr) {
  29344                     .eu_payload => container_ty.errorUnionPayload(mod),
  29345                     .opt_payload => container_ty.optionalChild(mod),
  29346                     else => unreachable,
  29347                 };
  29348                 var deref = try sema.beginComptimePtrLoad(block, src, container_ptr.toValue(), container_ty);
  29349 
  29350                 // eu_payload and opt_payload never have a well-defined layout
  29351                 if (deref.parent != null) {
  29352                     deref.parent = null;
  29353                     deref.ty_without_well_defined_layout = container_ty;
  29354                 }
  29355 
  29356                 if (deref.pointee) |*tv| {
  29357                     const coerce_in_mem_ok =
  29358                         (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or
  29359                         (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;
  29360                     if (coerce_in_mem_ok) {
  29361                         const payload_val = switch (tv.val.ip_index) {
  29362                             .none => tv.val.cast(Value.Payload.SubValue).?.data,
  29363                             .null_value => return sema.fail(block, src, "attempt to use null value", .{}),
  29364                             else => switch (mod.intern_pool.indexToKey(tv.val.toIntern())) {
  29365                                 .error_union => |error_union| switch (error_union.val) {
  29366                                     .err_name => |err_name| return sema.fail(
  29367                                         block,
  29368                                         src,
  29369                                         "attempt to unwrap error: {}",
  29370                                         .{err_name.fmt(&mod.intern_pool)},
  29371                                     ),
  29372                                     .payload => |payload| payload,
  29373                                 },
  29374                                 .opt => |opt| switch (opt.val) {
  29375                                     .none => return sema.fail(block, src, "attempt to use null value", .{}),
  29376                                     else => |payload| payload,
  29377                                 },
  29378                                 else => unreachable,
  29379                             }.toValue(),
  29380                         };
  29381                         tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
  29382                         break :blk deref;
  29383                     }
  29384                 }
  29385                 deref.pointee = null;
  29386                 break :blk deref;
  29387             },
  29388             .comptime_field => |comptime_field| blk: {
  29389                 const field_ty = mod.intern_pool.typeOf(comptime_field).toType();
  29390                 break :blk ComptimePtrLoadKit{
  29391                     .parent = null,
  29392                     .pointee = .{ .ty = field_ty, .val = comptime_field.toValue() },
  29393                     .is_mutable = false,
  29394                     .ty_without_well_defined_layout = field_ty,
  29395                 };
  29396             },
  29397             .elem => |elem_ptr| blk: {
  29398                 const elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod);
  29399                 var deref = try sema.beginComptimePtrLoad(block, src, elem_ptr.base.toValue(), null);
  29400 
  29401                 // This code assumes that elem_ptrs have been "flattened" in order for direct dereference
  29402                 // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that
  29403                 // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened"
  29404                 switch (mod.intern_pool.indexToKey(elem_ptr.base)) {
  29405                     .ptr => |base_ptr| switch (base_ptr.addr) {
  29406                         .elem => |base_elem| assert(!mod.intern_pool.typeOf(base_elem.base).toType().elemType2(mod).eql(elem_ty, mod)),
  29407                         else => {},
  29408                     },
  29409                     else => {},
  29410                 }
  29411 
  29412                 if (elem_ptr.index != 0) {
  29413                     if (elem_ty.hasWellDefinedLayout(mod)) {
  29414                         if (deref.parent) |*parent| {
  29415                             // Update the byte offset (in-place)
  29416                             const elem_size = try sema.typeAbiSize(elem_ty);
  29417                             const offset = parent.byte_offset + elem_size * elem_ptr.index;
  29418                             parent.byte_offset = try sema.usizeCast(block, src, offset);
  29419                         }
  29420                     } else {
  29421                         deref.parent = null;
  29422                         deref.ty_without_well_defined_layout = elem_ty;
  29423                     }
  29424                 }
  29425 
  29426                 // If we're loading an elem that was derived from a different type
  29427                 // than the true type of the underlying decl, we cannot deref directly
  29428                 const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector(mod)) x: {
  29429                     const deref_elem_ty = deref.pointee.?.ty.childType(mod);
  29430                     break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or
  29431                         (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok;
  29432                 } else false;
  29433                 if (!ty_matches) {
  29434                     deref.pointee = null;
  29435                     break :blk deref;
  29436                 }
  29437 
  29438                 var array_tv = deref.pointee.?;
  29439                 const check_len = array_tv.ty.arrayLenIncludingSentinel(mod);
  29440                 if (maybe_array_ty) |load_ty| {
  29441                     // It's possible that we're loading a [N]T, in which case we'd like to slice
  29442                     // the pointee array directly from our parent array.
  29443                     if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) {
  29444                         const len = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod));
  29445                         const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
  29446                         deref.pointee = if (elem_ptr.index + len <= check_len) TypedValue{
  29447                             .ty = try mod.arrayType(.{
  29448                                 .len = len,
  29449                                 .child = elem_ty.toIntern(),
  29450                             }),
  29451                             .val = try array_tv.val.sliceArray(mod, sema.arena, elem_idx, elem_idx + len),
  29452                         } else null;
  29453                         break :blk deref;
  29454                     }
  29455                 }
  29456 
  29457                 if (elem_ptr.index >= check_len) {
  29458                     deref.pointee = null;
  29459                     break :blk deref;
  29460                 }
  29461                 if (elem_ptr.index == check_len - 1) {
  29462                     if (array_tv.ty.sentinel(mod)) |sent| {
  29463                         deref.pointee = TypedValue{
  29464                             .ty = elem_ty,
  29465                             .val = sent,
  29466                         };
  29467                         break :blk deref;
  29468                     }
  29469                 }
  29470                 deref.pointee = TypedValue{
  29471                     .ty = elem_ty,
  29472                     .val = try array_tv.val.elemValue(mod, @as(usize, @intCast(elem_ptr.index))),
  29473                 };
  29474                 break :blk deref;
  29475             },
  29476             .field => |field_ptr| blk: {
  29477                 const field_index = @as(u32, @intCast(field_ptr.index));
  29478                 const container_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod);
  29479                 var deref = try sema.beginComptimePtrLoad(block, src, field_ptr.base.toValue(), container_ty);
  29480 
  29481                 if (container_ty.hasWellDefinedLayout(mod)) {
  29482                     const struct_obj = mod.typeToStruct(container_ty);
  29483                     if (struct_obj != null and struct_obj.?.layout == .Packed) {
  29484                         // packed structs are not byte addressable
  29485                         deref.parent = null;
  29486                     } else if (deref.parent) |*parent| {
  29487                         // Update the byte offset (in-place)
  29488                         try sema.resolveTypeLayout(container_ty);
  29489                         const field_offset = container_ty.structFieldOffset(field_index, mod);
  29490                         parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset);
  29491                     }
  29492                 } else {
  29493                     deref.parent = null;
  29494                     deref.ty_without_well_defined_layout = container_ty;
  29495                 }
  29496 
  29497                 const tv = deref.pointee orelse {
  29498                     deref.pointee = null;
  29499                     break :blk deref;
  29500                 };
  29501                 const coerce_in_mem_ok =
  29502                     (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or
  29503                     (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;
  29504                 if (!coerce_in_mem_ok) {
  29505                     deref.pointee = null;
  29506                     break :blk deref;
  29507                 }
  29508 
  29509                 if (container_ty.isSlice(mod)) {
  29510                     deref.pointee = switch (field_index) {
  29511                         Value.slice_ptr_index => TypedValue{
  29512                             .ty = container_ty.slicePtrFieldType(mod),
  29513                             .val = tv.val.slicePtr(mod),
  29514                         },
  29515                         Value.slice_len_index => TypedValue{
  29516                             .ty = Type.usize,
  29517                             .val = mod.intern_pool.indexToKey(try tv.val.intern(tv.ty, mod)).ptr.len.toValue(),
  29518                         },
  29519                         else => unreachable,
  29520                     };
  29521                 } else {
  29522                     const field_ty = container_ty.structFieldType(field_index, mod);
  29523                     deref.pointee = TypedValue{
  29524                         .ty = field_ty,
  29525                         .val = try tv.val.fieldValue(mod, field_index),
  29526                     };
  29527                 }
  29528                 break :blk deref;
  29529             },
  29530         },
  29531         .opt => |opt| switch (opt.val) {
  29532             .none => return sema.fail(block, src, "attempt to use null value", .{}),
  29533             else => |payload| try sema.beginComptimePtrLoad(block, src, payload.toValue(), null),
  29534         },
  29535         else => unreachable,
  29536     };
  29537 
  29538     if (deref.pointee) |tv| {
  29539         if (deref.parent == null and tv.ty.hasWellDefinedLayout(mod)) {
  29540             deref.parent = .{ .tv = tv, .byte_offset = 0 };
  29541         }
  29542     }
  29543     return deref;
  29544 }
  29545 
  29546 fn bitCast(
  29547     sema: *Sema,
  29548     block: *Block,
  29549     dest_ty_unresolved: Type,
  29550     inst: Air.Inst.Ref,
  29551     inst_src: LazySrcLoc,
  29552     operand_src: ?LazySrcLoc,
  29553 ) CompileError!Air.Inst.Ref {
  29554     const mod = sema.mod;
  29555     const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved);
  29556     try sema.resolveTypeLayout(dest_ty);
  29557 
  29558     const old_ty = try sema.resolveTypeFields(sema.typeOf(inst));
  29559     try sema.resolveTypeLayout(old_ty);
  29560 
  29561     const dest_bits = dest_ty.bitSize(mod);
  29562     const old_bits = old_ty.bitSize(mod);
  29563 
  29564     if (old_bits != dest_bits) {
  29565         return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{
  29566             dest_ty.fmt(mod),
  29567             dest_bits,
  29568             old_ty.fmt(mod),
  29569             old_bits,
  29570         });
  29571     }
  29572 
  29573     if (try sema.resolveMaybeUndefVal(inst)) |val| {
  29574         if (try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0)) |result_val| {
  29575             return sema.addConstant(result_val);
  29576         }
  29577     }
  29578     try sema.requireRuntimeBlock(block, inst_src, operand_src);
  29579     return block.addBitCast(dest_ty, inst);
  29580 }
  29581 
  29582 fn bitCastVal(
  29583     sema: *Sema,
  29584     block: *Block,
  29585     src: LazySrcLoc,
  29586     val: Value,
  29587     old_ty: Type,
  29588     new_ty: Type,
  29589     buffer_offset: usize,
  29590 ) !?Value {
  29591     const mod = sema.mod;
  29592     if (old_ty.eql(new_ty, mod)) return val;
  29593 
  29594     // For types with well-defined memory layouts, we serialize them a byte buffer,
  29595     // then deserialize to the new type.
  29596     const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(mod));
  29597     const buffer = try sema.gpa.alloc(u8, abi_size);
  29598     defer sema.gpa.free(buffer);
  29599     val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) {
  29600         error.OutOfMemory => return error.OutOfMemory,
  29601         error.ReinterpretDeclRef => return null,
  29602         error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
  29603         error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}),
  29604     };
  29605     return try Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena);
  29606 }
  29607 
  29608 fn coerceArrayPtrToSlice(
  29609     sema: *Sema,
  29610     block: *Block,
  29611     dest_ty: Type,
  29612     inst: Air.Inst.Ref,
  29613     inst_src: LazySrcLoc,
  29614 ) CompileError!Air.Inst.Ref {
  29615     const mod = sema.mod;
  29616     if (try sema.resolveMaybeUndefVal(inst)) |val| {
  29617         const ptr_array_ty = sema.typeOf(inst);
  29618         const array_ty = ptr_array_ty.childType(mod);
  29619         const slice_val = try mod.intern(.{ .ptr = .{
  29620             .ty = dest_ty.toIntern(),
  29621             .addr = switch (mod.intern_pool.indexToKey(val.toIntern())) {
  29622                 .undef => .{ .int = try mod.intern(.{ .undef = .usize_type }) },
  29623                 .ptr => |ptr| ptr.addr,
  29624                 else => unreachable,
  29625             },
  29626             .len = (try mod.intValue(Type.usize, array_ty.arrayLen(mod))).toIntern(),
  29627         } });
  29628         return sema.addConstant(slice_val.toValue());
  29629     }
  29630     try sema.requireRuntimeBlock(block, inst_src, null);
  29631     return block.addTyOp(.array_to_slice, dest_ty, inst);
  29632 }
  29633 
  29634 fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool {
  29635     const mod = sema.mod;
  29636     const dest_info = dest_ty.ptrInfo(mod);
  29637     const inst_info = inst_ty.ptrInfo(mod);
  29638     const len0 = (inst_info.child.toType().zigTypeTag(mod) == .Array and (inst_info.child.toType().arrayLenIncludingSentinel(mod) == 0 or
  29639         (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
  29640         (inst_info.child.toType().isTuple(mod) and inst_info.child.toType().structFieldCount(mod) == 0);
  29641 
  29642     const ok_cv_qualifiers =
  29643         ((!inst_info.flags.is_const or dest_info.flags.is_const) or len0) and
  29644         (!inst_info.flags.is_volatile or dest_info.flags.is_volatile);
  29645 
  29646     if (!ok_cv_qualifiers) {
  29647         in_memory_result.* = .{ .ptr_qualifiers = .{
  29648             .actual_const = inst_info.flags.is_const,
  29649             .wanted_const = dest_info.flags.is_const,
  29650             .actual_volatile = inst_info.flags.is_volatile,
  29651             .wanted_volatile = dest_info.flags.is_volatile,
  29652         } };
  29653         return false;
  29654     }
  29655     if (dest_info.flags.address_space != inst_info.flags.address_space) {
  29656         in_memory_result.* = .{ .ptr_addrspace = .{
  29657             .actual = inst_info.flags.address_space,
  29658             .wanted = dest_info.flags.address_space,
  29659         } };
  29660         return false;
  29661     }
  29662     if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true;
  29663     if (len0) return true;
  29664 
  29665     const inst_align = inst_info.flags.alignment.toByteUnitsOptional() orelse
  29666         inst_info.child.toType().abiAlignment(mod);
  29667 
  29668     const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse
  29669         dest_info.child.toType().abiAlignment(mod);
  29670 
  29671     if (dest_align > inst_align) {
  29672         in_memory_result.* = .{ .ptr_alignment = .{
  29673             .actual = inst_align,
  29674             .wanted = dest_align,
  29675         } };
  29676         return false;
  29677     }
  29678     return true;
  29679 }
  29680 
  29681 fn coerceCompatiblePtrs(
  29682     sema: *Sema,
  29683     block: *Block,
  29684     dest_ty: Type,
  29685     inst: Air.Inst.Ref,
  29686     inst_src: LazySrcLoc,
  29687 ) !Air.Inst.Ref {
  29688     const mod = sema.mod;
  29689     const inst_ty = sema.typeOf(inst);
  29690     if (try sema.resolveMaybeUndefVal(inst)) |val| {
  29691         if (!val.isUndef(mod) and val.isNull(mod) and !dest_ty.isAllowzeroPtr(mod)) {
  29692             return sema.fail(block, inst_src, "null pointer casted to type '{}'", .{dest_ty.fmt(sema.mod)});
  29693         }
  29694         // The comptime Value representation is compatible with both types.
  29695         return sema.addConstant(
  29696             try mod.getCoerced((try val.intern(inst_ty, mod)).toValue(), dest_ty),
  29697         );
  29698     }
  29699     try sema.requireRuntimeBlock(block, inst_src, null);
  29700     const inst_allows_zero = inst_ty.zigTypeTag(mod) != .Pointer or inst_ty.ptrAllowsZero(mod);
  29701     if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(mod) and
  29702         (try sema.typeHasRuntimeBits(dest_ty.elemType2(mod)) or dest_ty.elemType2(mod).zigTypeTag(mod) == .Fn))
  29703     {
  29704         const actual_ptr = if (inst_ty.isSlice(mod))
  29705             try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty)
  29706         else
  29707             inst;
  29708         const ptr_int = try block.addUnOp(.int_from_ptr, actual_ptr);
  29709         const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
  29710         const ok = if (inst_ty.isSlice(mod)) ok: {
  29711             const len = try sema.analyzeSliceLen(block, inst_src, inst);
  29712             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
  29713             break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero);
  29714         } else is_non_zero;
  29715         try sema.addSafetyCheck(block, ok, .cast_to_null);
  29716     }
  29717     return sema.bitCast(block, dest_ty, inst, inst_src, null);
  29718 }
  29719 
  29720 fn coerceEnumToUnion(
  29721     sema: *Sema,
  29722     block: *Block,
  29723     union_ty: Type,
  29724     union_ty_src: LazySrcLoc,
  29725     inst: Air.Inst.Ref,
  29726     inst_src: LazySrcLoc,
  29727 ) !Air.Inst.Ref {
  29728     const mod = sema.mod;
  29729     const ip = &mod.intern_pool;
  29730     const inst_ty = sema.typeOf(inst);
  29731 
  29732     const tag_ty = union_ty.unionTagType(mod) orelse {
  29733         const msg = msg: {
  29734             const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
  29735                 union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
  29736             });
  29737             errdefer msg.destroy(sema.gpa);
  29738             try sema.errNote(block, union_ty_src, msg, "cannot coerce enum to untagged union", .{});
  29739             try sema.addDeclaredHereNote(msg, union_ty);
  29740             break :msg msg;
  29741         };
  29742         return sema.failWithOwnedErrorMsg(msg);
  29743     };
  29744 
  29745     const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
  29746     if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| {
  29747         const field_index = union_ty.unionTagFieldIndex(val, sema.mod) orelse {
  29748             const msg = msg: {
  29749                 const msg = try sema.errMsg(block, inst_src, "union '{}' has no tag with value '{}'", .{
  29750                     union_ty.fmt(sema.mod), val.fmtValue(tag_ty, sema.mod),
  29751                 });
  29752                 errdefer msg.destroy(sema.gpa);
  29753                 try sema.addDeclaredHereNote(msg, union_ty);
  29754                 break :msg msg;
  29755             };
  29756             return sema.failWithOwnedErrorMsg(msg);
  29757         };
  29758 
  29759         const union_obj = mod.typeToUnion(union_ty).?;
  29760         const field = union_obj.fields.values()[field_index];
  29761         const field_ty = try sema.resolveTypeFields(field.ty);
  29762         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  29763             const msg = msg: {
  29764                 const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{});
  29765                 errdefer msg.destroy(sema.gpa);
  29766 
  29767                 const field_name = union_obj.fields.keys()[field_index];
  29768                 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{
  29769                     field_name.fmt(ip),
  29770                 });
  29771                 try sema.addDeclaredHereNote(msg, union_ty);
  29772                 break :msg msg;
  29773             };
  29774             return sema.failWithOwnedErrorMsg(msg);
  29775         }
  29776         const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse {
  29777             const msg = msg: {
  29778                 const field_name = union_obj.fields.keys()[field_index];
  29779                 const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{}'", .{
  29780                     inst_ty.fmt(sema.mod),  union_ty.fmt(sema.mod),
  29781                     field_ty.fmt(sema.mod), field_name.fmt(ip),
  29782                 });
  29783                 errdefer msg.destroy(sema.gpa);
  29784 
  29785                 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{
  29786                     field_name.fmt(ip),
  29787                 });
  29788                 try sema.addDeclaredHereNote(msg, union_ty);
  29789                 break :msg msg;
  29790             };
  29791             return sema.failWithOwnedErrorMsg(msg);
  29792         };
  29793 
  29794         return sema.addConstant(try mod.unionValue(union_ty, val, opv));
  29795     }
  29796 
  29797     try sema.requireRuntimeBlock(block, inst_src, null);
  29798 
  29799     if (tag_ty.isNonexhaustiveEnum(mod)) {
  29800         const msg = msg: {
  29801             const msg = try sema.errMsg(block, inst_src, "runtime coercion to union '{}' from non-exhaustive enum", .{
  29802                 union_ty.fmt(sema.mod),
  29803             });
  29804             errdefer msg.destroy(sema.gpa);
  29805             try sema.addDeclaredHereNote(msg, tag_ty);
  29806             break :msg msg;
  29807         };
  29808         return sema.failWithOwnedErrorMsg(msg);
  29809     }
  29810 
  29811     const union_obj = mod.typeToUnion(union_ty).?;
  29812     {
  29813         var msg: ?*Module.ErrorMsg = null;
  29814         errdefer if (msg) |some| some.destroy(sema.gpa);
  29815 
  29816         for (union_obj.fields.values(), 0..) |field, i| {
  29817             if (field.ty.zigTypeTag(mod) == .NoReturn) {
  29818                 const err_msg = msg orelse try sema.errMsg(
  29819                     block,
  29820                     inst_src,
  29821                     "runtime coercion from enum '{}' to union '{}' which has a 'noreturn' field",
  29822                     .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) },
  29823                 );
  29824                 msg = err_msg;
  29825 
  29826                 try sema.addFieldErrNote(union_ty, i, err_msg, "'noreturn' field here", .{});
  29827             }
  29828         }
  29829         if (msg) |some| {
  29830             msg = null;
  29831             try sema.addDeclaredHereNote(some, union_ty);
  29832             return sema.failWithOwnedErrorMsg(some);
  29833         }
  29834     }
  29835 
  29836     // If the union has all fields 0 bits, the union value is just the enum value.
  29837     if (union_ty.unionHasAllZeroBitFieldTypes(mod)) {
  29838         return block.addBitCast(union_ty, enum_tag);
  29839     }
  29840 
  29841     const msg = msg: {
  29842         const msg = try sema.errMsg(
  29843             block,
  29844             inst_src,
  29845             "runtime coercion from enum '{}' to union '{}' which has non-void fields",
  29846             .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) },
  29847         );
  29848         errdefer msg.destroy(sema.gpa);
  29849 
  29850         var it = union_obj.fields.iterator();
  29851         var field_index: usize = 0;
  29852         while (it.next()) |field| : (field_index += 1) {
  29853             const field_name = field.key_ptr.*;
  29854             const field_ty = field.value_ptr.ty;
  29855             if (!(try sema.typeHasRuntimeBits(field_ty))) continue;
  29856             try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' has type '{}'", .{
  29857                 field_name.fmt(ip),
  29858                 field_ty.fmt(sema.mod),
  29859             });
  29860         }
  29861         try sema.addDeclaredHereNote(msg, union_ty);
  29862         break :msg msg;
  29863     };
  29864     return sema.failWithOwnedErrorMsg(msg);
  29865 }
  29866 
  29867 fn coerceAnonStructToUnion(
  29868     sema: *Sema,
  29869     block: *Block,
  29870     union_ty: Type,
  29871     union_ty_src: LazySrcLoc,
  29872     inst: Air.Inst.Ref,
  29873     inst_src: LazySrcLoc,
  29874 ) !Air.Inst.Ref {
  29875     const mod = sema.mod;
  29876     const inst_ty = sema.typeOf(inst);
  29877     const field_info: union(enum) {
  29878         name: InternPool.NullTerminatedString,
  29879         count: usize,
  29880     } = switch (mod.intern_pool.indexToKey(inst_ty.toIntern())) {
  29881         .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 1)
  29882             .{ .name = anon_struct_type.names[0] }
  29883         else
  29884             .{ .count = anon_struct_type.names.len },
  29885         .struct_type => |struct_type| name: {
  29886             const field_names = mod.structPtrUnwrap(struct_type.index).?.fields.keys();
  29887             break :name if (field_names.len == 1)
  29888                 .{ .name = field_names[0] }
  29889             else
  29890                 .{ .count = field_names.len };
  29891         },
  29892         else => unreachable,
  29893     };
  29894     switch (field_info) {
  29895         .name => |field_name| {
  29896             const init = try sema.structFieldVal(block, inst_src, inst, field_name, inst_src, inst_ty);
  29897             return sema.unionInit(block, init, inst_src, union_ty, union_ty_src, field_name, inst_src);
  29898         },
  29899         .count => |field_count| {
  29900             assert(field_count != 1);
  29901             const msg = msg: {
  29902                 const msg = if (field_count > 1) try sema.errMsg(
  29903                     block,
  29904                     inst_src,
  29905                     "cannot initialize multiple union fields at once; unions can only have one active field",
  29906                     .{},
  29907                 ) else try sema.errMsg(
  29908                     block,
  29909                     inst_src,
  29910                     "union initializer must initialize one field",
  29911                     .{},
  29912                 );
  29913                 errdefer msg.destroy(sema.gpa);
  29914 
  29915                 // TODO add notes for where the anon struct was created to point out
  29916                 // the extra fields.
  29917 
  29918                 try sema.addDeclaredHereNote(msg, union_ty);
  29919                 break :msg msg;
  29920             };
  29921             return sema.failWithOwnedErrorMsg(msg);
  29922         },
  29923     }
  29924 }
  29925 
  29926 fn coerceAnonStructToUnionPtrs(
  29927     sema: *Sema,
  29928     block: *Block,
  29929     ptr_union_ty: Type,
  29930     union_ty_src: LazySrcLoc,
  29931     ptr_anon_struct: Air.Inst.Ref,
  29932     anon_struct_src: LazySrcLoc,
  29933 ) !Air.Inst.Ref {
  29934     const mod = sema.mod;
  29935     const union_ty = ptr_union_ty.childType(mod);
  29936     const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src);
  29937     const union_inst = try sema.coerceAnonStructToUnion(block, union_ty, union_ty_src, anon_struct, anon_struct_src);
  29938     return sema.analyzeRef(block, union_ty_src, union_inst);
  29939 }
  29940 
  29941 fn coerceAnonStructToStructPtrs(
  29942     sema: *Sema,
  29943     block: *Block,
  29944     ptr_struct_ty: Type,
  29945     struct_ty_src: LazySrcLoc,
  29946     ptr_anon_struct: Air.Inst.Ref,
  29947     anon_struct_src: LazySrcLoc,
  29948 ) !Air.Inst.Ref {
  29949     const mod = sema.mod;
  29950     const struct_ty = ptr_struct_ty.childType(mod);
  29951     const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src);
  29952     const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, anon_struct, anon_struct_src);
  29953     return sema.analyzeRef(block, struct_ty_src, struct_inst);
  29954 }
  29955 
  29956 /// If the lengths match, coerces element-wise.
  29957 fn coerceArrayLike(
  29958     sema: *Sema,
  29959     block: *Block,
  29960     dest_ty: Type,
  29961     dest_ty_src: LazySrcLoc,
  29962     inst: Air.Inst.Ref,
  29963     inst_src: LazySrcLoc,
  29964 ) !Air.Inst.Ref {
  29965     const mod = sema.mod;
  29966     const inst_ty = sema.typeOf(inst);
  29967     const inst_len = inst_ty.arrayLen(mod);
  29968     const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod));
  29969     const target = mod.getTarget();
  29970 
  29971     if (dest_len != inst_len) {
  29972         const msg = msg: {
  29973             const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
  29974                 dest_ty.fmt(mod), inst_ty.fmt(mod),
  29975             });
  29976             errdefer msg.destroy(sema.gpa);
  29977             try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
  29978             try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
  29979             break :msg msg;
  29980         };
  29981         return sema.failWithOwnedErrorMsg(msg);
  29982     }
  29983 
  29984     const dest_elem_ty = dest_ty.childType(mod);
  29985     const inst_elem_ty = inst_ty.childType(mod);
  29986     const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src);
  29987     if (in_memory_result == .ok) {
  29988         if (try sema.resolveMaybeUndefVal(inst)) |inst_val| {
  29989             // These types share the same comptime value representation.
  29990             return sema.coerceInMemory(inst_val, dest_ty);
  29991         }
  29992         try sema.requireRuntimeBlock(block, inst_src, null);
  29993         return block.addBitCast(dest_ty, inst);
  29994     }
  29995 
  29996     const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
  29997     const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
  29998     var runtime_src: ?LazySrcLoc = null;
  29999 
  30000     for (element_vals, element_refs, 0..) |*val, *ref, i| {
  30001         const index_ref = try sema.addConstant(try mod.intValue(Type.usize, i));
  30002         const src = inst_src; // TODO better source location
  30003         const elem_src = inst_src; // TODO better source location
  30004         const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true);
  30005         const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
  30006         ref.* = coerced;
  30007         if (runtime_src == null) {
  30008             if (try sema.resolveMaybeUndefVal(coerced)) |elem_val| {
  30009                 val.* = try elem_val.intern(dest_elem_ty, mod);
  30010             } else {
  30011                 runtime_src = elem_src;
  30012             }
  30013         }
  30014     }
  30015 
  30016     if (runtime_src) |rs| {
  30017         try sema.requireRuntimeBlock(block, inst_src, rs);
  30018         return block.addAggregateInit(dest_ty, element_refs);
  30019     }
  30020 
  30021     return sema.addConstant((try mod.intern(.{ .aggregate = .{
  30022         .ty = dest_ty.toIntern(),
  30023         .storage = .{ .elems = element_vals },
  30024     } })).toValue());
  30025 }
  30026 
  30027 /// If the lengths match, coerces element-wise.
  30028 fn coerceTupleToArray(
  30029     sema: *Sema,
  30030     block: *Block,
  30031     dest_ty: Type,
  30032     dest_ty_src: LazySrcLoc,
  30033     inst: Air.Inst.Ref,
  30034     inst_src: LazySrcLoc,
  30035 ) !Air.Inst.Ref {
  30036     const mod = sema.mod;
  30037     const inst_ty = sema.typeOf(inst);
  30038     const inst_len = inst_ty.arrayLen(mod);
  30039     const dest_len = dest_ty.arrayLen(mod);
  30040 
  30041     if (dest_len != inst_len) {
  30042         const msg = msg: {
  30043             const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
  30044                 dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
  30045             });
  30046             errdefer msg.destroy(sema.gpa);
  30047             try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
  30048             try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
  30049             break :msg msg;
  30050         };
  30051         return sema.failWithOwnedErrorMsg(msg);
  30052     }
  30053 
  30054     const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len);
  30055     const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems);
  30056     const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);
  30057     const dest_elem_ty = dest_ty.childType(mod);
  30058 
  30059     var runtime_src: ?LazySrcLoc = null;
  30060     for (element_vals, element_refs, 0..) |*val, *ref, i_usize| {
  30061         const i = @as(u32, @intCast(i_usize));
  30062         if (i_usize == inst_len) {
  30063             const sentinel_val = dest_ty.sentinel(mod).?;
  30064             val.* = sentinel_val.toIntern();
  30065             ref.* = try sema.addConstant(sentinel_val);
  30066             break;
  30067         }
  30068         const elem_src = inst_src; // TODO better source location
  30069         const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i);
  30070         const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
  30071         ref.* = coerced;
  30072         if (runtime_src == null) {
  30073             if (try sema.resolveMaybeUndefVal(coerced)) |elem_val| {
  30074                 val.* = try elem_val.intern(dest_elem_ty, mod);
  30075             } else {
  30076                 runtime_src = elem_src;
  30077             }
  30078         }
  30079     }
  30080 
  30081     if (runtime_src) |rs| {
  30082         try sema.requireRuntimeBlock(block, inst_src, rs);
  30083         return block.addAggregateInit(dest_ty, element_refs);
  30084     }
  30085 
  30086     return sema.addConstant((try mod.intern(.{ .aggregate = .{
  30087         .ty = dest_ty.toIntern(),
  30088         .storage = .{ .elems = element_vals },
  30089     } })).toValue());
  30090 }
  30091 
  30092 /// If the lengths match, coerces element-wise.
  30093 fn coerceTupleToSlicePtrs(
  30094     sema: *Sema,
  30095     block: *Block,
  30096     slice_ty: Type,
  30097     slice_ty_src: LazySrcLoc,
  30098     ptr_tuple: Air.Inst.Ref,
  30099     tuple_src: LazySrcLoc,
  30100 ) !Air.Inst.Ref {
  30101     const mod = sema.mod;
  30102     const tuple_ty = sema.typeOf(ptr_tuple).childType(mod);
  30103     const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
  30104     const slice_info = slice_ty.ptrInfo(mod);
  30105     const array_ty = try mod.arrayType(.{
  30106         .len = tuple_ty.structFieldCount(mod),
  30107         .sentinel = slice_info.sentinel,
  30108         .child = slice_info.child,
  30109     });
  30110     const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src);
  30111     if (slice_info.flags.alignment != .none) {
  30112         return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{});
  30113     }
  30114     const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst);
  30115     return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);
  30116 }
  30117 
  30118 /// If the lengths match, coerces element-wise.
  30119 fn coerceTupleToArrayPtrs(
  30120     sema: *Sema,
  30121     block: *Block,
  30122     ptr_array_ty: Type,
  30123     array_ty_src: LazySrcLoc,
  30124     ptr_tuple: Air.Inst.Ref,
  30125     tuple_src: LazySrcLoc,
  30126 ) !Air.Inst.Ref {
  30127     const mod = sema.mod;
  30128     const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
  30129     const ptr_info = ptr_array_ty.ptrInfo(mod);
  30130     const array_ty = ptr_info.child.toType();
  30131     const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src);
  30132     if (ptr_info.flags.alignment != .none) {
  30133         return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{});
  30134     }
  30135     const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst);
  30136     return ptr_array;
  30137 }
  30138 
  30139 /// Handles both tuples and anon struct literals. Coerces field-wise. Reports
  30140 /// errors for both extra fields and missing fields.
  30141 fn coerceTupleToStruct(
  30142     sema: *Sema,
  30143     block: *Block,
  30144     dest_ty: Type,
  30145     inst: Air.Inst.Ref,
  30146     inst_src: LazySrcLoc,
  30147 ) !Air.Inst.Ref {
  30148     const mod = sema.mod;
  30149     const ip = &mod.intern_pool;
  30150     const struct_ty = try sema.resolveTypeFields(dest_ty);
  30151 
  30152     if (struct_ty.isTupleOrAnonStruct(mod)) {
  30153         return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src);
  30154     }
  30155 
  30156     const fields = struct_ty.structFields(mod);
  30157     const field_vals = try sema.arena.alloc(InternPool.Index, fields.count());
  30158     const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
  30159     @memset(field_refs, .none);
  30160 
  30161     const inst_ty = sema.typeOf(inst);
  30162     var runtime_src: ?LazySrcLoc = null;
  30163     const field_count = switch (ip.indexToKey(inst_ty.toIntern())) {
  30164         .anon_struct_type => |anon_struct_type| anon_struct_type.types.len,
  30165         .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj|
  30166             struct_obj.fields.count()
  30167         else
  30168             0,
  30169         else => unreachable,
  30170     };
  30171     for (0..field_count) |field_index_usize| {
  30172         const field_i = @as(u32, @intCast(field_index_usize));
  30173         const field_src = inst_src; // TODO better source location
  30174         // https://github.com/ziglang/zig/issues/15709
  30175         const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) {
  30176             .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0)
  30177                 anon_struct_type.names[field_i]
  30178             else
  30179                 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}),
  30180             .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i],
  30181             else => unreachable,
  30182         };
  30183         const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);
  30184         const field = fields.values()[field_index];
  30185         const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
  30186         const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);
  30187         field_refs[field_index] = coerced;
  30188         if (field.is_comptime) {
  30189             const init_val = (try sema.resolveMaybeUndefVal(coerced)) orelse {
  30190                 return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known");
  30191             };
  30192 
  30193             if (!init_val.eql(field.default_val.toValue(), field.ty, sema.mod)) {
  30194                 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i);
  30195             }
  30196         }
  30197         if (runtime_src == null) {
  30198             if (try sema.resolveMaybeUndefVal(coerced)) |field_val| {
  30199                 field_vals[field_index] = field_val.toIntern();
  30200             } else {
  30201                 runtime_src = field_src;
  30202             }
  30203         }
  30204     }
  30205 
  30206     // Populate default field values and report errors for missing fields.
  30207     var root_msg: ?*Module.ErrorMsg = null;
  30208     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  30209 
  30210     for (field_refs, 0..) |*field_ref, i| {
  30211         if (field_ref.* != .none) continue;
  30212 
  30213         const field_name = fields.keys()[i];
  30214         const field = fields.values()[i];
  30215         const field_src = inst_src; // TODO better source location
  30216         if (field.default_val == .none) {
  30217             const template = "missing struct field: {}";
  30218             const args = .{field_name.fmt(ip)};
  30219             if (root_msg) |msg| {
  30220                 try sema.errNote(block, field_src, msg, template, args);
  30221             } else {
  30222                 root_msg = try sema.errMsg(block, field_src, template, args);
  30223             }
  30224             continue;
  30225         }
  30226         if (runtime_src == null) {
  30227             field_vals[i] = field.default_val;
  30228         } else {
  30229             field_ref.* = try sema.addConstant(field.default_val.toValue());
  30230         }
  30231     }
  30232 
  30233     if (root_msg) |msg| {
  30234         try sema.addDeclaredHereNote(msg, struct_ty);
  30235         root_msg = null;
  30236         return sema.failWithOwnedErrorMsg(msg);
  30237     }
  30238 
  30239     if (runtime_src) |rs| {
  30240         try sema.requireRuntimeBlock(block, inst_src, rs);
  30241         return block.addAggregateInit(struct_ty, field_refs);
  30242     }
  30243 
  30244     const struct_val = try mod.intern(.{ .aggregate = .{
  30245         .ty = struct_ty.toIntern(),
  30246         .storage = .{ .elems = field_vals },
  30247     } });
  30248     // TODO: figure out InternPool removals for incremental compilation
  30249     //errdefer ip.remove(struct_val);
  30250 
  30251     return sema.addConstant(struct_val.toValue());
  30252 }
  30253 
  30254 fn coerceTupleToTuple(
  30255     sema: *Sema,
  30256     block: *Block,
  30257     tuple_ty: Type,
  30258     inst: Air.Inst.Ref,
  30259     inst_src: LazySrcLoc,
  30260 ) !Air.Inst.Ref {
  30261     const mod = sema.mod;
  30262     const ip = &mod.intern_pool;
  30263     const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) {
  30264         .anon_struct_type => |anon_struct_type| anon_struct_type.types.len,
  30265         .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj|
  30266             struct_obj.fields.count()
  30267         else
  30268             0,
  30269         else => unreachable,
  30270     };
  30271     const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count);
  30272     const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
  30273     @memset(field_refs, .none);
  30274 
  30275     const inst_ty = sema.typeOf(inst);
  30276     const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) {
  30277         .anon_struct_type => |anon_struct_type| anon_struct_type.types.len,
  30278         .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj|
  30279             struct_obj.fields.count()
  30280         else
  30281             0,
  30282         else => unreachable,
  30283     };
  30284     if (src_field_count > dest_field_count) return error.NotCoercible;
  30285 
  30286     var runtime_src: ?LazySrcLoc = null;
  30287     for (0..dest_field_count) |field_index_usize| {
  30288         const field_i = @as(u32, @intCast(field_index_usize));
  30289         const field_src = inst_src; // TODO better source location
  30290         // https://github.com/ziglang/zig/issues/15709
  30291         const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) {
  30292             .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0)
  30293                 anon_struct_type.names[field_i]
  30294             else
  30295                 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}),
  30296             .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i],
  30297             else => unreachable,
  30298         };
  30299 
  30300         if (ip.stringEqlSlice(field_name, "len"))
  30301             return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{});
  30302 
  30303         const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) {
  30304             .anon_struct_type => |anon_struct_type| anon_struct_type.types[field_index_usize].toType(),
  30305             .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].ty,
  30306             else => unreachable,
  30307         };
  30308         const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
  30309             .anon_struct_type => |anon_struct_type| anon_struct_type.values[field_index_usize],
  30310             .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].default_val,
  30311             else => unreachable,
  30312         };
  30313 
  30314         const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src);
  30315 
  30316         const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
  30317         const coerced = try sema.coerce(block, field_ty, elem_ref, field_src);
  30318         field_refs[field_index] = coerced;
  30319         if (default_val != .none) {
  30320             const init_val = (try sema.resolveMaybeUndefVal(coerced)) orelse {
  30321                 return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known");
  30322             };
  30323 
  30324             if (!init_val.eql(default_val.toValue(), field_ty, sema.mod)) {
  30325                 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i);
  30326             }
  30327         }
  30328         if (runtime_src == null) {
  30329             if (try sema.resolveMaybeUndefVal(coerced)) |field_val| {
  30330                 field_vals[field_index] = field_val.toIntern();
  30331             } else {
  30332                 runtime_src = field_src;
  30333             }
  30334         }
  30335     }
  30336 
  30337     // Populate default field values and report errors for missing fields.
  30338     var root_msg: ?*Module.ErrorMsg = null;
  30339     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  30340 
  30341     for (field_refs, 0..) |*field_ref, i| {
  30342         if (field_ref.* != .none) continue;
  30343 
  30344         const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
  30345             .anon_struct_type => |anon_struct_type| anon_struct_type.values[i],
  30346             .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[i].default_val,
  30347             else => unreachable,
  30348         };
  30349 
  30350         const field_src = inst_src; // TODO better source location
  30351         if (default_val == .none) {
  30352             if (tuple_ty.isTuple(mod)) {
  30353                 const template = "missing tuple field: {d}";
  30354                 if (root_msg) |msg| {
  30355                     try sema.errNote(block, field_src, msg, template, .{i});
  30356                 } else {
  30357                     root_msg = try sema.errMsg(block, field_src, template, .{i});
  30358                 }
  30359                 continue;
  30360             }
  30361             const template = "missing struct field: {}";
  30362             const args = .{tuple_ty.structFieldName(i, mod).fmt(ip)};
  30363             if (root_msg) |msg| {
  30364                 try sema.errNote(block, field_src, msg, template, args);
  30365             } else {
  30366                 root_msg = try sema.errMsg(block, field_src, template, args);
  30367             }
  30368             continue;
  30369         }
  30370         if (runtime_src == null) {
  30371             field_vals[i] = default_val;
  30372         } else {
  30373             field_ref.* = try sema.addConstant(default_val.toValue());
  30374         }
  30375     }
  30376 
  30377     if (root_msg) |msg| {
  30378         try sema.addDeclaredHereNote(msg, tuple_ty);
  30379         root_msg = null;
  30380         return sema.failWithOwnedErrorMsg(msg);
  30381     }
  30382 
  30383     if (runtime_src) |rs| {
  30384         try sema.requireRuntimeBlock(block, inst_src, rs);
  30385         return block.addAggregateInit(tuple_ty, field_refs);
  30386     }
  30387 
  30388     return sema.addConstant(
  30389         (try mod.intern(.{ .aggregate = .{
  30390             .ty = tuple_ty.toIntern(),
  30391             .storage = .{ .elems = field_vals },
  30392         } })).toValue(),
  30393     );
  30394 }
  30395 
  30396 fn analyzeDeclVal(
  30397     sema: *Sema,
  30398     block: *Block,
  30399     src: LazySrcLoc,
  30400     decl_index: Decl.Index,
  30401 ) CompileError!Air.Inst.Ref {
  30402     try sema.addReferencedBy(block, src, decl_index);
  30403     if (sema.decl_val_table.get(decl_index)) |result| {
  30404         return result;
  30405     }
  30406     const decl_ref = try sema.analyzeDeclRefInner(decl_index, false);
  30407     const result = try sema.analyzeLoad(block, src, decl_ref, src);
  30408     if (Air.refToInterned(result) != null) {
  30409         if (!block.is_typeof) {
  30410             try sema.decl_val_table.put(sema.gpa, decl_index, result);
  30411         }
  30412     }
  30413     return result;
  30414 }
  30415 
  30416 fn addReferencedBy(
  30417     sema: *Sema,
  30418     block: *Block,
  30419     src: LazySrcLoc,
  30420     decl_index: Decl.Index,
  30421 ) !void {
  30422     if (sema.mod.comp.reference_trace == @as(u32, 0)) return;
  30423     try sema.mod.reference_table.put(sema.gpa, decl_index, .{
  30424         .referencer = block.src_decl,
  30425         .src = src,
  30426     });
  30427 }
  30428 
  30429 fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void {
  30430     const mod = sema.mod;
  30431     const decl = mod.declPtr(decl_index);
  30432     if (decl.analysis == .in_progress) {
  30433         const msg = try Module.ErrorMsg.create(sema.gpa, decl.srcLoc(mod), "dependency loop detected", .{});
  30434         return sema.failWithOwnedErrorMsg(msg);
  30435     }
  30436 
  30437     mod.ensureDeclAnalyzed(decl_index) catch |err| {
  30438         if (sema.owner_func) |owner_func| {
  30439             owner_func.state = .dependency_failure;
  30440         } else {
  30441             sema.owner_decl.analysis = .dependency_failure;
  30442         }
  30443         return err;
  30444     };
  30445 }
  30446 
  30447 fn ensureFuncBodyAnalyzed(sema: *Sema, func: Module.Fn.Index) CompileError!void {
  30448     sema.mod.ensureFuncBodyAnalyzed(func) catch |err| {
  30449         if (sema.owner_func) |owner_func| {
  30450             owner_func.state = .dependency_failure;
  30451         } else {
  30452             sema.owner_decl.analysis = .dependency_failure;
  30453         }
  30454         return err;
  30455     };
  30456 }
  30457 
  30458 fn refValue(sema: *Sema, block: *Block, ty: Type, val: Value) !Value {
  30459     const mod = sema.mod;
  30460     var anon_decl = try block.startAnonDecl();
  30461     defer anon_decl.deinit();
  30462     const decl = try anon_decl.finish(
  30463         ty,
  30464         val,
  30465         .none, // default alignment
  30466     );
  30467     try sema.maybeQueueFuncBodyAnalysis(decl);
  30468     try mod.declareDeclDependency(sema.owner_decl_index, decl);
  30469     const result = try mod.intern(.{ .ptr = .{
  30470         .ty = (try mod.singleConstPtrType(ty)).toIntern(),
  30471         .addr = .{ .decl = decl },
  30472     } });
  30473     return result.toValue();
  30474 }
  30475 
  30476 fn optRefValue(sema: *Sema, block: *Block, ty: Type, opt_val: ?Value) !Value {
  30477     const mod = sema.mod;
  30478     const ptr_anyopaque_ty = try mod.singleConstPtrType(Type.anyopaque);
  30479     return (try mod.intern(.{ .opt = .{
  30480         .ty = (try mod.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(),
  30481         .val = if (opt_val) |val| (try mod.getCoerced(
  30482             try sema.refValue(block, ty, val),
  30483             ptr_anyopaque_ty,
  30484         )).toIntern() else .none,
  30485     } })).toValue();
  30486 }
  30487 
  30488 fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref {
  30489     return sema.analyzeDeclRefInner(decl_index, true);
  30490 }
  30491 
  30492 /// Analyze a reference to the decl at the given index. Ensures the underlying decl is analyzed, but
  30493 /// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a
  30494 /// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeDeclRef` wraps
  30495 /// this function with `analyze_fn_body` set to true.
  30496 fn analyzeDeclRefInner(sema: *Sema, decl_index: Decl.Index, analyze_fn_body: bool) CompileError!Air.Inst.Ref {
  30497     const mod = sema.mod;
  30498     try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
  30499     try sema.ensureDeclAnalyzed(decl_index);
  30500 
  30501     const decl = mod.declPtr(decl_index);
  30502     const decl_tv = try decl.typedValue();
  30503     const ptr_ty = try mod.ptrType(.{
  30504         .child = decl_tv.ty.toIntern(),
  30505         .flags = .{
  30506             .alignment = decl.alignment,
  30507             .is_const = if (decl.val.getVariable(mod)) |variable| variable.is_const else true,
  30508             .address_space = decl.@"addrspace",
  30509         },
  30510     });
  30511     if (analyze_fn_body) {
  30512         try sema.maybeQueueFuncBodyAnalysis(decl_index);
  30513     }
  30514     return sema.addConstant((try mod.intern(.{ .ptr = .{
  30515         .ty = ptr_ty.toIntern(),
  30516         .addr = .{ .decl = decl_index },
  30517     } })).toValue());
  30518 }
  30519 
  30520 fn maybeQueueFuncBodyAnalysis(sema: *Sema, decl_index: Decl.Index) !void {
  30521     const mod = sema.mod;
  30522     const decl = mod.declPtr(decl_index);
  30523     const tv = try decl.typedValue();
  30524     if (tv.ty.zigTypeTag(mod) != .Fn) return;
  30525     if (!try sema.fnHasRuntimeBits(tv.ty)) return;
  30526     const func_index = mod.intern_pool.indexToFunc(tv.val.toIntern()).unwrap() orelse return; // undef or extern_fn
  30527     try mod.ensureFuncBodyAnalysisQueued(func_index);
  30528 }
  30529 
  30530 fn analyzeRef(
  30531     sema: *Sema,
  30532     block: *Block,
  30533     src: LazySrcLoc,
  30534     operand: Air.Inst.Ref,
  30535 ) CompileError!Air.Inst.Ref {
  30536     const mod = sema.mod;
  30537     const operand_ty = sema.typeOf(operand);
  30538 
  30539     if (try sema.resolveMaybeUndefVal(operand)) |val| {
  30540         switch (mod.intern_pool.indexToKey(val.toIntern())) {
  30541             .extern_func => |extern_func| return sema.analyzeDeclRef(extern_func.decl),
  30542             .func => |func| return sema.analyzeDeclRef(mod.funcPtr(func.index).owner_decl),
  30543             else => {},
  30544         }
  30545         var anon_decl = try block.startAnonDecl();
  30546         defer anon_decl.deinit();
  30547         return sema.analyzeDeclRef(try anon_decl.finish(
  30548             operand_ty,
  30549             val,
  30550             .none, // default alignment
  30551         ));
  30552     }
  30553 
  30554     try sema.requireRuntimeBlock(block, src, null);
  30555     const address_space = target_util.defaultAddressSpace(mod.getTarget(), .local);
  30556     const ptr_type = try mod.ptrType(.{
  30557         .child = operand_ty.toIntern(),
  30558         .flags = .{
  30559             .is_const = true,
  30560             .address_space = address_space,
  30561         },
  30562     });
  30563     const mut_ptr_type = try mod.ptrType(.{
  30564         .child = operand_ty.toIntern(),
  30565         .flags = .{ .address_space = address_space },
  30566     });
  30567     const alloc = try block.addTy(.alloc, mut_ptr_type);
  30568     try sema.storePtr(block, src, alloc, operand);
  30569 
  30570     // TODO: Replace with sema.coerce when that supports adding pointer constness.
  30571     return sema.bitCast(block, ptr_type, alloc, src, null);
  30572 }
  30573 
  30574 fn analyzeLoad(
  30575     sema: *Sema,
  30576     block: *Block,
  30577     src: LazySrcLoc,
  30578     ptr: Air.Inst.Ref,
  30579     ptr_src: LazySrcLoc,
  30580 ) CompileError!Air.Inst.Ref {
  30581     const mod = sema.mod;
  30582     const ptr_ty = sema.typeOf(ptr);
  30583     const elem_ty = switch (ptr_ty.zigTypeTag(mod)) {
  30584         .Pointer => ptr_ty.childType(mod),
  30585         else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}),
  30586     };
  30587 
  30588     if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| {
  30589         return sema.addConstant(opv);
  30590     }
  30591 
  30592     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
  30593         if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| {
  30594             return sema.addConstant(elem_val);
  30595         }
  30596     }
  30597 
  30598     if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) {
  30599         const ptr_inst = Air.refToIndex(ptr).?;
  30600         const air_tags = sema.air_instructions.items(.tag);
  30601         if (air_tags[ptr_inst] == .ptr_elem_ptr) {
  30602             const ty_pl = sema.air_instructions.items(.data)[ptr_inst].ty_pl;
  30603             const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data;
  30604             return block.addBinOp(.ptr_elem_val, bin_op.lhs, bin_op.rhs);
  30605         }
  30606         return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{
  30607             ptr_ty.fmt(sema.mod),
  30608         });
  30609     }
  30610 
  30611     return block.addTyOp(.load, elem_ty, ptr);
  30612 }
  30613 
  30614 fn analyzeSlicePtr(
  30615     sema: *Sema,
  30616     block: *Block,
  30617     slice_src: LazySrcLoc,
  30618     slice: Air.Inst.Ref,
  30619     slice_ty: Type,
  30620 ) CompileError!Air.Inst.Ref {
  30621     const mod = sema.mod;
  30622     const result_ty = slice_ty.slicePtrFieldType(mod);
  30623     if (try sema.resolveMaybeUndefVal(slice)) |val| {
  30624         if (val.isUndef(mod)) return sema.addConstUndef(result_ty);
  30625         return sema.addConstant(val.slicePtr(mod));
  30626     }
  30627     try sema.requireRuntimeBlock(block, slice_src, null);
  30628     return block.addTyOp(.slice_ptr, result_ty, slice);
  30629 }
  30630 
  30631 fn analyzeSliceLen(
  30632     sema: *Sema,
  30633     block: *Block,
  30634     src: LazySrcLoc,
  30635     slice_inst: Air.Inst.Ref,
  30636 ) CompileError!Air.Inst.Ref {
  30637     const mod = sema.mod;
  30638     if (try sema.resolveMaybeUndefVal(slice_inst)) |slice_val| {
  30639         if (slice_val.isUndef(mod)) {
  30640             return sema.addConstUndef(Type.usize);
  30641         }
  30642         return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod));
  30643     }
  30644     try sema.requireRuntimeBlock(block, src, null);
  30645     return block.addTyOp(.slice_len, Type.usize, slice_inst);
  30646 }
  30647 
  30648 fn analyzeIsNull(
  30649     sema: *Sema,
  30650     block: *Block,
  30651     src: LazySrcLoc,
  30652     operand: Air.Inst.Ref,
  30653     invert_logic: bool,
  30654 ) CompileError!Air.Inst.Ref {
  30655     const mod = sema.mod;
  30656     const result_ty = Type.bool;
  30657     if (try sema.resolveMaybeUndefVal(operand)) |opt_val| {
  30658         if (opt_val.isUndef(mod)) {
  30659             return sema.addConstUndef(result_ty);
  30660         }
  30661         const is_null = opt_val.isNull(mod);
  30662         const bool_value = if (invert_logic) !is_null else is_null;
  30663         if (bool_value) {
  30664             return Air.Inst.Ref.bool_true;
  30665         } else {
  30666             return Air.Inst.Ref.bool_false;
  30667         }
  30668     }
  30669 
  30670     const inverted_non_null_res = if (invert_logic) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
  30671     const operand_ty = sema.typeOf(operand);
  30672     if (operand_ty.zigTypeTag(mod) == .Optional and operand_ty.optionalChild(mod).zigTypeTag(mod) == .NoReturn) {
  30673         return inverted_non_null_res;
  30674     }
  30675     if (operand_ty.zigTypeTag(mod) != .Optional and !operand_ty.isPtrLikeOptional(mod)) {
  30676         return inverted_non_null_res;
  30677     }
  30678     try sema.requireRuntimeBlock(block, src, null);
  30679     const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
  30680     return block.addUnOp(air_tag, operand);
  30681 }
  30682 
  30683 fn analyzePtrIsNonErrComptimeOnly(
  30684     sema: *Sema,
  30685     block: *Block,
  30686     src: LazySrcLoc,
  30687     operand: Air.Inst.Ref,
  30688 ) CompileError!Air.Inst.Ref {
  30689     const mod = sema.mod;
  30690     const ptr_ty = sema.typeOf(operand);
  30691     assert(ptr_ty.zigTypeTag(mod) == .Pointer);
  30692     const child_ty = ptr_ty.childType(mod);
  30693 
  30694     const child_tag = child_ty.zigTypeTag(mod);
  30695     if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return Air.Inst.Ref.bool_true;
  30696     if (child_tag == .ErrorSet) return Air.Inst.Ref.bool_false;
  30697     assert(child_tag == .ErrorUnion);
  30698 
  30699     _ = block;
  30700     _ = src;
  30701 
  30702     return Air.Inst.Ref.none;
  30703 }
  30704 
  30705 fn analyzeIsNonErrComptimeOnly(
  30706     sema: *Sema,
  30707     block: *Block,
  30708     src: LazySrcLoc,
  30709     operand: Air.Inst.Ref,
  30710 ) CompileError!Air.Inst.Ref {
  30711     const mod = sema.mod;
  30712     const operand_ty = sema.typeOf(operand);
  30713     const ot = operand_ty.zigTypeTag(mod);
  30714     if (ot != .ErrorSet and ot != .ErrorUnion) return Air.Inst.Ref.bool_true;
  30715     if (ot == .ErrorSet) return Air.Inst.Ref.bool_false;
  30716     assert(ot == .ErrorUnion);
  30717 
  30718     const payload_ty = operand_ty.errorUnionPayload(mod);
  30719     if (payload_ty.zigTypeTag(mod) == .NoReturn) {
  30720         return Air.Inst.Ref.bool_false;
  30721     }
  30722 
  30723     if (Air.refToIndex(operand)) |operand_inst| {
  30724         switch (sema.air_instructions.items(.tag)[operand_inst]) {
  30725             .wrap_errunion_payload => return Air.Inst.Ref.bool_true,
  30726             .wrap_errunion_err => return Air.Inst.Ref.bool_false,
  30727             else => {},
  30728         }
  30729     } else if (operand == .undef) {
  30730         return sema.addConstUndef(Type.bool);
  30731     } else if (@intFromEnum(operand) < InternPool.static_len) {
  30732         // None of the ref tags can be errors.
  30733         return Air.Inst.Ref.bool_true;
  30734     }
  30735 
  30736     const maybe_operand_val = try sema.resolveMaybeUndefVal(operand);
  30737 
  30738     // exception if the error union error set is known to be empty,
  30739     // we allow the comparison but always make it comptime-known.
  30740     const set_ty = operand_ty.errorUnionSet(mod);
  30741     switch (set_ty.toIntern()) {
  30742         .anyerror_type => {},
  30743         else => switch (mod.intern_pool.indexToKey(set_ty.toIntern())) {
  30744             .error_set_type => |error_set_type| {
  30745                 if (error_set_type.names.len == 0) return Air.Inst.Ref.bool_true;
  30746             },
  30747             .inferred_error_set_type => |ies_index| blk: {
  30748                 // If the error set is empty, we must return a comptime true or false.
  30749                 // However we want to avoid unnecessarily resolving an inferred error set
  30750                 // in case it is already non-empty.
  30751                 const ies = mod.inferredErrorSetPtr(ies_index);
  30752                 if (ies.is_anyerror) break :blk;
  30753                 if (ies.errors.count() != 0) break :blk;
  30754                 if (maybe_operand_val == null) {
  30755                     // Try to avoid resolving inferred error set if possible.
  30756                     if (ies.errors.count() != 0) break :blk;
  30757                     if (ies.is_anyerror) break :blk;
  30758                     for (ies.inferred_error_sets.keys()) |other_ies_index| {
  30759                         if (ies_index == other_ies_index) continue;
  30760                         try sema.resolveInferredErrorSet(block, src, other_ies_index);
  30761                         const other_ies = mod.inferredErrorSetPtr(other_ies_index);
  30762                         if (other_ies.is_anyerror) {
  30763                             ies.is_anyerror = true;
  30764                             ies.is_resolved = true;
  30765                             break :blk;
  30766                         }
  30767 
  30768                         if (other_ies.errors.count() != 0) break :blk;
  30769                     }
  30770                     if (ies.func == sema.owner_func_index.unwrap()) {
  30771                         // We're checking the inferred errorset of the current function and none of
  30772                         // its child inferred error sets contained any errors meaning that any value
  30773                         // so far with this type can't contain errors either.
  30774                         return Air.Inst.Ref.bool_true;
  30775                     }
  30776                     try sema.resolveInferredErrorSet(block, src, ies_index);
  30777                     if (ies.is_anyerror) break :blk;
  30778                     if (ies.errors.count() == 0) return Air.Inst.Ref.bool_true;
  30779                 }
  30780             },
  30781             else => unreachable,
  30782         },
  30783     }
  30784 
  30785     if (maybe_operand_val) |err_union| {
  30786         if (err_union.isUndef(mod)) {
  30787             return sema.addConstUndef(Type.bool);
  30788         }
  30789         if (err_union.getErrorName(mod) == .none) {
  30790             return Air.Inst.Ref.bool_true;
  30791         } else {
  30792             return Air.Inst.Ref.bool_false;
  30793         }
  30794     }
  30795     return Air.Inst.Ref.none;
  30796 }
  30797 
  30798 fn analyzeIsNonErr(
  30799     sema: *Sema,
  30800     block: *Block,
  30801     src: LazySrcLoc,
  30802     operand: Air.Inst.Ref,
  30803 ) CompileError!Air.Inst.Ref {
  30804     const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
  30805     if (result == .none) {
  30806         try sema.requireRuntimeBlock(block, src, null);
  30807         return block.addUnOp(.is_non_err, operand);
  30808     } else {
  30809         return result;
  30810     }
  30811 }
  30812 
  30813 fn analyzePtrIsNonErr(
  30814     sema: *Sema,
  30815     block: *Block,
  30816     src: LazySrcLoc,
  30817     operand: Air.Inst.Ref,
  30818 ) CompileError!Air.Inst.Ref {
  30819     const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand);
  30820     if (result == .none) {
  30821         try sema.requireRuntimeBlock(block, src, null);
  30822         return block.addUnOp(.is_non_err_ptr, operand);
  30823     } else {
  30824         return result;
  30825     }
  30826 }
  30827 
  30828 fn analyzeSlice(
  30829     sema: *Sema,
  30830     block: *Block,
  30831     src: LazySrcLoc,
  30832     ptr_ptr: Air.Inst.Ref,
  30833     uncasted_start: Air.Inst.Ref,
  30834     uncasted_end_opt: Air.Inst.Ref,
  30835     sentinel_opt: Air.Inst.Ref,
  30836     sentinel_src: LazySrcLoc,
  30837     ptr_src: LazySrcLoc,
  30838     start_src: LazySrcLoc,
  30839     end_src: LazySrcLoc,
  30840     by_length: bool,
  30841 ) CompileError!Air.Inst.Ref {
  30842     const mod = sema.mod;
  30843     // Slice expressions can operate on a variable whose type is an array. This requires
  30844     // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
  30845     const ptr_ptr_ty = sema.typeOf(ptr_ptr);
  30846     const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(mod)) {
  30847         .Pointer => ptr_ptr_ty.childType(mod),
  30848         else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(mod)}),
  30849     };
  30850 
  30851     var array_ty = ptr_ptr_child_ty;
  30852     var slice_ty = ptr_ptr_ty;
  30853     var ptr_or_slice = ptr_ptr;
  30854     var elem_ty: Type = undefined;
  30855     var ptr_sentinel: ?Value = null;
  30856     switch (ptr_ptr_child_ty.zigTypeTag(mod)) {
  30857         .Array => {
  30858             ptr_sentinel = ptr_ptr_child_ty.sentinel(mod);
  30859             elem_ty = ptr_ptr_child_ty.childType(mod);
  30860         },
  30861         .Pointer => switch (ptr_ptr_child_ty.ptrSize(mod)) {
  30862             .One => {
  30863                 const double_child_ty = ptr_ptr_child_ty.childType(mod);
  30864                 if (double_child_ty.zigTypeTag(mod) == .Array) {
  30865                     ptr_sentinel = double_child_ty.sentinel(mod);
  30866                     ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  30867                     slice_ty = ptr_ptr_child_ty;
  30868                     array_ty = double_child_ty;
  30869                     elem_ty = double_child_ty.childType(mod);
  30870                 } else {
  30871                     return sema.fail(block, src, "slice of single-item pointer", .{});
  30872                 }
  30873             },
  30874             .Many, .C => {
  30875                 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod);
  30876                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  30877                 slice_ty = ptr_ptr_child_ty;
  30878                 array_ty = ptr_ptr_child_ty;
  30879                 elem_ty = ptr_ptr_child_ty.childType(mod);
  30880 
  30881                 if (ptr_ptr_child_ty.ptrSize(mod) == .C) {
  30882                     if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| {
  30883                         if (ptr_val.isNull(mod)) {
  30884                             return sema.fail(block, src, "slice of null pointer", .{});
  30885                         }
  30886                     }
  30887                 }
  30888             },
  30889             .Slice => {
  30890                 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod);
  30891                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  30892                 slice_ty = ptr_ptr_child_ty;
  30893                 array_ty = ptr_ptr_child_ty;
  30894                 elem_ty = ptr_ptr_child_ty.childType(mod);
  30895             },
  30896         },
  30897         else => return sema.fail(block, src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(mod)}),
  30898     }
  30899 
  30900     const ptr = if (slice_ty.isSlice(mod))
  30901         try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty)
  30902     else if (array_ty.zigTypeTag(mod) == .Array) ptr: {
  30903         var manyptr_ty_key = mod.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type;
  30904         assert(manyptr_ty_key.child == array_ty.toIntern());
  30905         assert(manyptr_ty_key.flags.size == .One);
  30906         manyptr_ty_key.child = elem_ty.toIntern();
  30907         manyptr_ty_key.flags.size = .Many;
  30908         break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(manyptr_ty_key), ptr_or_slice, ptr_src);
  30909     } else ptr_or_slice;
  30910 
  30911     const start = try sema.coerce(block, Type.usize, uncasted_start, start_src);
  30912     const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
  30913     const new_ptr_ty = sema.typeOf(new_ptr);
  30914 
  30915     // true if and only if the end index of the slice, implicitly or explicitly, equals
  30916     // the length of the underlying object being sliced. we might learn the length of the
  30917     // underlying object because it is an array (which has the length in the type), or
  30918     // we might learn of the length because it is a comptime-known slice value.
  30919     var end_is_len = uncasted_end_opt == .none;
  30920     const end = e: {
  30921         if (array_ty.zigTypeTag(mod) == .Array) {
  30922             const len_val = try mod.intValue(Type.usize, array_ty.arrayLen(mod));
  30923 
  30924             if (!end_is_len) {
  30925                 const end = if (by_length) end: {
  30926                     const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  30927                     const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  30928                     break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
  30929                 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  30930                 if (try sema.resolveMaybeUndefVal(end)) |end_val| {
  30931                     const len_s_val = try mod.intValue(
  30932                         Type.usize,
  30933                         array_ty.arrayLenIncludingSentinel(mod),
  30934                     );
  30935                     if (!(try sema.compareAll(end_val, .lte, len_s_val, Type.usize))) {
  30936                         const sentinel_label: []const u8 = if (array_ty.sentinel(mod) != null)
  30937                             " +1 (sentinel)"
  30938                         else
  30939                             "";
  30940 
  30941                         return sema.fail(
  30942                             block,
  30943                             end_src,
  30944                             "end index {} out of bounds for array of length {}{s}",
  30945                             .{
  30946                                 end_val.fmtValue(Type.usize, mod),
  30947                                 len_val.fmtValue(Type.usize, mod),
  30948                                 sentinel_label,
  30949                             },
  30950                         );
  30951                     }
  30952 
  30953                     // end_is_len is only true if we are NOT using the sentinel
  30954                     // length. For sentinel-length, we don't want the type to
  30955                     // contain the sentinel.
  30956                     if (end_val.eql(len_val, Type.usize, mod)) {
  30957                         end_is_len = true;
  30958                     }
  30959                 }
  30960                 break :e end;
  30961             }
  30962 
  30963             break :e try sema.addConstant(len_val);
  30964         } else if (slice_ty.isSlice(mod)) {
  30965             if (!end_is_len) {
  30966                 const end = if (by_length) end: {
  30967                     const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  30968                     const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  30969                     break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
  30970                 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  30971                 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  30972                     if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| {
  30973                         if (slice_val.isUndef(mod)) {
  30974                             return sema.fail(block, src, "slice of undefined", .{});
  30975                         }
  30976                         const has_sentinel = slice_ty.sentinel(mod) != null;
  30977                         const slice_len = slice_val.sliceLen(mod);
  30978                         const len_plus_sent = slice_len + @intFromBool(has_sentinel);
  30979                         const slice_len_val_with_sentinel = try mod.intValue(Type.usize, len_plus_sent);
  30980                         if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, Type.usize))) {
  30981                             const sentinel_label: []const u8 = if (has_sentinel)
  30982                                 " +1 (sentinel)"
  30983                             else
  30984                                 "";
  30985 
  30986                             return sema.fail(
  30987                                 block,
  30988                                 end_src,
  30989                                 "end index {} out of bounds for slice of length {d}{s}",
  30990                                 .{
  30991                                     end_val.fmtValue(Type.usize, mod),
  30992                                     slice_val.sliceLen(mod),
  30993                                     sentinel_label,
  30994                                 },
  30995                             );
  30996                         }
  30997 
  30998                         // If the slice has a sentinel, we consider end_is_len
  30999                         // is only true if it equals the length WITHOUT the
  31000                         // sentinel, so we don't add a sentinel type.
  31001                         const slice_len_val = try mod.intValue(Type.usize, slice_len);
  31002                         if (end_val.eql(slice_len_val, Type.usize, mod)) {
  31003                             end_is_len = true;
  31004                         }
  31005                     }
  31006                 }
  31007                 break :e end;
  31008             }
  31009             break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
  31010         }
  31011         if (!end_is_len) {
  31012             if (by_length) {
  31013                 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  31014                 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  31015                 break :e try sema.coerce(block, Type.usize, uncasted_end, end_src);
  31016             } else break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
  31017         }
  31018         return sema.fail(block, src, "slice of pointer must include end value", .{});
  31019     };
  31020 
  31021     const sentinel = s: {
  31022         if (sentinel_opt != .none) {
  31023             const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
  31024             break :s try sema.resolveConstValue(block, sentinel_src, casted, "slice sentinel must be comptime-known");
  31025         }
  31026         // If we are slicing to the end of something that is sentinel-terminated
  31027         // then the resulting slice type is also sentinel-terminated.
  31028         if (end_is_len) {
  31029             if (ptr_sentinel) |sent| {
  31030                 break :s sent;
  31031             }
  31032         }
  31033         break :s null;
  31034     };
  31035     const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
  31036 
  31037     var checked_start_lte_end = by_length;
  31038     var runtime_src: ?LazySrcLoc = null;
  31039 
  31040     // requirement: start <= end
  31041     if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  31042         if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
  31043             if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
  31044                 return sema.fail(
  31045                     block,
  31046                     start_src,
  31047                     "start index {} is larger than end index {}",
  31048                     .{
  31049                         start_val.fmtValue(Type.usize, mod),
  31050                         end_val.fmtValue(Type.usize, mod),
  31051                     },
  31052                 );
  31053             }
  31054             checked_start_lte_end = true;
  31055             if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: {
  31056                 const expected_sentinel = sentinel orelse break :sentinel_check;
  31057                 const start_int = start_val.getUnsignedInt(mod).?;
  31058                 const end_int = end_val.getUnsignedInt(mod).?;
  31059                 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
  31060 
  31061                 const many_ptr_ty = try mod.manyConstPtrType(elem_ty);
  31062                 const many_ptr_val = try mod.getCoerced(ptr_val, many_ptr_ty);
  31063                 const elem_ptr_ty = try mod.singleConstPtrType(elem_ty);
  31064                 const elem_ptr = try many_ptr_val.elemPtr(elem_ptr_ty, sentinel_index, mod);
  31065                 const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty);
  31066                 const actual_sentinel = switch (res) {
  31067                     .runtime_load => break :sentinel_check,
  31068                     .val => |v| v,
  31069                     .needed_well_defined => |ty| return sema.fail(
  31070                         block,
  31071                         src,
  31072                         "comptime dereference requires '{}' to have a well-defined layout, but it does not.",
  31073                         .{ty.fmt(mod)},
  31074                     ),
  31075                     .out_of_bounds => |ty| return sema.fail(
  31076                         block,
  31077                         end_src,
  31078                         "slice end index {d} exceeds bounds of containing decl of type '{}'",
  31079                         .{ end_int, ty.fmt(mod) },
  31080                     ),
  31081                 };
  31082 
  31083                 if (!actual_sentinel.eql(expected_sentinel, elem_ty, mod)) {
  31084                     const msg = msg: {
  31085                         const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{});
  31086                         errdefer msg.destroy(sema.gpa);
  31087                         try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{
  31088                             expected_sentinel.fmtValue(elem_ty, mod),
  31089                             actual_sentinel.fmtValue(elem_ty, mod),
  31090                         });
  31091 
  31092                         break :msg msg;
  31093                     };
  31094                     return sema.failWithOwnedErrorMsg(msg);
  31095                 }
  31096             } else {
  31097                 runtime_src = ptr_src;
  31098             }
  31099         } else {
  31100             runtime_src = start_src;
  31101         }
  31102     } else {
  31103         runtime_src = end_src;
  31104     }
  31105 
  31106     if (!checked_start_lte_end and block.wantSafety() and !block.is_comptime) {
  31107         // requirement: start <= end
  31108         assert(!block.is_comptime);
  31109         try sema.requireRuntimeBlock(block, src, runtime_src.?);
  31110         const ok = try block.addBinOp(.cmp_lte, start, end);
  31111         if (!sema.mod.comp.formatted_panics) {
  31112             try sema.addSafetyCheck(block, ok, .start_index_greater_than_end);
  31113         } else {
  31114             try sema.safetyCheckFormatted(block, ok, "panicStartGreaterThanEnd", &.{ start, end });
  31115         }
  31116     }
  31117     const new_len = if (by_length)
  31118         try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
  31119     else
  31120         try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
  31121     const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
  31122 
  31123     const new_ptr_ty_info = new_ptr_ty.ptrInfo(mod);
  31124     const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(mod) != .C;
  31125 
  31126     if (opt_new_len_val) |new_len_val| {
  31127         const new_len_int = new_len_val.toUnsignedInt(mod);
  31128 
  31129         const return_ty = try mod.ptrType(.{
  31130             .child = (try mod.arrayType(.{
  31131                 .len = new_len_int,
  31132                 .sentinel = if (sentinel) |s| s.toIntern() else .none,
  31133                 .child = elem_ty.toIntern(),
  31134             })).toIntern(),
  31135             .flags = .{
  31136                 .alignment = new_ptr_ty_info.flags.alignment,
  31137                 .is_const = new_ptr_ty_info.flags.is_const,
  31138                 .is_allowzero = new_allowzero,
  31139                 .is_volatile = new_ptr_ty_info.flags.is_volatile,
  31140                 .address_space = new_ptr_ty_info.flags.address_space,
  31141             },
  31142         });
  31143 
  31144         const opt_new_ptr_val = try sema.resolveMaybeUndefVal(new_ptr);
  31145         const new_ptr_val = opt_new_ptr_val orelse {
  31146             const result = try block.addBitCast(return_ty, new_ptr);
  31147             if (block.wantSafety()) {
  31148                 // requirement: slicing C ptr is non-null
  31149                 if (ptr_ptr_child_ty.isCPtr(mod)) {
  31150                     const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
  31151                     try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
  31152                 }
  31153 
  31154                 if (slice_ty.isSlice(mod)) {
  31155                     const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);
  31156                     const actual_len = if (slice_ty.sentinel(mod) == null)
  31157                         slice_len_inst
  31158                     else
  31159                         try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
  31160 
  31161                     const actual_end = if (slice_sentinel != null)
  31162                         try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
  31163                     else
  31164                         end;
  31165 
  31166                     try sema.panicIndexOutOfBounds(block, actual_end, actual_len, .cmp_lte);
  31167                 }
  31168 
  31169                 // requirement: result[new_len] == slice_sentinel
  31170                 try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
  31171             }
  31172             return result;
  31173         };
  31174 
  31175         if (!new_ptr_val.isUndef(mod)) {
  31176             return sema.addConstant(try mod.getCoerced(
  31177                 (try new_ptr_val.intern(new_ptr_ty, mod)).toValue(),
  31178                 return_ty,
  31179             ));
  31180         }
  31181 
  31182         // Special case: @as([]i32, undefined)[x..x]
  31183         if (new_len_int == 0) {
  31184             return sema.addConstUndef(return_ty);
  31185         }
  31186 
  31187         return sema.fail(block, src, "non-zero length slice of undefined pointer", .{});
  31188     }
  31189 
  31190     const return_ty = try mod.ptrType(.{
  31191         .child = elem_ty.toIntern(),
  31192         .sentinel = if (sentinel) |s| s.toIntern() else .none,
  31193         .flags = .{
  31194             .size = .Slice,
  31195             .alignment = new_ptr_ty_info.flags.alignment,
  31196             .is_const = new_ptr_ty_info.flags.is_const,
  31197             .is_volatile = new_ptr_ty_info.flags.is_volatile,
  31198             .is_allowzero = new_allowzero,
  31199             .address_space = new_ptr_ty_info.flags.address_space,
  31200         },
  31201     });
  31202 
  31203     try sema.requireRuntimeBlock(block, src, runtime_src.?);
  31204     if (block.wantSafety()) {
  31205         // requirement: slicing C ptr is non-null
  31206         if (ptr_ptr_child_ty.isCPtr(mod)) {
  31207             const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
  31208             try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
  31209         }
  31210 
  31211         // requirement: end <= len
  31212         const opt_len_inst = if (array_ty.zigTypeTag(mod) == .Array)
  31213             try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel(mod))
  31214         else if (slice_ty.isSlice(mod)) blk: {
  31215             if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
  31216                 // we don't need to add one for sentinels because the
  31217                 // underlying value data includes the sentinel
  31218                 break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod));
  31219             }
  31220 
  31221             const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);
  31222             if (slice_ty.sentinel(mod) == null) break :blk slice_len_inst;
  31223 
  31224             // we have to add one because slice lengths don't include the sentinel
  31225             break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
  31226         } else null;
  31227         if (opt_len_inst) |len_inst| {
  31228             const actual_end = if (slice_sentinel != null)
  31229                 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
  31230             else
  31231                 end;
  31232             try sema.panicIndexOutOfBounds(block, actual_end, len_inst, .cmp_lte);
  31233         }
  31234 
  31235         // requirement: start <= end
  31236         try sema.panicIndexOutOfBounds(block, start, end, .cmp_lte);
  31237     }
  31238     const result = try block.addInst(.{
  31239         .tag = .slice,
  31240         .data = .{ .ty_pl = .{
  31241             .ty = try sema.addType(return_ty),
  31242             .payload = try sema.addExtra(Air.Bin{
  31243                 .lhs = new_ptr,
  31244                 .rhs = new_len,
  31245             }),
  31246         } },
  31247     });
  31248     if (block.wantSafety()) {
  31249         // requirement: result[new_len] == slice_sentinel
  31250         try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
  31251     }
  31252     return result;
  31253 }
  31254 
  31255 /// Asserts that lhs and rhs types are both numeric.
  31256 fn cmpNumeric(
  31257     sema: *Sema,
  31258     block: *Block,
  31259     src: LazySrcLoc,
  31260     uncasted_lhs: Air.Inst.Ref,
  31261     uncasted_rhs: Air.Inst.Ref,
  31262     op: std.math.CompareOperator,
  31263     lhs_src: LazySrcLoc,
  31264     rhs_src: LazySrcLoc,
  31265 ) CompileError!Air.Inst.Ref {
  31266     const mod = sema.mod;
  31267     const lhs_ty = sema.typeOf(uncasted_lhs);
  31268     const rhs_ty = sema.typeOf(uncasted_rhs);
  31269 
  31270     assert(lhs_ty.isNumeric(mod));
  31271     assert(rhs_ty.isNumeric(mod));
  31272 
  31273     const lhs_ty_tag = lhs_ty.zigTypeTag(mod);
  31274     const rhs_ty_tag = rhs_ty.zigTypeTag(mod);
  31275     const target = mod.getTarget();
  31276 
  31277     // One exception to heterogeneous comparison: comptime_float needs to
  31278     // coerce to fixed-width float.
  31279 
  31280     const lhs = if (lhs_ty_tag == .ComptimeFloat and rhs_ty_tag == .Float)
  31281         try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src)
  31282     else
  31283         uncasted_lhs;
  31284 
  31285     const rhs = if (lhs_ty_tag == .Float and rhs_ty_tag == .ComptimeFloat)
  31286         try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src)
  31287     else
  31288         uncasted_rhs;
  31289 
  31290     const runtime_src: LazySrcLoc = src: {
  31291         if (try sema.resolveMaybeUndefVal(lhs)) |lhs_val| {
  31292             if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| {
  31293                 // Compare ints: const vs. undefined (or vice versa)
  31294                 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)) {
  31295                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
  31296                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
  31297                     }
  31298                 } 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)) {
  31299                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
  31300                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
  31301                     }
  31302                 }
  31303 
  31304                 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  31305                     return sema.addConstUndef(Type.bool);
  31306                 }
  31307                 if (lhs_val.isNan(mod) or rhs_val.isNan(mod)) {
  31308                     if (op == std.math.CompareOperator.neq) {
  31309                         return Air.Inst.Ref.bool_true;
  31310                     } else {
  31311                         return Air.Inst.Ref.bool_false;
  31312                     }
  31313                 }
  31314                 if (try Value.compareHeteroAdvanced(lhs_val, op, rhs_val, mod, sema)) {
  31315                     return Air.Inst.Ref.bool_true;
  31316                 } else {
  31317                     return Air.Inst.Ref.bool_false;
  31318                 }
  31319             } else {
  31320                 if (!lhs_val.isUndef(mod) and (lhs_ty.isInt(mod) or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt(mod)) {
  31321                     // Compare ints: const vs. var
  31322                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
  31323                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
  31324                     }
  31325                 }
  31326                 break :src rhs_src;
  31327             }
  31328         } else {
  31329             if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| {
  31330                 if (!rhs_val.isUndef(mod) and (rhs_ty.isInt(mod) or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt(mod)) {
  31331                     // Compare ints: var vs. const
  31332                     if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
  31333                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
  31334                     }
  31335                 }
  31336             }
  31337             break :src lhs_src;
  31338         }
  31339     };
  31340 
  31341     // TODO handle comparisons against lazy zero values
  31342     // Some values can be compared against zero without being runtime-known or without forcing
  31343     // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
  31344     // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
  31345     // of this function if we don't need to.
  31346     try sema.requireRuntimeBlock(block, src, runtime_src);
  31347 
  31348     // For floats, emit a float comparison instruction.
  31349     const lhs_is_float = switch (lhs_ty_tag) {
  31350         .Float, .ComptimeFloat => true,
  31351         else => false,
  31352     };
  31353     const rhs_is_float = switch (rhs_ty_tag) {
  31354         .Float, .ComptimeFloat => true,
  31355         else => false,
  31356     };
  31357 
  31358     if (lhs_is_float and rhs_is_float) {
  31359         // Smaller fixed-width floats coerce to larger fixed-width floats.
  31360         // comptime_float coerces to fixed-width float.
  31361         const dest_ty = x: {
  31362             if (lhs_ty_tag == .ComptimeFloat) {
  31363                 break :x rhs_ty;
  31364             } else if (rhs_ty_tag == .ComptimeFloat) {
  31365                 break :x lhs_ty;
  31366             }
  31367             if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) {
  31368                 break :x lhs_ty;
  31369             } else {
  31370                 break :x rhs_ty;
  31371             }
  31372         };
  31373         const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
  31374         const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
  31375         return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs);
  31376     }
  31377     // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
  31378     // For mixed signed and unsigned integers, implicit cast both operands to a signed
  31379     // integer with + 1 bit.
  31380     // For mixed floats and integers, extract the integer part from the float, cast that to
  31381     // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
  31382     // add/subtract 1.
  31383     const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|
  31384         !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema))
  31385     else
  31386         (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(mod));
  31387     const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
  31388         !(try rhs_val.compareAllWithZeroAdvanced(.gte, sema))
  31389     else
  31390         (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(mod));
  31391     const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
  31392 
  31393     var dest_float_type: ?Type = null;
  31394 
  31395     var lhs_bits: usize = undefined;
  31396     if (try sema.resolveMaybeUndefLazyVal(lhs)) |lhs_val| {
  31397         if (lhs_val.isUndef(mod))
  31398             return sema.addConstUndef(Type.bool);
  31399         if (lhs_val.isNan(mod)) switch (op) {
  31400             .neq => return Air.Inst.Ref.bool_true,
  31401             else => return Air.Inst.Ref.bool_false,
  31402         };
  31403         if (lhs_val.isInf(mod)) switch (op) {
  31404             .neq => return Air.Inst.Ref.bool_true,
  31405             .eq => return Air.Inst.Ref.bool_false,
  31406             .gt, .gte => return if (lhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true,
  31407             .lt, .lte => return if (lhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false,
  31408         };
  31409         if (!rhs_is_signed) {
  31410             switch (lhs_val.orderAgainstZero(mod)) {
  31411                 .gt => {},
  31412                 .eq => switch (op) { // LHS = 0, RHS is unsigned
  31413                     .lte => return Air.Inst.Ref.bool_true,
  31414                     .gt => return Air.Inst.Ref.bool_false,
  31415                     else => {},
  31416                 },
  31417                 .lt => switch (op) { // LHS < 0, RHS is unsigned
  31418                     .neq, .lt, .lte => return Air.Inst.Ref.bool_true,
  31419                     .eq, .gt, .gte => return Air.Inst.Ref.bool_false,
  31420                 },
  31421             }
  31422         }
  31423         if (lhs_is_float) {
  31424             if (lhs_val.floatHasFraction(mod)) {
  31425                 switch (op) {
  31426                     .eq => return Air.Inst.Ref.bool_false,
  31427                     .neq => return Air.Inst.Ref.bool_true,
  31428                     else => {},
  31429                 }
  31430             }
  31431 
  31432             var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128, mod));
  31433             defer bigint.deinit();
  31434             if (lhs_val.floatHasFraction(mod)) {
  31435                 if (lhs_is_signed) {
  31436                     try bigint.addScalar(&bigint, -1);
  31437                 } else {
  31438                     try bigint.addScalar(&bigint, 1);
  31439                 }
  31440             }
  31441             lhs_bits = bigint.toConst().bitCountTwosComp();
  31442         } else {
  31443             lhs_bits = lhs_val.intBitCountTwosComp(mod);
  31444         }
  31445         lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed);
  31446     } else if (lhs_is_float) {
  31447         dest_float_type = lhs_ty;
  31448     } else {
  31449         const int_info = lhs_ty.intInfo(mod);
  31450         lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
  31451     }
  31452 
  31453     var rhs_bits: usize = undefined;
  31454     if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| {
  31455         if (rhs_val.isUndef(mod))
  31456             return sema.addConstUndef(Type.bool);
  31457         if (rhs_val.isNan(mod)) switch (op) {
  31458             .neq => return Air.Inst.Ref.bool_true,
  31459             else => return Air.Inst.Ref.bool_false,
  31460         };
  31461         if (rhs_val.isInf(mod)) switch (op) {
  31462             .neq => return Air.Inst.Ref.bool_true,
  31463             .eq => return Air.Inst.Ref.bool_false,
  31464             .gt, .gte => return if (rhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false,
  31465             .lt, .lte => return if (rhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true,
  31466         };
  31467         if (!lhs_is_signed) {
  31468             switch (rhs_val.orderAgainstZero(mod)) {
  31469                 .gt => {},
  31470                 .eq => switch (op) { // RHS = 0, LHS is unsigned
  31471                     .gte => return Air.Inst.Ref.bool_true,
  31472                     .lt => return Air.Inst.Ref.bool_false,
  31473                     else => {},
  31474                 },
  31475                 .lt => switch (op) { // RHS < 0, LHS is unsigned
  31476                     .neq, .gt, .gte => return Air.Inst.Ref.bool_true,
  31477                     .eq, .lt, .lte => return Air.Inst.Ref.bool_false,
  31478                 },
  31479             }
  31480         }
  31481         if (rhs_is_float) {
  31482             if (rhs_val.floatHasFraction(mod)) {
  31483                 switch (op) {
  31484                     .eq => return Air.Inst.Ref.bool_false,
  31485                     .neq => return Air.Inst.Ref.bool_true,
  31486                     else => {},
  31487                 }
  31488             }
  31489 
  31490             var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128, mod));
  31491             defer bigint.deinit();
  31492             if (rhs_val.floatHasFraction(mod)) {
  31493                 if (rhs_is_signed) {
  31494                     try bigint.addScalar(&bigint, -1);
  31495                 } else {
  31496                     try bigint.addScalar(&bigint, 1);
  31497                 }
  31498             }
  31499             rhs_bits = bigint.toConst().bitCountTwosComp();
  31500         } else {
  31501             rhs_bits = rhs_val.intBitCountTwosComp(mod);
  31502         }
  31503         rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed);
  31504     } else if (rhs_is_float) {
  31505         dest_float_type = rhs_ty;
  31506     } else {
  31507         const int_info = rhs_ty.intInfo(mod);
  31508         rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
  31509     }
  31510 
  31511     const dest_ty = if (dest_float_type) |ft| ft else blk: {
  31512         const max_bits = @max(lhs_bits, rhs_bits);
  31513         const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits});
  31514         const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
  31515         break :blk try mod.intType(signedness, casted_bits);
  31516     };
  31517     const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
  31518     const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
  31519 
  31520     return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs);
  31521 }
  31522 
  31523 /// Asserts that LHS value is an int or comptime int and not undefined, and
  31524 /// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to
  31525 /// determine whether `op` has a guaranteed result.
  31526 /// If it cannot be determined, returns null.
  31527 /// Otherwise returns a bool for the guaranteed comparison operation.
  31528 fn compareIntsOnlyPossibleResult(
  31529     sema: *Sema,
  31530     lhs_val: Value,
  31531     op: std.math.CompareOperator,
  31532     rhs_ty: Type,
  31533 ) Allocator.Error!?bool {
  31534     const mod = sema.mod;
  31535     const rhs_info = rhs_ty.intInfo(mod);
  31536     const vs_zero = lhs_val.orderAgainstZeroAdvanced(mod, sema) catch unreachable;
  31537     const is_zero = vs_zero == .eq;
  31538     const is_negative = vs_zero == .lt;
  31539     const is_positive = vs_zero == .gt;
  31540 
  31541     // Anything vs. zero-sized type has guaranteed outcome.
  31542     if (rhs_info.bits == 0) return switch (op) {
  31543         .eq, .lte, .gte => is_zero,
  31544         .neq, .lt, .gt => !is_zero,
  31545     };
  31546 
  31547     // Special case for i1, which can only be 0 or -1.
  31548     // Zero and positive ints have guaranteed outcome.
  31549     if (rhs_info.bits == 1 and rhs_info.signedness == .signed) {
  31550         if (is_positive) return switch (op) {
  31551             .gt, .gte, .neq => true,
  31552             .lt, .lte, .eq => false,
  31553         };
  31554         if (is_zero) return switch (op) {
  31555             .gte => true,
  31556             .lt => false,
  31557             .gt, .lte, .eq, .neq => null,
  31558         };
  31559     }
  31560 
  31561     // Negative vs. unsigned has guaranteed outcome.
  31562     if (rhs_info.signedness == .unsigned and is_negative) return switch (op) {
  31563         .eq, .gt, .gte => false,
  31564         .neq, .lt, .lte => true,
  31565     };
  31566 
  31567     const sign_adj = @intFromBool(!is_negative and rhs_info.signedness == .signed);
  31568     const req_bits = lhs_val.intBitCountTwosComp(mod) + sign_adj;
  31569 
  31570     // No sized type can have more than 65535 bits.
  31571     // The RHS type operand is either a runtime value or sized (but undefined) constant.
  31572     if (req_bits > 65535) return switch (op) {
  31573         .lt, .lte => is_negative,
  31574         .gt, .gte => is_positive,
  31575         .eq => false,
  31576         .neq => true,
  31577     };
  31578     const fits = req_bits <= rhs_info.bits;
  31579 
  31580     // Oversized int has guaranteed outcome.
  31581     switch (op) {
  31582         .eq => return if (!fits) false else null,
  31583         .neq => return if (!fits) true else null,
  31584         .lt, .lte => if (!fits) return is_negative,
  31585         .gt, .gte => if (!fits) return !is_negative,
  31586     }
  31587 
  31588     // For any other comparison, we need to know if the LHS value is
  31589     // equal to the maximum or minimum possible value of the RHS type.
  31590     const edge: struct { min: bool, max: bool } = edge: {
  31591         if (is_zero and rhs_info.signedness == .unsigned) break :edge .{
  31592             .min = true,
  31593             .max = false,
  31594         };
  31595 
  31596         if (req_bits != rhs_info.bits) break :edge .{
  31597             .min = false,
  31598             .max = false,
  31599         };
  31600 
  31601         const ty = try mod.intType(
  31602             if (is_negative) .signed else .unsigned,
  31603             @as(u16, @intCast(req_bits)),
  31604         );
  31605         const pop_count = lhs_val.popCount(ty, mod);
  31606 
  31607         if (is_negative) {
  31608             break :edge .{
  31609                 .min = pop_count == 1,
  31610                 .max = false,
  31611             };
  31612         } else {
  31613             break :edge .{
  31614                 .min = false,
  31615                 .max = pop_count == req_bits - sign_adj,
  31616             };
  31617         }
  31618     };
  31619 
  31620     assert(fits);
  31621     return switch (op) {
  31622         .lt => if (edge.max) false else null,
  31623         .lte => if (edge.min) true else null,
  31624         .gt => if (edge.min) false else null,
  31625         .gte => if (edge.max) true else null,
  31626         .eq, .neq => unreachable,
  31627     };
  31628 }
  31629 
  31630 /// Asserts that lhs and rhs types are both vectors.
  31631 fn cmpVector(
  31632     sema: *Sema,
  31633     block: *Block,
  31634     src: LazySrcLoc,
  31635     lhs: Air.Inst.Ref,
  31636     rhs: Air.Inst.Ref,
  31637     op: std.math.CompareOperator,
  31638     lhs_src: LazySrcLoc,
  31639     rhs_src: LazySrcLoc,
  31640 ) CompileError!Air.Inst.Ref {
  31641     const mod = sema.mod;
  31642     const lhs_ty = sema.typeOf(lhs);
  31643     const rhs_ty = sema.typeOf(rhs);
  31644     assert(lhs_ty.zigTypeTag(mod) == .Vector);
  31645     assert(rhs_ty.zigTypeTag(mod) == .Vector);
  31646     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  31647 
  31648     const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } });
  31649     const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src);
  31650     const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src);
  31651 
  31652     const result_ty = try mod.vectorType(.{
  31653         .len = lhs_ty.vectorLen(mod),
  31654         .child = .bool_type,
  31655     });
  31656 
  31657     const runtime_src: LazySrcLoc = src: {
  31658         if (try sema.resolveMaybeUndefVal(casted_lhs)) |lhs_val| {
  31659             if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| {
  31660                 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) {
  31661                     return sema.addConstUndef(result_ty);
  31662                 }
  31663                 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
  31664                 return sema.addConstant(cmp_val);
  31665             } else {
  31666                 break :src rhs_src;
  31667             }
  31668         } else {
  31669             break :src lhs_src;
  31670         }
  31671     };
  31672 
  31673     try sema.requireRuntimeBlock(block, src, runtime_src);
  31674     return block.addCmpVector(casted_lhs, casted_rhs, op);
  31675 }
  31676 
  31677 fn wrapOptional(
  31678     sema: *Sema,
  31679     block: *Block,
  31680     dest_ty: Type,
  31681     inst: Air.Inst.Ref,
  31682     inst_src: LazySrcLoc,
  31683 ) !Air.Inst.Ref {
  31684     if (try sema.resolveMaybeUndefVal(inst)) |val| {
  31685         return sema.addConstant((try sema.mod.intern(.{ .opt = .{
  31686             .ty = dest_ty.toIntern(),
  31687             .val = val.toIntern(),
  31688         } })).toValue());
  31689     }
  31690 
  31691     try sema.requireRuntimeBlock(block, inst_src, null);
  31692     return block.addTyOp(.wrap_optional, dest_ty, inst);
  31693 }
  31694 
  31695 fn wrapErrorUnionPayload(
  31696     sema: *Sema,
  31697     block: *Block,
  31698     dest_ty: Type,
  31699     inst: Air.Inst.Ref,
  31700     inst_src: LazySrcLoc,
  31701 ) !Air.Inst.Ref {
  31702     const mod = sema.mod;
  31703     const dest_payload_ty = dest_ty.errorUnionPayload(mod);
  31704     const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false });
  31705     if (try sema.resolveMaybeUndefVal(coerced)) |val| {
  31706         return sema.addConstant((try mod.intern(.{ .error_union = .{
  31707             .ty = dest_ty.toIntern(),
  31708             .val = .{ .payload = try val.intern(dest_payload_ty, mod) },
  31709         } })).toValue());
  31710     }
  31711     try sema.requireRuntimeBlock(block, inst_src, null);
  31712     try sema.queueFullTypeResolution(dest_payload_ty);
  31713     return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
  31714 }
  31715 
  31716 fn wrapErrorUnionSet(
  31717     sema: *Sema,
  31718     block: *Block,
  31719     dest_ty: Type,
  31720     inst: Air.Inst.Ref,
  31721     inst_src: LazySrcLoc,
  31722 ) !Air.Inst.Ref {
  31723     const mod = sema.mod;
  31724     const ip = &mod.intern_pool;
  31725     const inst_ty = sema.typeOf(inst);
  31726     const dest_err_set_ty = dest_ty.errorUnionSet(mod);
  31727     if (try sema.resolveMaybeUndefVal(inst)) |val| {
  31728         switch (dest_err_set_ty.toIntern()) {
  31729             .anyerror_type => {},
  31730             else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) {
  31731                 .error_set_type => |error_set_type| ok: {
  31732                     const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name;
  31733                     if (error_set_type.nameIndex(ip, expected_name) != null) break :ok;
  31734                     return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  31735                 },
  31736                 .inferred_error_set_type => |ies_index| ok: {
  31737                     const ies = mod.inferredErrorSetPtr(ies_index);
  31738                     const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name;
  31739 
  31740                     // We carefully do this in an order that avoids unnecessarily
  31741                     // resolving the destination error set type.
  31742                     if (ies.is_anyerror) break :ok;
  31743 
  31744                     if (ies.errors.contains(expected_name)) break :ok;
  31745                     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) break :ok;
  31746 
  31747                     return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  31748                 },
  31749                 else => unreachable,
  31750             },
  31751         }
  31752         return sema.addConstant((try mod.intern(.{ .error_union = .{
  31753             .ty = dest_ty.toIntern(),
  31754             .val = .{
  31755                 .err_name = mod.intern_pool.indexToKey(try val.intern(dest_err_set_ty, mod)).err.name,
  31756             },
  31757         } })).toValue());
  31758     }
  31759 
  31760     try sema.requireRuntimeBlock(block, inst_src, null);
  31761     const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src);
  31762     return block.addTyOp(.wrap_errunion_err, dest_ty, coerced);
  31763 }
  31764 
  31765 fn unionToTag(
  31766     sema: *Sema,
  31767     block: *Block,
  31768     enum_ty: Type,
  31769     un: Air.Inst.Ref,
  31770     un_src: LazySrcLoc,
  31771 ) !Air.Inst.Ref {
  31772     const mod = sema.mod;
  31773     if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| {
  31774         return sema.addConstant(opv);
  31775     }
  31776     if (try sema.resolveMaybeUndefVal(un)) |un_val| {
  31777         return sema.addConstant(un_val.unionTag(mod));
  31778     }
  31779     try sema.requireRuntimeBlock(block, un_src, null);
  31780     return block.addTyOp(.get_union_tag, enum_ty, un);
  31781 }
  31782 
  31783 const PeerResolveStrategy = enum {
  31784     /// The type is not known.
  31785     /// If refined no further, this is equivalent to `exact`.
  31786     unknown,
  31787     /// The type may be an error set or error union.
  31788     /// If refined no further, it is an error set.
  31789     error_set,
  31790     /// The type must be some error union.
  31791     error_union,
  31792     /// The type may be @TypeOf(null), an optional or a C pointer.
  31793     /// If refined no further, it is @TypeOf(null).
  31794     nullable,
  31795     /// The type must be some optional or a C pointer.
  31796     /// If refined no further, it is an optional.
  31797     optional,
  31798     /// The type must be either an array or a vector.
  31799     /// If refined no further, it is an array.
  31800     array,
  31801     /// The type must be a vector.
  31802     vector,
  31803     /// The type must be a C pointer.
  31804     c_ptr,
  31805     /// The type must be a pointer (C or not).
  31806     /// If refined no further, it is a non-C pointer.
  31807     ptr,
  31808     /// The type must be a function or a pointer to a function.
  31809     /// If refined no further, it is a function.
  31810     func,
  31811     /// The type must be an enum literal, or some specific enum or union. Which one is decided
  31812     /// afterwards based on the types in question.
  31813     enum_or_union,
  31814     /// The type must be some integer or float type.
  31815     /// If refined no further, it is `comptime_int`.
  31816     comptime_int,
  31817     /// The type must be some float type.
  31818     /// If refined no further, it is `comptime_float`.
  31819     comptime_float,
  31820     /// The type must be some float or fixed-width integer type.
  31821     /// If refined no further, it is some fixed-width integer type.
  31822     fixed_int,
  31823     /// The type must be some fixed-width float type.
  31824     fixed_float,
  31825     /// The type must be a struct literal or tuple type.
  31826     coercible_struct,
  31827     /// The peers must all be of the same type.
  31828     exact,
  31829 
  31830     /// Given two strategies, find a strategy that satisfies both, if one exists. If no such
  31831     /// strategy exists, any strategy may be returned; an error will be emitted when the caller
  31832     /// attempts to use the strategy to resolve the type.
  31833     /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at
  31834     /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy.
  31835     fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy {
  31836         // Our merging should be order-independent. Thus, even though the union order is arbitrary,
  31837         // by sorting the tags and switching first on the smaller, we have half as many cases to
  31838         // worry about (since we avoid the duplicates).
  31839         const s0_is_a = @intFromEnum(a) <= @intFromEnum(b);
  31840         const s0 = if (s0_is_a) a else b;
  31841         const s1 = if (s0_is_a) b else a;
  31842 
  31843         const ReasonMethod = enum {
  31844             all_s0,
  31845             all_s1,
  31846             either,
  31847         };
  31848 
  31849         const res: struct { ReasonMethod, PeerResolveStrategy } = switch (s0) {
  31850             .unknown => .{ .all_s1, s1 },
  31851             .error_set => switch (s1) {
  31852                 .error_set => .{ .either, .error_set },
  31853                 else => .{ .all_s0, .error_union },
  31854             },
  31855             .error_union => switch (s1) {
  31856                 .error_union => .{ .either, .error_union },
  31857                 else => .{ .all_s0, .error_union },
  31858             },
  31859             .nullable => switch (s1) {
  31860                 .nullable => .{ .either, .nullable },
  31861                 .c_ptr => .{ .all_s1, .c_ptr },
  31862                 else => .{ .all_s0, .optional },
  31863             },
  31864             .optional => switch (s1) {
  31865                 .optional => .{ .either, .optional },
  31866                 .c_ptr => .{ .all_s1, .c_ptr },
  31867                 else => .{ .all_s0, .optional },
  31868             },
  31869             .array => switch (s1) {
  31870                 .array => .{ .either, .array },
  31871                 .vector => .{ .all_s1, .vector },
  31872                 else => .{ .all_s0, .array },
  31873             },
  31874             .vector => switch (s1) {
  31875                 .vector => .{ .either, .vector },
  31876                 else => .{ .all_s0, .vector },
  31877             },
  31878             .c_ptr => switch (s1) {
  31879                 .c_ptr => .{ .either, .c_ptr },
  31880                 else => .{ .all_s0, .c_ptr },
  31881             },
  31882             .ptr => switch (s1) {
  31883                 .ptr => .{ .either, .ptr },
  31884                 else => .{ .all_s0, .ptr },
  31885             },
  31886             .func => switch (s1) {
  31887                 .func => .{ .either, .func },
  31888                 else => .{ .all_s1, s1 }, // doesn't override anything later
  31889             },
  31890             .enum_or_union => switch (s1) {
  31891                 .enum_or_union => .{ .either, .enum_or_union },
  31892                 else => .{ .all_s0, .enum_or_union },
  31893             },
  31894             .comptime_int => switch (s1) {
  31895                 .comptime_int => .{ .either, .comptime_int },
  31896                 else => .{ .all_s1, s1 }, // doesn't override anything later
  31897             },
  31898             .comptime_float => switch (s1) {
  31899                 .comptime_float => .{ .either, .comptime_float },
  31900                 else => .{ .all_s1, s1 }, // doesn't override anything later
  31901             },
  31902             .fixed_int => switch (s1) {
  31903                 .fixed_int => .{ .either, .fixed_int },
  31904                 else => .{ .all_s1, s1 }, // doesn't override anything later
  31905             },
  31906             .fixed_float => switch (s1) {
  31907                 .fixed_float => .{ .either, .fixed_float },
  31908                 else => .{ .all_s1, s1 }, // doesn't override anything later
  31909             },
  31910             .coercible_struct => switch (s1) {
  31911                 .exact => .{ .all_s1, .exact },
  31912                 else => .{ .all_s0, .coercible_struct },
  31913             },
  31914             .exact => .{ .all_s0, .exact },
  31915         };
  31916 
  31917         switch (res[0]) {
  31918             .all_s0 => {
  31919                 if (!s0_is_a) {
  31920                     reason_peer.* = b_peer_idx;
  31921                 }
  31922             },
  31923             .all_s1 => {
  31924                 if (s0_is_a) {
  31925                     reason_peer.* = b_peer_idx;
  31926                 }
  31927             },
  31928             .either => {
  31929                 // Prefer the earliest peer
  31930                 reason_peer.* = @min(reason_peer.*, b_peer_idx);
  31931             },
  31932         }
  31933 
  31934         return res[1];
  31935     }
  31936 
  31937     fn select(ty: Type, mod: *Module) PeerResolveStrategy {
  31938         return switch (ty.zigTypeTag(mod)) {
  31939             .Type, .Void, .Bool, .Opaque, .Frame, .AnyFrame => .exact,
  31940             .NoReturn, .Undefined => .unknown,
  31941             .Null => .nullable,
  31942             .ComptimeInt => .comptime_int,
  31943             .Int => .fixed_int,
  31944             .ComptimeFloat => .comptime_float,
  31945             .Float => .fixed_float,
  31946             .Pointer => if (ty.ptrInfo(mod).flags.size == .C) .c_ptr else .ptr,
  31947             .Array => .array,
  31948             .Vector => .vector,
  31949             .Optional => .optional,
  31950             .ErrorSet => .error_set,
  31951             .ErrorUnion => .error_union,
  31952             .EnumLiteral, .Enum, .Union => .enum_or_union,
  31953             .Struct => if (ty.isTupleOrAnonStruct(mod)) .coercible_struct else .exact,
  31954             .Fn => .func,
  31955         };
  31956     }
  31957 };
  31958 
  31959 const PeerResolveResult = union(enum) {
  31960     /// The peer type resolution was successful, and resulted in the given type.
  31961     success: Type,
  31962     /// There was some generic conflict between two peers.
  31963     conflict: struct {
  31964         peer_idx_a: usize,
  31965         peer_idx_b: usize,
  31966     },
  31967     /// There was an error when resolving the type of a struct or tuple field.
  31968     field_error: struct {
  31969         /// The name of the field which caused the failure.
  31970         field_name: []const u8,
  31971         /// The type of this field in each peer.
  31972         field_types: []Type,
  31973         /// The error from resolving the field type. Guaranteed not to be `success`.
  31974         sub_result: *PeerResolveResult,
  31975     },
  31976 
  31977     fn report(
  31978         result: PeerResolveResult,
  31979         sema: *Sema,
  31980         block: *Block,
  31981         src: LazySrcLoc,
  31982         instructions: []const Air.Inst.Ref,
  31983         candidate_srcs: Module.PeerTypeCandidateSrc,
  31984     ) !*Module.ErrorMsg {
  31985         const mod = sema.mod;
  31986         const decl_ptr = mod.declPtr(block.src_decl);
  31987 
  31988         var opt_msg: ?*Module.ErrorMsg = null;
  31989         errdefer if (opt_msg) |msg| msg.destroy(sema.gpa);
  31990 
  31991         // If we mention fields we'll want to include field types, so put peer types in a buffer
  31992         var peer_tys = try sema.arena.alloc(Type, instructions.len);
  31993         for (peer_tys, instructions) |*ty, inst| {
  31994             ty.* = sema.typeOf(inst);
  31995         }
  31996 
  31997         var cur = result;
  31998         while (true) {
  31999             var conflict_idx: [2]usize = undefined;
  32000 
  32001             switch (cur) {
  32002                 .success => unreachable,
  32003                 .conflict => |conflict| {
  32004                     // Fall through to two-peer conflict handling below
  32005                     conflict_idx = .{
  32006                         conflict.peer_idx_a,
  32007                         conflict.peer_idx_b,
  32008                     };
  32009                 },
  32010                 .field_error => |field_error| {
  32011                     const fmt = "struct field '{s}' has conflicting types";
  32012                     const args = .{field_error.field_name};
  32013                     if (opt_msg) |msg| {
  32014                         try sema.errNote(block, src, msg, fmt, args);
  32015                     } else {
  32016                         opt_msg = try sema.errMsg(block, src, fmt, args);
  32017                     }
  32018 
  32019                     // Continue on to child error
  32020                     cur = field_error.sub_result.*;
  32021                     peer_tys = field_error.field_types;
  32022                     continue;
  32023                 },
  32024             }
  32025 
  32026             // This is the path for reporting a generic conflict between two peers.
  32027 
  32028             if (conflict_idx[1] < conflict_idx[0]) {
  32029                 // b comes first in source, so it's better if it comes first in the error
  32030                 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]);
  32031             }
  32032 
  32033             const conflict_tys: [2]Type = .{
  32034                 peer_tys[conflict_idx[0]],
  32035                 peer_tys[conflict_idx[1]],
  32036             };
  32037             const conflict_srcs: [2]?LazySrcLoc = .{
  32038                 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[0]),
  32039                 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[1]),
  32040             };
  32041 
  32042             const fmt = "incompatible types: '{}' and '{}'";
  32043             const args = .{
  32044                 conflict_tys[0].fmt(mod),
  32045                 conflict_tys[1].fmt(mod),
  32046             };
  32047             const msg = if (opt_msg) |msg| msg: {
  32048                 try sema.errNote(block, src, msg, fmt, args);
  32049                 break :msg msg;
  32050             } else msg: {
  32051                 const msg = try sema.errMsg(block, src, fmt, args);
  32052                 opt_msg = msg;
  32053                 break :msg msg;
  32054             };
  32055 
  32056             if (conflict_srcs[0]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[0].fmt(mod)});
  32057             if (conflict_srcs[1]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[1].fmt(mod)});
  32058 
  32059             // No child error
  32060             break;
  32061         }
  32062 
  32063         return opt_msg.?;
  32064     }
  32065 };
  32066 
  32067 fn resolvePeerTypes(
  32068     sema: *Sema,
  32069     block: *Block,
  32070     src: LazySrcLoc,
  32071     instructions: []const Air.Inst.Ref,
  32072     candidate_srcs: Module.PeerTypeCandidateSrc,
  32073 ) !Type {
  32074     switch (instructions.len) {
  32075         0 => return Type.noreturn,
  32076         1 => return sema.typeOf(instructions[0]),
  32077         else => {},
  32078     }
  32079 
  32080     var peer_tys = try sema.arena.alloc(?Type, instructions.len);
  32081     var peer_vals = try sema.arena.alloc(?Value, instructions.len);
  32082 
  32083     for (instructions, peer_tys, peer_vals) |inst, *ty, *val| {
  32084         ty.* = sema.typeOf(inst);
  32085         val.* = try sema.resolveMaybeUndefVal(inst);
  32086     }
  32087 
  32088     switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) {
  32089         .success => |ty| return ty,
  32090         else => |result| {
  32091             const msg = try result.report(sema, block, src, instructions, candidate_srcs);
  32092             return sema.failWithOwnedErrorMsg(msg);
  32093         },
  32094     }
  32095 }
  32096 
  32097 fn resolvePeerTypesInner(
  32098     sema: *Sema,
  32099     block: *Block,
  32100     src: LazySrcLoc,
  32101     peer_tys: []?Type,
  32102     peer_vals: []?Value,
  32103 ) !PeerResolveResult {
  32104     const mod = sema.mod;
  32105 
  32106     var strat_reason: usize = 0;
  32107     var s: PeerResolveStrategy = .unknown;
  32108     for (peer_tys, 0..) |opt_ty, i| {
  32109         const ty = opt_ty orelse continue;
  32110         s = s.merge(PeerResolveStrategy.select(ty, mod), &strat_reason, i);
  32111     }
  32112 
  32113     if (s == .unknown) {
  32114         // The whole thing was noreturn or undefined - try to do an exact match
  32115         s = .exact;
  32116     } else {
  32117         // There was something other than noreturn and undefined, so we can ignore those peers
  32118         for (peer_tys) |*ty_ptr| {
  32119             const ty = ty_ptr.* orelse continue;
  32120             switch (ty.zigTypeTag(mod)) {
  32121                 .NoReturn, .Undefined => ty_ptr.* = null,
  32122                 else => {},
  32123             }
  32124         }
  32125     }
  32126 
  32127     const target = mod.getTarget();
  32128 
  32129     switch (s) {
  32130         .unknown => unreachable,
  32131 
  32132         .error_set => {
  32133             var final_set: ?Type = null;
  32134             for (peer_tys, 0..) |opt_ty, i| {
  32135                 const ty = opt_ty orelse continue;
  32136                 if (ty.zigTypeTag(mod) != .ErrorSet) return .{ .conflict = .{
  32137                     .peer_idx_a = strat_reason,
  32138                     .peer_idx_b = i,
  32139                 } };
  32140                 if (final_set) |cur_set| {
  32141                     final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty);
  32142                 } else {
  32143                     final_set = ty;
  32144                 }
  32145             }
  32146             return .{ .success = final_set.? };
  32147         },
  32148 
  32149         .error_union => {
  32150             var final_set: ?Type = null;
  32151             for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
  32152                 const ty = ty_ptr.* orelse continue;
  32153                 const set_ty = switch (ty.zigTypeTag(mod)) {
  32154                     .ErrorSet => blk: {
  32155                         ty_ptr.* = null; // no payload to decide on
  32156                         val_ptr.* = null;
  32157                         break :blk ty;
  32158                     },
  32159                     .ErrorUnion => blk: {
  32160                         const set_ty = ty.errorUnionSet(mod);
  32161                         ty_ptr.* = ty.errorUnionPayload(mod);
  32162                         if (val_ptr.*) |eu_val| switch (mod.intern_pool.indexToKey(eu_val.toIntern())) {
  32163                             .error_union => |eu| switch (eu.val) {
  32164                                 .payload => |payload_ip| val_ptr.* = payload_ip.toValue(),
  32165                                 .err_name => val_ptr.* = null,
  32166                             },
  32167                             .undef => val_ptr.* = (try sema.mod.intern(.{ .undef = ty_ptr.*.?.toIntern() })).toValue(),
  32168                             else => unreachable,
  32169                         };
  32170                         break :blk set_ty;
  32171                     },
  32172                     else => continue, // whole type is the payload
  32173                 };
  32174                 if (final_set) |cur_set| {
  32175                     final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty);
  32176                 } else {
  32177                     final_set = set_ty;
  32178                 }
  32179             }
  32180             assert(final_set != null);
  32181             const final_payload = switch (try sema.resolvePeerTypesInner(
  32182                 block,
  32183                 src,
  32184                 peer_tys,
  32185                 peer_vals,
  32186             )) {
  32187                 .success => |ty| ty,
  32188                 else => |result| return result,
  32189             };
  32190             return .{ .success = try mod.errorUnionType(final_set.?, final_payload) };
  32191         },
  32192 
  32193         .nullable => {
  32194             for (peer_tys, 0..) |opt_ty, i| {
  32195                 const ty = opt_ty orelse continue;
  32196                 if (!ty.eql(Type.null, mod)) return .{ .conflict = .{
  32197                     .peer_idx_a = strat_reason,
  32198                     .peer_idx_b = i,
  32199                 } };
  32200             }
  32201             return .{ .success = Type.null };
  32202         },
  32203 
  32204         .optional => {
  32205             for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
  32206                 const ty = ty_ptr.* orelse continue;
  32207                 switch (ty.zigTypeTag(mod)) {
  32208                     .Null => {
  32209                         ty_ptr.* = null;
  32210                         val_ptr.* = null;
  32211                     },
  32212                     .Optional => {
  32213                         ty_ptr.* = ty.optionalChild(mod);
  32214                         if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(mod)) opt_val.optionalValue(mod) else null;
  32215                     },
  32216                     else => {},
  32217                 }
  32218             }
  32219             const child_ty = switch (try sema.resolvePeerTypesInner(
  32220                 block,
  32221                 src,
  32222                 peer_tys,
  32223                 peer_vals,
  32224             )) {
  32225                 .success => |ty| ty,
  32226                 else => |result| return result,
  32227             };
  32228             return .{ .success = try mod.optionalType(child_ty.toIntern()) };
  32229         },
  32230 
  32231         .array => {
  32232             // Index of the first non-null peer
  32233             var opt_first_idx: ?usize = null;
  32234             // Index of the first array or vector peer (i.e. not a tuple)
  32235             var opt_first_arr_idx: ?usize = null;
  32236             // Set to non-null once we see any peer, even a tuple
  32237             var len: u64 = undefined;
  32238             var sentinel: ?Value = undefined;
  32239             // Only set once we see a non-tuple peer
  32240             var elem_ty: Type = undefined;
  32241 
  32242             for (peer_tys, 0..) |*ty_ptr, i| {
  32243                 const ty = ty_ptr.* orelse continue;
  32244 
  32245                 if (!ty.isArrayOrVector(mod)) {
  32246                     // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced.
  32247                     const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
  32248                         .peer_idx_a = strat_reason,
  32249                         .peer_idx_b = i,
  32250                     } };
  32251 
  32252                     if (opt_first_idx) |first_idx| {
  32253                         if (arr_like.len != len) return .{ .conflict = .{
  32254                             .peer_idx_a = first_idx,
  32255                             .peer_idx_b = i,
  32256                         } };
  32257                     } else {
  32258                         opt_first_idx = i;
  32259                         len = arr_like.len;
  32260                     }
  32261 
  32262                     sentinel = null;
  32263 
  32264                     continue;
  32265                 }
  32266 
  32267                 const first_arr_idx = opt_first_arr_idx orelse {
  32268                     if (opt_first_idx == null) {
  32269                         opt_first_idx = i;
  32270                         len = ty.arrayLen(mod);
  32271                         sentinel = ty.sentinel(mod);
  32272                     }
  32273                     opt_first_arr_idx = i;
  32274                     elem_ty = ty.childType(mod);
  32275                     continue;
  32276                 };
  32277 
  32278                 if (ty.arrayLen(mod) != len) return .{ .conflict = .{
  32279                     .peer_idx_a = first_arr_idx,
  32280                     .peer_idx_b = i,
  32281                 } };
  32282 
  32283                 if (!ty.childType(mod).eql(elem_ty, mod)) {
  32284                     return .{ .conflict = .{
  32285                         .peer_idx_a = first_arr_idx,
  32286                         .peer_idx_b = i,
  32287                     } };
  32288                 }
  32289 
  32290                 if (sentinel) |cur_sent| {
  32291                     if (ty.sentinel(mod)) |peer_sent| {
  32292                         if (!peer_sent.eql(cur_sent, elem_ty, mod)) sentinel = null;
  32293                     } else {
  32294                         sentinel = null;
  32295                     }
  32296                 }
  32297             }
  32298 
  32299             // There should always be at least one array or vector peer
  32300             assert(opt_first_arr_idx != null);
  32301 
  32302             return .{ .success = try mod.arrayType(.{
  32303                 .len = len,
  32304                 .child = elem_ty.toIntern(),
  32305                 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none,
  32306             }) };
  32307         },
  32308 
  32309         .vector => {
  32310             var len: ?u64 = null;
  32311             var first_idx: usize = undefined;
  32312             for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| {
  32313                 const ty = ty_ptr.* orelse continue;
  32314 
  32315                 if (!ty.isArrayOrVector(mod)) {
  32316                     // Allow tuples of the correct length
  32317                     const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
  32318                         .peer_idx_a = strat_reason,
  32319                         .peer_idx_b = i,
  32320                     } };
  32321 
  32322                     if (len) |expect_len| {
  32323                         if (arr_like.len != expect_len) return .{ .conflict = .{
  32324                             .peer_idx_a = first_idx,
  32325                             .peer_idx_b = i,
  32326                         } };
  32327                     } else {
  32328                         len = arr_like.len;
  32329                         first_idx = i;
  32330                     }
  32331 
  32332                     // Tuples won't participate in the child type resolution. We'll resolve without
  32333                     // them, and if the tuples have a bad type, we'll get a coercion error later.
  32334                     ty_ptr.* = null;
  32335                     val_ptr.* = null;
  32336 
  32337                     continue;
  32338                 }
  32339 
  32340                 if (len) |expect_len| {
  32341                     if (ty.arrayLen(mod) != expect_len) return .{ .conflict = .{
  32342                         .peer_idx_a = first_idx,
  32343                         .peer_idx_b = i,
  32344                     } };
  32345                 } else {
  32346                     len = ty.arrayLen(mod);
  32347                     first_idx = i;
  32348                 }
  32349 
  32350                 ty_ptr.* = ty.childType(mod);
  32351                 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR
  32352             }
  32353 
  32354             const child_ty = switch (try sema.resolvePeerTypesInner(
  32355                 block,
  32356                 src,
  32357                 peer_tys,
  32358                 peer_vals,
  32359             )) {
  32360                 .success => |ty| ty,
  32361                 else => |result| return result,
  32362             };
  32363 
  32364             return .{ .success = try mod.vectorType(.{
  32365                 .len = @as(u32, @intCast(len.?)),
  32366                 .child = child_ty.toIntern(),
  32367             }) };
  32368         },
  32369 
  32370         .c_ptr => {
  32371             var opt_ptr_info: ?InternPool.Key.PtrType = null;
  32372             var first_idx: usize = undefined;
  32373             for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
  32374                 const ty = opt_ty orelse continue;
  32375                 switch (ty.zigTypeTag(mod)) {
  32376                     .ComptimeInt => continue, // comptime-known integers can always coerce to C pointers
  32377                     .Int => {
  32378                         if (opt_val != null) {
  32379                             // Always allow the coercion for comptime-known ints
  32380                             continue;
  32381                         } else {
  32382                             // Runtime-known, so check if the type is no bigger than a usize
  32383                             const ptr_bits = target.ptrBitWidth();
  32384                             const bits = ty.intInfo(mod).bits;
  32385                             if (bits <= ptr_bits) continue;
  32386                         }
  32387                     },
  32388                     .Null => continue,
  32389                     else => {},
  32390                 }
  32391 
  32392                 if (!ty.isPtrAtRuntime(mod)) return .{ .conflict = .{
  32393                     .peer_idx_a = strat_reason,
  32394                     .peer_idx_b = i,
  32395                 } };
  32396 
  32397                 // Goes through optionals
  32398                 const peer_info = ty.ptrInfo(mod);
  32399 
  32400                 var ptr_info = opt_ptr_info orelse {
  32401                     opt_ptr_info = peer_info;
  32402                     opt_ptr_info.?.flags.size = .C;
  32403                     first_idx = i;
  32404                     continue;
  32405                 };
  32406 
  32407                 // Try peer -> cur, then cur -> peer
  32408                 ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) orelse {
  32409                     return .{ .conflict = .{
  32410                         .peer_idx_a = first_idx,
  32411                         .peer_idx_b = i,
  32412                     } };
  32413                 }).toIntern();
  32414 
  32415                 if (ptr_info.sentinel != .none and peer_info.sentinel != .none) {
  32416                     const peer_sent = try mod.intern_pool.getCoerced(sema.gpa, ptr_info.sentinel, ptr_info.child);
  32417                     const ptr_sent = try mod.intern_pool.getCoerced(sema.gpa, peer_info.sentinel, ptr_info.child);
  32418                     if (ptr_sent == peer_sent) {
  32419                         ptr_info.sentinel = ptr_sent;
  32420                     } else {
  32421                         ptr_info.sentinel = .none;
  32422                     }
  32423                 } else {
  32424                     ptr_info.sentinel = .none;
  32425                 }
  32426 
  32427                 // Note that the align can be always non-zero; Module.ptrType will canonicalize it
  32428                 ptr_info.flags.alignment = Alignment.fromByteUnits(@min(
  32429                     ptr_info.flags.alignment.toByteUnitsOptional() orelse
  32430                         ptr_info.child.toType().abiAlignment(mod),
  32431                     peer_info.flags.alignment.toByteUnitsOptional() orelse
  32432                         peer_info.child.toType().abiAlignment(mod),
  32433                 ));
  32434                 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
  32435                     return .{ .conflict = .{
  32436                         .peer_idx_a = first_idx,
  32437                         .peer_idx_b = i,
  32438                     } };
  32439                 }
  32440 
  32441                 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
  32442                     ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
  32443                 {
  32444                     return .{ .conflict = .{
  32445                         .peer_idx_a = first_idx,
  32446                         .peer_idx_b = i,
  32447                     } };
  32448                 }
  32449 
  32450                 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
  32451                 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
  32452 
  32453                 opt_ptr_info = ptr_info;
  32454             }
  32455             return .{ .success = try mod.ptrType(opt_ptr_info.?) };
  32456         },
  32457 
  32458         .ptr => {
  32459             // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only
  32460             // if there were no actual slices. Else, we want the slice index to report a conflict.
  32461             var opt_slice_idx: ?usize = null;
  32462 
  32463             var opt_ptr_info: ?InternPool.Key.PtrType = null;
  32464             var first_idx: usize = undefined;
  32465             var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error
  32466 
  32467             for (peer_tys, 0..) |opt_ty, i| {
  32468                 const ty = opt_ty orelse continue;
  32469                 const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(mod)) {
  32470                     .Pointer => ty.ptrInfo(mod),
  32471                     .Fn => .{
  32472                         .child = ty.toIntern(),
  32473                         .flags = .{
  32474                             .address_space = target_util.defaultAddressSpace(target, .global_constant),
  32475                         },
  32476                     },
  32477                     else => return .{ .conflict = .{
  32478                         .peer_idx_a = strat_reason,
  32479                         .peer_idx_b = i,
  32480                     } },
  32481                 };
  32482 
  32483                 switch (peer_info.flags.size) {
  32484                     .One, .Many => {},
  32485                     .Slice => opt_slice_idx = i,
  32486                     .C => return .{ .conflict = .{
  32487                         .peer_idx_a = strat_reason,
  32488                         .peer_idx_b = i,
  32489                     } },
  32490                 }
  32491 
  32492                 var ptr_info = opt_ptr_info orelse {
  32493                     opt_ptr_info = peer_info;
  32494                     first_idx = i;
  32495                     continue;
  32496                 };
  32497 
  32498                 other_idx = i;
  32499 
  32500                 // We want to return this in a lot of cases, so alias it here for convenience
  32501                 const generic_err: PeerResolveResult = .{ .conflict = .{
  32502                     .peer_idx_a = first_idx,
  32503                     .peer_idx_b = i,
  32504                 } };
  32505 
  32506                 // Note that the align can be always non-zero; Type.ptr will canonicalize it
  32507                 ptr_info.flags.alignment = Alignment.fromByteUnits(@min(
  32508                     ptr_info.flags.alignment.toByteUnitsOptional() orelse
  32509                         ptr_info.child.toType().abiAlignment(mod),
  32510                     peer_info.flags.alignment.toByteUnitsOptional() orelse
  32511                         peer_info.child.toType().abiAlignment(mod),
  32512                 ));
  32513 
  32514                 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
  32515                     return generic_err;
  32516                 }
  32517 
  32518                 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
  32519                     ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
  32520                 {
  32521                     return generic_err;
  32522                 }
  32523 
  32524                 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
  32525                 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
  32526 
  32527                 const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) {
  32528                     .One => switch (mod.intern_pool.indexToKey(peer_info.child)) {
  32529                         .array_type => |array_type| array_type.sentinel,
  32530                         else => .none,
  32531                     },
  32532                     .Many, .Slice => peer_info.sentinel,
  32533                     .C => unreachable,
  32534                 };
  32535 
  32536                 const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) {
  32537                     .One => switch (mod.intern_pool.indexToKey(ptr_info.child)) {
  32538                         .array_type => |array_type| array_type.sentinel,
  32539                         else => .none,
  32540                     },
  32541                     .Many, .Slice => ptr_info.sentinel,
  32542                     .C => unreachable,
  32543                 };
  32544 
  32545                 // We abstract array handling slightly so that tuple pointers can work like array pointers
  32546                 const peer_pointee_array = sema.typeIsArrayLike(peer_info.child.toType());
  32547                 const cur_pointee_array = sema.typeIsArrayLike(ptr_info.child.toType());
  32548 
  32549                 // This switch is just responsible for deciding the size and pointee (not including
  32550                 // single-pointer array sentinel).
  32551                 good: {
  32552                     switch (peer_info.flags.size) {
  32553                         .One => switch (ptr_info.flags.size) {
  32554                             .One => {
  32555                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| {
  32556                                     ptr_info.child = pointee.toIntern();
  32557                                     break :good;
  32558                                 }
  32559 
  32560                                 const cur_arr = cur_pointee_array orelse return generic_err;
  32561                                 const peer_arr = peer_pointee_array orelse return generic_err;
  32562 
  32563                                 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| {
  32564                                     // *[n:x]T + *[n:y]T = *[n]T
  32565                                     if (cur_arr.len == peer_arr.len) {
  32566                                         ptr_info.child = (try mod.arrayType(.{
  32567                                             .len = cur_arr.len,
  32568                                             .child = elem_ty.toIntern(),
  32569                                         })).toIntern();
  32570                                         break :good;
  32571                                     }
  32572                                     // *[a]T + *[b]T = []T
  32573                                     ptr_info.flags.size = .Slice;
  32574                                     ptr_info.child = elem_ty.toIntern();
  32575                                     break :good;
  32576                                 }
  32577 
  32578                                 if (peer_arr.elem_ty.toIntern() == .noreturn_type) {
  32579                                     // *struct{} + *[a]T = []T
  32580                                     ptr_info.flags.size = .Slice;
  32581                                     ptr_info.child = cur_arr.elem_ty.toIntern();
  32582                                     break :good;
  32583                                 }
  32584 
  32585                                 if (cur_arr.elem_ty.toIntern() == .noreturn_type) {
  32586                                     // *[a]T + *struct{} = []T
  32587                                     ptr_info.flags.size = .Slice;
  32588                                     ptr_info.child = peer_arr.elem_ty.toIntern();
  32589                                     break :good;
  32590                                 }
  32591 
  32592                                 return generic_err;
  32593                             },
  32594                             .Many => {
  32595                                 // Only works for *[n]T + [*]T -> [*]T
  32596                                 const arr = peer_pointee_array orelse return generic_err;
  32597                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), arr.elem_ty)) |pointee| {
  32598                                     ptr_info.child = pointee.toIntern();
  32599                                     break :good;
  32600                                 }
  32601                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  32602                                     // *struct{} + [*]T -> [*]T
  32603                                     break :good;
  32604                                 }
  32605                                 return generic_err;
  32606                             },
  32607                             .Slice => {
  32608                                 // Only works for *[n]T + []T -> []T
  32609                                 const arr = peer_pointee_array orelse return generic_err;
  32610                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), arr.elem_ty)) |pointee| {
  32611                                     ptr_info.child = pointee.toIntern();
  32612                                     break :good;
  32613                                 }
  32614                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  32615                                     // *struct{} + []T -> []T
  32616                                     break :good;
  32617                                 }
  32618                                 return generic_err;
  32619                             },
  32620                             .C => unreachable,
  32621                         },
  32622                         .Many => switch (ptr_info.flags.size) {
  32623                             .One => {
  32624                                 // Only works for [*]T + *[n]T -> [*]T
  32625                                 const arr = cur_pointee_array orelse return generic_err;
  32626                                 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.child.toType())) |pointee| {
  32627                                     ptr_info.flags.size = .Many;
  32628                                     ptr_info.child = pointee.toIntern();
  32629                                     break :good;
  32630                                 }
  32631                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  32632                                     // [*]T + *struct{} -> [*]T
  32633                                     ptr_info.flags.size = .Many;
  32634                                     ptr_info.child = peer_info.child;
  32635                                     break :good;
  32636                                 }
  32637                                 return generic_err;
  32638                             },
  32639                             .Many => {
  32640                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| {
  32641                                     ptr_info.child = pointee.toIntern();
  32642                                     break :good;
  32643                                 }
  32644                                 return generic_err;
  32645                             },
  32646                             .Slice => {
  32647                                 // Only works if no peers are actually slices
  32648                                 if (opt_slice_idx) |slice_idx| {
  32649                                     return .{ .conflict = .{
  32650                                         .peer_idx_a = slice_idx,
  32651                                         .peer_idx_b = i,
  32652                                     } };
  32653                                 }
  32654                                 // Okay, then works for [*]T + "[]T" -> [*]T
  32655                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| {
  32656                                     ptr_info.flags.size = .Many;
  32657                                     ptr_info.child = pointee.toIntern();
  32658                                     break :good;
  32659                                 }
  32660                                 return generic_err;
  32661                             },
  32662                             .C => unreachable,
  32663                         },
  32664                         .Slice => switch (ptr_info.flags.size) {
  32665                             .One => {
  32666                                 // Only works for []T + *[n]T -> []T
  32667                                 const arr = cur_pointee_array orelse return generic_err;
  32668                                 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.child.toType())) |pointee| {
  32669                                     ptr_info.flags.size = .Slice;
  32670                                     ptr_info.child = pointee.toIntern();
  32671                                     break :good;
  32672                                 }
  32673                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  32674                                     // []T + *struct{} -> []T
  32675                                     ptr_info.flags.size = .Slice;
  32676                                     ptr_info.child = peer_info.child;
  32677                                     break :good;
  32678                                 }
  32679                                 return generic_err;
  32680                             },
  32681                             .Many => {
  32682                                 // Impossible! (current peer is an actual slice)
  32683                                 return generic_err;
  32684                             },
  32685                             .Slice => {
  32686                                 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| {
  32687                                     ptr_info.child = pointee.toIntern();
  32688                                     break :good;
  32689                                 }
  32690                                 return generic_err;
  32691                             },
  32692                             .C => unreachable,
  32693                         },
  32694                         .C => unreachable,
  32695                     }
  32696                 }
  32697 
  32698                 const sentinel_ty = switch (ptr_info.flags.size) {
  32699                     .One => switch (mod.intern_pool.indexToKey(ptr_info.child)) {
  32700                         .array_type => |array_type| array_type.child,
  32701                         else => ptr_info.child,
  32702                     },
  32703                     .Many, .Slice, .C => ptr_info.child,
  32704                 };
  32705 
  32706                 sentinel: {
  32707                     no_sentinel: {
  32708                         if (peer_sentinel == .none) break :no_sentinel;
  32709                         if (cur_sentinel == .none) break :no_sentinel;
  32710                         const peer_sent_coerced = try mod.intern_pool.getCoerced(sema.gpa, peer_sentinel, sentinel_ty);
  32711                         const cur_sent_coerced = try mod.intern_pool.getCoerced(sema.gpa, cur_sentinel, sentinel_ty);
  32712                         if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel;
  32713                         // Sentinels match
  32714                         if (ptr_info.flags.size == .One) switch (mod.intern_pool.indexToKey(ptr_info.child)) {
  32715                             .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{
  32716                                 .len = array_type.len,
  32717                                 .child = array_type.child,
  32718                                 .sentinel = cur_sent_coerced,
  32719                             })).toIntern(),
  32720                             else => unreachable,
  32721                         } else {
  32722                             ptr_info.sentinel = cur_sent_coerced;
  32723                         }
  32724                         break :sentinel;
  32725                     }
  32726                     // Clear existing sentinel
  32727                     ptr_info.sentinel = .none;
  32728                     switch (mod.intern_pool.indexToKey(ptr_info.child)) {
  32729                         .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{
  32730                             .len = array_type.len,
  32731                             .child = array_type.child,
  32732                             .sentinel = .none,
  32733                         })).toIntern(),
  32734                         else => {},
  32735                     }
  32736                 }
  32737 
  32738                 opt_ptr_info = ptr_info;
  32739             }
  32740 
  32741             // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance)
  32742             // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to
  32743             // coerce the empty struct to a specific type, but no peer provided one. We need to
  32744             // detect this case and emit an error.
  32745             const pointee = opt_ptr_info.?.child;
  32746             switch (pointee) {
  32747                 .noreturn_type => return .{ .conflict = .{
  32748                     .peer_idx_a = first_idx,
  32749                     .peer_idx_b = other_idx,
  32750                 } },
  32751                 else => switch (mod.intern_pool.indexToKey(pointee)) {
  32752                     .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{
  32753                         .peer_idx_a = first_idx,
  32754                         .peer_idx_b = other_idx,
  32755                     } },
  32756                     else => {},
  32757                 },
  32758             }
  32759 
  32760             return .{ .success = try mod.ptrType(opt_ptr_info.?) };
  32761         },
  32762 
  32763         .func => {
  32764             var opt_cur_ty: ?Type = null;
  32765             var first_idx: usize = undefined;
  32766             for (peer_tys, 0..) |opt_ty, i| {
  32767                 const ty = opt_ty orelse continue;
  32768                 const cur_ty = opt_cur_ty orelse {
  32769                     opt_cur_ty = ty;
  32770                     first_idx = i;
  32771                     continue;
  32772                 };
  32773                 if (ty.zigTypeTag(mod) != .Fn) return .{ .conflict = .{
  32774                     .peer_idx_a = strat_reason,
  32775                     .peer_idx_b = i,
  32776                 } };
  32777                 // ty -> cur_ty
  32778                 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, target, src, src)) {
  32779                     continue;
  32780                 }
  32781                 // cur_ty -> ty
  32782                 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, target, src, src)) {
  32783                     opt_cur_ty = ty;
  32784                     continue;
  32785                 }
  32786                 return .{ .conflict = .{
  32787                     .peer_idx_a = first_idx,
  32788                     .peer_idx_b = i,
  32789                 } };
  32790             }
  32791             return .{ .success = opt_cur_ty.? };
  32792         },
  32793 
  32794         .enum_or_union => {
  32795             var opt_cur_ty: ?Type = null;
  32796             // The peer index which gave the current type
  32797             var cur_ty_idx: usize = undefined;
  32798 
  32799             for (peer_tys, 0..) |opt_ty, i| {
  32800                 const ty = opt_ty orelse continue;
  32801                 switch (ty.zigTypeTag(mod)) {
  32802                     .EnumLiteral, .Enum, .Union => {},
  32803                     else => return .{ .conflict = .{
  32804                         .peer_idx_a = strat_reason,
  32805                         .peer_idx_b = i,
  32806                     } },
  32807                 }
  32808                 const cur_ty = opt_cur_ty orelse {
  32809                     opt_cur_ty = ty;
  32810                     cur_ty_idx = i;
  32811                     continue;
  32812                 };
  32813 
  32814                 // We want to return this in a lot of cases, so alias it here for convenience
  32815                 const generic_err: PeerResolveResult = .{ .conflict = .{
  32816                     .peer_idx_a = cur_ty_idx,
  32817                     .peer_idx_b = i,
  32818                 } };
  32819 
  32820                 switch (cur_ty.zigTypeTag(mod)) {
  32821                     .EnumLiteral => {
  32822                         opt_cur_ty = ty;
  32823                         cur_ty_idx = i;
  32824                     },
  32825                     .Enum => switch (ty.zigTypeTag(mod)) {
  32826                         .EnumLiteral => {},
  32827                         .Enum => {
  32828                             if (!ty.eql(cur_ty, mod)) return generic_err;
  32829                         },
  32830                         .Union => {
  32831                             const tag_ty = ty.unionTagTypeHypothetical(mod);
  32832                             if (!tag_ty.eql(cur_ty, mod)) return generic_err;
  32833                             opt_cur_ty = ty;
  32834                             cur_ty_idx = i;
  32835                         },
  32836                         else => unreachable,
  32837                     },
  32838                     .Union => switch (ty.zigTypeTag(mod)) {
  32839                         .EnumLiteral => {},
  32840                         .Enum => {
  32841                             const cur_tag_ty = cur_ty.unionTagTypeHypothetical(mod);
  32842                             if (!ty.eql(cur_tag_ty, mod)) return generic_err;
  32843                         },
  32844                         .Union => {
  32845                             if (!ty.eql(cur_ty, mod)) return generic_err;
  32846                         },
  32847                         else => unreachable,
  32848                     },
  32849                     else => unreachable,
  32850                 }
  32851             }
  32852             return .{ .success = opt_cur_ty.? };
  32853         },
  32854 
  32855         .comptime_int => {
  32856             for (peer_tys, 0..) |opt_ty, i| {
  32857                 const ty = opt_ty orelse continue;
  32858                 switch (ty.zigTypeTag(mod)) {
  32859                     .ComptimeInt => {},
  32860                     else => return .{ .conflict = .{
  32861                         .peer_idx_a = strat_reason,
  32862                         .peer_idx_b = i,
  32863                     } },
  32864                 }
  32865             }
  32866             return .{ .success = Type.comptime_int };
  32867         },
  32868 
  32869         .comptime_float => {
  32870             for (peer_tys, 0..) |opt_ty, i| {
  32871                 const ty = opt_ty orelse continue;
  32872                 switch (ty.zigTypeTag(mod)) {
  32873                     .ComptimeInt, .ComptimeFloat => {},
  32874                     else => return .{ .conflict = .{
  32875                         .peer_idx_a = strat_reason,
  32876                         .peer_idx_b = i,
  32877                     } },
  32878                 }
  32879             }
  32880             return .{ .success = Type.comptime_float };
  32881         },
  32882 
  32883         .fixed_int => {
  32884             var idx_unsigned: ?usize = null;
  32885             var idx_signed: ?usize = null;
  32886 
  32887             // TODO: this is for compatibility with legacy behavior. See beneath the loop.
  32888             var any_comptime_known = false;
  32889 
  32890             for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| {
  32891                 const ty = opt_ty orelse continue;
  32892                 const opt_val = ptr_opt_val.*;
  32893 
  32894                 const peer_tag = ty.zigTypeTag(mod);
  32895                 switch (peer_tag) {
  32896                     .ComptimeInt => {
  32897                         // If the value is undefined, we can't refine to a fixed-width int
  32898                         if (opt_val == null or opt_val.?.isUndef(mod)) return .{ .conflict = .{
  32899                             .peer_idx_a = strat_reason,
  32900                             .peer_idx_b = i,
  32901                         } };
  32902                         any_comptime_known = true;
  32903                         ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?);
  32904                         continue;
  32905                     },
  32906                     .Int => {},
  32907                     else => return .{ .conflict = .{
  32908                         .peer_idx_a = strat_reason,
  32909                         .peer_idx_b = i,
  32910                     } },
  32911                 }
  32912 
  32913                 if (opt_val != null) any_comptime_known = true;
  32914 
  32915                 const info = ty.intInfo(mod);
  32916 
  32917                 const idx_ptr = switch (info.signedness) {
  32918                     .unsigned => &idx_unsigned,
  32919                     .signed => &idx_signed,
  32920                 };
  32921 
  32922                 const largest_idx = idx_ptr.* orelse {
  32923                     idx_ptr.* = i;
  32924                     continue;
  32925                 };
  32926 
  32927                 const cur_info = peer_tys[largest_idx].?.intInfo(mod);
  32928                 if (info.bits > cur_info.bits) {
  32929                     idx_ptr.* = i;
  32930                 }
  32931             }
  32932 
  32933             if (idx_signed == null) {
  32934                 return .{ .success = peer_tys[idx_unsigned.?].? };
  32935             }
  32936 
  32937             if (idx_unsigned == null) {
  32938                 return .{ .success = peer_tys[idx_signed.?].? };
  32939             }
  32940 
  32941             const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(mod);
  32942             const signed_info = peer_tys[idx_signed.?].?.intInfo(mod);
  32943             if (signed_info.bits > unsigned_info.bits) {
  32944                 return .{ .success = peer_tys[idx_signed.?].? };
  32945             }
  32946 
  32947             // TODO: this is for compatibility with legacy behavior. Before this version of PTR was
  32948             // implemented, the algorithm very often returned false positives, with the expectation
  32949             // that you'd just hit a coercion error later. One of these was that for integers, the
  32950             // largest type would always be returned, even if it couldn't fit everything. This had
  32951             // an unintentional consequence to semantics, which is that if values were known at
  32952             // comptime, they would be coerced down to the smallest type where possible. This
  32953             // behavior is unintuitive and order-dependent, so in my opinion should be eliminated,
  32954             // but for now we'll retain compatibility.
  32955             if (any_comptime_known) {
  32956                 if (unsigned_info.bits > signed_info.bits) {
  32957                     return .{ .success = peer_tys[idx_unsigned.?].? };
  32958                 }
  32959                 const idx = @min(idx_unsigned.?, idx_signed.?);
  32960                 return .{ .success = peer_tys[idx].? };
  32961             }
  32962 
  32963             return .{ .conflict = .{
  32964                 .peer_idx_a = idx_unsigned.?,
  32965                 .peer_idx_b = idx_signed.?,
  32966             } };
  32967         },
  32968 
  32969         .fixed_float => {
  32970             var opt_cur_ty: ?Type = null;
  32971 
  32972             for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
  32973                 const ty = opt_ty orelse continue;
  32974                 switch (ty.zigTypeTag(mod)) {
  32975                     .ComptimeFloat, .ComptimeInt => {},
  32976                     .Int => {
  32977                         if (opt_val == null) return .{ .conflict = .{
  32978                             .peer_idx_a = strat_reason,
  32979                             .peer_idx_b = i,
  32980                         } };
  32981                     },
  32982                     .Float => {
  32983                         if (opt_cur_ty) |cur_ty| {
  32984                             if (cur_ty.eql(ty, mod)) continue;
  32985                             // Recreate the type so we eliminate any c_longdouble
  32986                             const bits = @max(cur_ty.floatBits(target), ty.floatBits(target));
  32987                             opt_cur_ty = switch (bits) {
  32988                                 16 => Type.f16,
  32989                                 32 => Type.f32,
  32990                                 64 => Type.f64,
  32991                                 80 => Type.f80,
  32992                                 128 => Type.f128,
  32993                                 else => unreachable,
  32994                             };
  32995                         } else {
  32996                             opt_cur_ty = ty;
  32997                         }
  32998                     },
  32999                     else => return .{ .conflict = .{
  33000                         .peer_idx_a = strat_reason,
  33001                         .peer_idx_b = i,
  33002                     } },
  33003                 }
  33004             }
  33005 
  33006             // Note that fixed_float is only chosen if there is at least one fixed-width float peer,
  33007             // so opt_cur_ty must be non-null.
  33008             return .{ .success = opt_cur_ty.? };
  33009         },
  33010 
  33011         .coercible_struct => {
  33012             // First, check that every peer has the same approximate structure (field count and names)
  33013 
  33014             var opt_first_idx: ?usize = null;
  33015             var is_tuple: bool = undefined;
  33016             var field_count: usize = undefined;
  33017             // Only defined for non-tuples.
  33018             var field_names: []InternPool.NullTerminatedString = undefined;
  33019 
  33020             for (peer_tys, 0..) |opt_ty, i| {
  33021                 const ty = opt_ty orelse continue;
  33022 
  33023                 if (!ty.isTupleOrAnonStruct(mod)) {
  33024                     return .{ .conflict = .{
  33025                         .peer_idx_a = strat_reason,
  33026                         .peer_idx_b = i,
  33027                     } };
  33028                 }
  33029 
  33030                 const first_idx = opt_first_idx orelse {
  33031                     opt_first_idx = i;
  33032                     is_tuple = ty.isTuple(mod);
  33033                     field_count = ty.structFieldCount(mod);
  33034                     if (!is_tuple) {
  33035                         const names = mod.intern_pool.indexToKey(ty.toIntern()).anon_struct_type.names;
  33036                         field_names = try sema.arena.dupe(InternPool.NullTerminatedString, names);
  33037                     }
  33038                     continue;
  33039                 };
  33040 
  33041                 if (ty.isTuple(mod) != is_tuple or ty.structFieldCount(mod) != field_count) {
  33042                     return .{ .conflict = .{
  33043                         .peer_idx_a = first_idx,
  33044                         .peer_idx_b = i,
  33045                     } };
  33046                 }
  33047 
  33048                 if (!is_tuple) {
  33049                     for (field_names, 0..) |expected, field_idx| {
  33050                         const actual = ty.structFieldName(field_idx, mod);
  33051                         if (actual == expected) continue;
  33052                         return .{ .conflict = .{
  33053                             .peer_idx_a = first_idx,
  33054                             .peer_idx_b = i,
  33055                         } };
  33056                     }
  33057                 }
  33058             }
  33059 
  33060             assert(opt_first_idx != null);
  33061 
  33062             // Now, we'll recursively resolve the field types
  33063             const field_types = try sema.arena.alloc(InternPool.Index, field_count);
  33064             // Values for `comptime` fields - `.none` used for non-comptime fields
  33065             const field_vals = try sema.arena.alloc(InternPool.Index, field_count);
  33066             const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len);
  33067             const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len);
  33068 
  33069             for (field_types, field_vals, 0..) |*field_ty, *field_val, field_idx| {
  33070                 // Fill buffers with types and values of the field
  33071                 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| {
  33072                     const ty = opt_ty orelse {
  33073                         peer_field_ty.* = null;
  33074                         peer_field_val.* = null;
  33075                         continue;
  33076                     };
  33077                     peer_field_ty.* = ty.structFieldType(field_idx, mod);
  33078                     peer_field_val.* = if (opt_val) |val| try val.fieldValue(mod, field_idx) else null;
  33079                 }
  33080 
  33081                 // Resolve field type recursively
  33082                 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) {
  33083                     .success => |ty| ty.toIntern(),
  33084                     else => |result| {
  33085                         const result_buf = try sema.arena.create(PeerResolveResult);
  33086                         result_buf.* = result;
  33087                         const field_name = if (is_tuple) name: {
  33088                             break :name try std.fmt.allocPrint(sema.arena, "{d}", .{field_idx});
  33089                         } else try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(field_names[field_idx]));
  33090 
  33091                         // The error info needs the field types, but we can't reuse sub_peer_tys
  33092                         // since the recursive call may have clobbered it.
  33093                         const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len);
  33094                         for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| {
  33095                             // Already-resolved types won't be referenced by the error so it's fine
  33096                             // to leave them undefined.
  33097                             const ty = opt_ty orelse continue;
  33098                             peer_field_ty.* = ty.structFieldType(field_idx, mod);
  33099                         }
  33100 
  33101                         return .{ .field_error = .{
  33102                             .field_name = field_name,
  33103                             .field_types = peer_field_tys,
  33104                             .sub_result = result_buf,
  33105                         } };
  33106                     },
  33107                 };
  33108 
  33109                 // Decide if this is a comptime field. If it is comptime in all peers, and the
  33110                 // coerced comptime values are all the same, we say it is comptime, else not.
  33111 
  33112                 var comptime_val: ?Value = null;
  33113                 for (peer_tys) |opt_ty| {
  33114                     const struct_ty = opt_ty orelse continue;
  33115                     const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse {
  33116                         comptime_val = null;
  33117                         break;
  33118                     };
  33119                     const uncoerced_field = try sema.addConstant(uncoerced_field_val);
  33120                     const coerced_inst = sema.coerceExtra(block, field_ty.toType(), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) {
  33121                         // 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
  33122                         error.NotCoercible => {
  33123                             comptime_val = null;
  33124                             break;
  33125                         },
  33126                         else => |e| return e,
  33127                     };
  33128                     const coerced_val = (try sema.resolveMaybeUndefVal(coerced_inst)) orelse continue;
  33129                     const existing = comptime_val orelse {
  33130                         comptime_val = coerced_val;
  33131                         continue;
  33132                     };
  33133                     if (!coerced_val.eql(existing, field_ty.toType(), mod)) {
  33134                         comptime_val = null;
  33135                         break;
  33136                     }
  33137                 }
  33138 
  33139                 field_val.* = if (comptime_val) |v| v.toIntern() else .none;
  33140             }
  33141 
  33142             const final_ty = try mod.intern(.{ .anon_struct_type = .{
  33143                 .types = field_types,
  33144                 .names = if (is_tuple) &.{} else field_names,
  33145                 .values = field_vals,
  33146             } });
  33147 
  33148             return .{ .success = final_ty.toType() };
  33149         },
  33150 
  33151         .exact => {
  33152             var expect_ty: ?Type = null;
  33153             var first_idx: usize = undefined;
  33154             for (peer_tys, 0..) |opt_ty, i| {
  33155                 const ty = opt_ty orelse continue;
  33156                 if (expect_ty) |expect| {
  33157                     if (!ty.eql(expect, mod)) return .{ .conflict = .{
  33158                         .peer_idx_a = first_idx,
  33159                         .peer_idx_b = i,
  33160                     } };
  33161                 } else {
  33162                     expect_ty = ty;
  33163                     first_idx = i;
  33164                 }
  33165             }
  33166             return .{ .success = expect_ty.? };
  33167         },
  33168     }
  33169 }
  33170 
  33171 fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type {
  33172     // e0 -> e1
  33173     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) {
  33174         return e1;
  33175     }
  33176 
  33177     // e1 -> e0
  33178     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) {
  33179         return e0;
  33180     }
  33181 
  33182     return sema.errorSetMerge(e0, e1);
  33183 }
  33184 
  33185 fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type {
  33186     // ty_b -> ty_a
  33187     if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, sema.mod.getTarget(), src, src)) {
  33188         return ty_a;
  33189     }
  33190 
  33191     // ty_a -> ty_b
  33192     if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, sema.mod.getTarget(), src, src)) {
  33193         return ty_b;
  33194     }
  33195 
  33196     return null;
  33197 }
  33198 
  33199 const ArrayLike = struct {
  33200     len: u64,
  33201     /// `noreturn` indicates that this type is `struct{}` so can coerce to anything
  33202     elem_ty: Type,
  33203 };
  33204 fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike {
  33205     const mod = sema.mod;
  33206     return switch (ty.zigTypeTag(mod)) {
  33207         .Array => .{
  33208             .len = ty.arrayLen(mod),
  33209             .elem_ty = ty.childType(mod),
  33210         },
  33211         .Struct => {
  33212             const field_count = ty.structFieldCount(mod);
  33213             if (field_count == 0) return .{
  33214                 .len = 0,
  33215                 .elem_ty = Type.noreturn,
  33216             };
  33217             if (!ty.isTuple(mod)) return null;
  33218             const elem_ty = ty.structFieldType(0, mod);
  33219             for (1..field_count) |i| {
  33220                 if (!ty.structFieldType(i, mod).eql(elem_ty, mod)) {
  33221                     return null;
  33222                 }
  33223             }
  33224             return .{
  33225                 .len = field_count,
  33226                 .elem_ty = elem_ty,
  33227             };
  33228         },
  33229         else => null,
  33230     };
  33231 }
  33232 
  33233 pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void {
  33234     const mod = sema.mod;
  33235     try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.return_type.toType());
  33236 
  33237     if (mod.comp.bin_file.options.error_return_tracing and mod.typeToFunc(fn_ty).?.return_type.toType().isError(mod)) {
  33238         // Ensure the type exists so that backends can assume that.
  33239         _ = try sema.getBuiltinType("StackTrace");
  33240     }
  33241 
  33242     for (0..mod.typeToFunc(fn_ty).?.param_types.len) |i| {
  33243         try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.param_types[i].toType());
  33244     }
  33245 }
  33246 
  33247 /// Make it so that calling hash() and eql() on `val` will not assert due
  33248 /// to a type not having its layout resolved.
  33249 fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value {
  33250     const mod = sema.mod;
  33251     switch (mod.intern_pool.indexToKey(val.toIntern())) {
  33252         .int => |int| switch (int.storage) {
  33253             .u64, .i64, .big_int => return val,
  33254             .lazy_align, .lazy_size => return (try mod.intern(.{ .int = .{
  33255                 .ty = int.ty,
  33256                 .storage = .{ .u64 = (try val.getUnsignedIntAdvanced(mod, sema)).? },
  33257             } })).toValue(),
  33258         },
  33259         .ptr => |ptr| {
  33260             const resolved_len = switch (ptr.len) {
  33261                 .none => .none,
  33262                 else => (try sema.resolveLazyValue(ptr.len.toValue())).toIntern(),
  33263             };
  33264             switch (ptr.addr) {
  33265                 .decl, .mut_decl => return if (resolved_len == ptr.len)
  33266                     val
  33267                 else
  33268                     (try mod.intern(.{ .ptr = .{
  33269                         .ty = ptr.ty,
  33270                         .addr = switch (ptr.addr) {
  33271                             .decl => |decl| .{ .decl = decl },
  33272                             .mut_decl => |mut_decl| .{ .mut_decl = mut_decl },
  33273                             else => unreachable,
  33274                         },
  33275                         .len = resolved_len,
  33276                     } })).toValue(),
  33277                 .comptime_field => |field_val| {
  33278                     const resolved_field_val =
  33279                         (try sema.resolveLazyValue(field_val.toValue())).toIntern();
  33280                     return if (resolved_field_val == field_val and resolved_len == ptr.len)
  33281                         val
  33282                     else
  33283                         (try mod.intern(.{ .ptr = .{
  33284                             .ty = ptr.ty,
  33285                             .addr = .{ .comptime_field = resolved_field_val },
  33286                             .len = resolved_len,
  33287                         } })).toValue();
  33288                 },
  33289                 .int => |int| {
  33290                     const resolved_int = (try sema.resolveLazyValue(int.toValue())).toIntern();
  33291                     return if (resolved_int == int and resolved_len == ptr.len)
  33292                         val
  33293                     else
  33294                         (try mod.intern(.{ .ptr = .{
  33295                             .ty = ptr.ty,
  33296                             .addr = .{ .int = resolved_int },
  33297                             .len = resolved_len,
  33298                         } })).toValue();
  33299                 },
  33300                 .eu_payload, .opt_payload => |base| {
  33301                     const resolved_base = (try sema.resolveLazyValue(base.toValue())).toIntern();
  33302                     return if (resolved_base == base and resolved_len == ptr.len)
  33303                         val
  33304                     else
  33305                         (try mod.intern(.{ .ptr = .{
  33306                             .ty = ptr.ty,
  33307                             .addr = switch (ptr.addr) {
  33308                                 .eu_payload => .{ .eu_payload = resolved_base },
  33309                                 .opt_payload => .{ .opt_payload = resolved_base },
  33310                                 else => unreachable,
  33311                             },
  33312                             .len = ptr.len,
  33313                         } })).toValue();
  33314                 },
  33315                 .elem, .field => |base_index| {
  33316                     const resolved_base = (try sema.resolveLazyValue(base_index.base.toValue())).toIntern();
  33317                     return if (resolved_base == base_index.base and resolved_len == ptr.len)
  33318                         val
  33319                     else
  33320                         (try mod.intern(.{ .ptr = .{
  33321                             .ty = ptr.ty,
  33322                             .addr = switch (ptr.addr) {
  33323                                 .elem => .{ .elem = .{
  33324                                     .base = resolved_base,
  33325                                     .index = base_index.index,
  33326                                 } },
  33327                                 .field => .{ .field = .{
  33328                                     .base = resolved_base,
  33329                                     .index = base_index.index,
  33330                                 } },
  33331                                 else => unreachable,
  33332                             },
  33333                             .len = ptr.len,
  33334                         } })).toValue();
  33335                 },
  33336             }
  33337         },
  33338         .aggregate => |aggregate| switch (aggregate.storage) {
  33339             .bytes => return val,
  33340             .elems => |elems| {
  33341                 var resolved_elems: []InternPool.Index = &.{};
  33342                 for (elems, 0..) |elem, i| {
  33343                     const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern();
  33344                     if (resolved_elems.len == 0 and resolved_elem != elem) {
  33345                         resolved_elems = try sema.arena.alloc(InternPool.Index, elems.len);
  33346                         @memcpy(resolved_elems[0..i], elems[0..i]);
  33347                     }
  33348                     if (resolved_elems.len > 0) resolved_elems[i] = resolved_elem;
  33349                 }
  33350                 return if (resolved_elems.len == 0) val else (try mod.intern(.{ .aggregate = .{
  33351                     .ty = aggregate.ty,
  33352                     .storage = .{ .elems = resolved_elems },
  33353                 } })).toValue();
  33354             },
  33355             .repeated_elem => |elem| {
  33356                 const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern();
  33357                 return if (resolved_elem == elem) val else (try mod.intern(.{ .aggregate = .{
  33358                     .ty = aggregate.ty,
  33359                     .storage = .{ .repeated_elem = resolved_elem },
  33360                 } })).toValue();
  33361             },
  33362         },
  33363         .un => |un| {
  33364             const resolved_tag = (try sema.resolveLazyValue(un.tag.toValue())).toIntern();
  33365             const resolved_val = (try sema.resolveLazyValue(un.val.toValue())).toIntern();
  33366             return if (resolved_tag == un.tag and resolved_val == un.val)
  33367                 val
  33368             else
  33369                 (try mod.intern(.{ .un = .{
  33370                     .ty = un.ty,
  33371                     .tag = resolved_tag,
  33372                     .val = resolved_val,
  33373                 } })).toValue();
  33374         },
  33375         else => return val,
  33376     }
  33377 }
  33378 
  33379 pub fn resolveTypeLayout(sema: *Sema, ty: Type) CompileError!void {
  33380     const mod = sema.mod;
  33381     switch (ty.zigTypeTag(mod)) {
  33382         .Struct => return sema.resolveStructLayout(ty),
  33383         .Union => return sema.resolveUnionLayout(ty),
  33384         .Array => {
  33385             if (ty.arrayLenIncludingSentinel(mod) == 0) return;
  33386             const elem_ty = ty.childType(mod);
  33387             return sema.resolveTypeLayout(elem_ty);
  33388         },
  33389         .Optional => {
  33390             const payload_ty = ty.optionalChild(mod);
  33391             // In case of querying the ABI alignment of this optional, we will ask
  33392             // for hasRuntimeBits() of the payload type, so we need "requires comptime"
  33393             // to be known already before this function returns.
  33394             _ = try sema.typeRequiresComptime(payload_ty);
  33395             return sema.resolveTypeLayout(payload_ty);
  33396         },
  33397         .ErrorUnion => {
  33398             const payload_ty = ty.errorUnionPayload(mod);
  33399             return sema.resolveTypeLayout(payload_ty);
  33400         },
  33401         .Fn => {
  33402             const info = mod.typeToFunc(ty).?;
  33403             if (info.is_generic) {
  33404                 // Resolving of generic function types is deferred to when
  33405                 // the function is instantiated.
  33406                 return;
  33407             }
  33408             for (info.param_types) |param_ty| {
  33409                 try sema.resolveTypeLayout(param_ty.toType());
  33410             }
  33411             try sema.resolveTypeLayout(info.return_type.toType());
  33412         },
  33413         else => {},
  33414     }
  33415 }
  33416 
  33417 fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
  33418     const mod = sema.mod;
  33419     const resolved_ty = try sema.resolveTypeFields(ty);
  33420     if (mod.typeToStruct(resolved_ty)) |struct_obj| {
  33421         switch (struct_obj.status) {
  33422             .none, .have_field_types => {},
  33423             .field_types_wip, .layout_wip => {
  33424                 const msg = try Module.ErrorMsg.create(
  33425                     sema.gpa,
  33426                     struct_obj.srcLoc(mod),
  33427                     "struct '{}' depends on itself",
  33428                     .{ty.fmt(mod)},
  33429                 );
  33430                 return sema.failWithOwnedErrorMsg(msg);
  33431             },
  33432             .have_layout, .fully_resolved_wip, .fully_resolved => return,
  33433         }
  33434         const prev_status = struct_obj.status;
  33435         errdefer if (struct_obj.status == .layout_wip) {
  33436             struct_obj.status = prev_status;
  33437         };
  33438 
  33439         struct_obj.status = .layout_wip;
  33440         for (struct_obj.fields.values(), 0..) |field, i| {
  33441             sema.resolveTypeLayout(field.ty) catch |err| switch (err) {
  33442                 error.AnalysisFail => {
  33443                     const msg = sema.err orelse return err;
  33444                     try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{});
  33445                     return err;
  33446                 },
  33447                 else => return err,
  33448             };
  33449         }
  33450 
  33451         if (struct_obj.layout == .Packed) {
  33452             try semaBackingIntType(mod, struct_obj);
  33453         }
  33454 
  33455         struct_obj.status = .have_layout;
  33456         _ = try sema.resolveTypeRequiresComptime(resolved_ty);
  33457 
  33458         if (struct_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) {
  33459             const msg = try Module.ErrorMsg.create(
  33460                 sema.gpa,
  33461                 struct_obj.srcLoc(mod),
  33462                 "struct layout depends on it having runtime bits",
  33463                 .{},
  33464             );
  33465             return sema.failWithOwnedErrorMsg(msg);
  33466         }
  33467 
  33468         if (struct_obj.layout == .Auto and mod.backendSupportsFeature(.field_reordering)) {
  33469             const optimized_order = try mod.tmp_hack_arena.allocator().alloc(u32, struct_obj.fields.count());
  33470 
  33471             for (struct_obj.fields.values(), 0..) |field, i| {
  33472                 optimized_order[i] = if (try sema.typeHasRuntimeBits(field.ty))
  33473                     @as(u32, @intCast(i))
  33474                 else
  33475                     Module.Struct.omitted_field;
  33476             }
  33477 
  33478             const AlignSortContext = struct {
  33479                 struct_obj: *Module.Struct,
  33480                 sema: *Sema,
  33481 
  33482                 fn lessThan(ctx: @This(), a: u32, b: u32) bool {
  33483                     const m = ctx.sema.mod;
  33484                     if (a == Module.Struct.omitted_field) return false;
  33485                     if (b == Module.Struct.omitted_field) return true;
  33486                     return ctx.struct_obj.fields.values()[a].ty.abiAlignment(m) >
  33487                         ctx.struct_obj.fields.values()[b].ty.abiAlignment(m);
  33488                 }
  33489             };
  33490             mem.sort(u32, optimized_order, AlignSortContext{
  33491                 .struct_obj = struct_obj,
  33492                 .sema = sema,
  33493             }, AlignSortContext.lessThan);
  33494             struct_obj.optimized_order = optimized_order.ptr;
  33495         }
  33496     }
  33497     // otherwise it's a tuple; no need to resolve anything
  33498 }
  33499 
  33500 fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!void {
  33501     const gpa = mod.gpa;
  33502 
  33503     var fields_bit_sum: u64 = 0;
  33504     for (struct_obj.fields.values()) |field| {
  33505         fields_bit_sum += field.ty.bitSize(mod);
  33506     }
  33507 
  33508     const decl_index = struct_obj.owner_decl;
  33509     const decl = mod.declPtr(decl_index);
  33510 
  33511     const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir;
  33512     const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
  33513     assert(extended.opcode == .struct_decl);
  33514     const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small));
  33515 
  33516     if (small.has_backing_int) {
  33517         var extra_index: usize = extended.operand;
  33518         extra_index += @intFromBool(small.has_src_node);
  33519         extra_index += @intFromBool(small.has_fields_len);
  33520         extra_index += @intFromBool(small.has_decls_len);
  33521 
  33522         const backing_int_body_len = zir.extra[extra_index];
  33523         extra_index += 1;
  33524 
  33525         var analysis_arena = std.heap.ArenaAllocator.init(gpa);
  33526         defer analysis_arena.deinit();
  33527 
  33528         var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
  33529         defer comptime_mutable_decls.deinit();
  33530 
  33531         var sema: Sema = .{
  33532             .mod = mod,
  33533             .gpa = gpa,
  33534             .arena = analysis_arena.allocator(),
  33535             .code = zir,
  33536             .owner_decl = decl,
  33537             .owner_decl_index = decl_index,
  33538             .func = null,
  33539             .func_index = .none,
  33540             .fn_ret_ty = Type.void,
  33541             .owner_func = null,
  33542             .owner_func_index = .none,
  33543             .comptime_mutable_decls = &comptime_mutable_decls,
  33544         };
  33545         defer sema.deinit();
  33546 
  33547         var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope);
  33548         defer wip_captures.deinit();
  33549 
  33550         var block: Block = .{
  33551             .parent = null,
  33552             .sema = &sema,
  33553             .src_decl = decl_index,
  33554             .namespace = struct_obj.namespace,
  33555             .wip_capture_scope = wip_captures.scope,
  33556             .instructions = .{},
  33557             .inlining = null,
  33558             .is_comptime = true,
  33559         };
  33560         defer {
  33561             assert(block.instructions.items.len == 0);
  33562             block.params.deinit(gpa);
  33563         }
  33564 
  33565         const backing_int_src: LazySrcLoc = .{ .node_offset_container_tag = 0 };
  33566         const backing_int_ty = blk: {
  33567             if (backing_int_body_len == 0) {
  33568                 const backing_int_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  33569                 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref);
  33570             } else {
  33571                 const body = zir.extra[extra_index..][0..backing_int_body_len];
  33572                 const ty_ref = try sema.resolveBody(&block, body, struct_obj.zir_index);
  33573                 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref);
  33574             }
  33575         };
  33576 
  33577         try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum);
  33578         struct_obj.backing_int_ty = backing_int_ty;
  33579         try wip_captures.finalize();
  33580         for (comptime_mutable_decls.items) |ct_decl_index| {
  33581             const ct_decl = mod.declPtr(ct_decl_index);
  33582             try ct_decl.intern(mod);
  33583         }
  33584     } else {
  33585         if (fields_bit_sum > std.math.maxInt(u16)) {
  33586             var sema: Sema = .{
  33587                 .mod = mod,
  33588                 .gpa = gpa,
  33589                 .arena = undefined,
  33590                 .code = zir,
  33591                 .owner_decl = decl,
  33592                 .owner_decl_index = decl_index,
  33593                 .func = null,
  33594                 .func_index = .none,
  33595                 .fn_ret_ty = Type.void,
  33596                 .owner_func = null,
  33597                 .owner_func_index = .none,
  33598                 .comptime_mutable_decls = undefined,
  33599             };
  33600             defer sema.deinit();
  33601 
  33602             var block: Block = .{
  33603                 .parent = null,
  33604                 .sema = &sema,
  33605                 .src_decl = decl_index,
  33606                 .namespace = struct_obj.namespace,
  33607                 .wip_capture_scope = undefined,
  33608                 .instructions = .{},
  33609                 .inlining = null,
  33610                 .is_comptime = true,
  33611             };
  33612             return sema.fail(&block, LazySrcLoc.nodeOffset(0), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum});
  33613         }
  33614         struct_obj.backing_int_ty = try mod.intType(.unsigned, @as(u16, @intCast(fields_bit_sum)));
  33615     }
  33616 }
  33617 
  33618 fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void {
  33619     const mod = sema.mod;
  33620 
  33621     if (!backing_int_ty.isInt(mod)) {
  33622         return sema.fail(block, src, "expected backing integer type, found '{}'", .{backing_int_ty.fmt(sema.mod)});
  33623     }
  33624     if (backing_int_ty.bitSize(mod) != fields_bit_sum) {
  33625         return sema.fail(
  33626             block,
  33627             src,
  33628             "backing integer type '{}' has bit size {} but the struct fields have a total bit size of {}",
  33629             .{ backing_int_ty.fmt(sema.mod), backing_int_ty.bitSize(mod), fields_bit_sum },
  33630         );
  33631     }
  33632 }
  33633 
  33634 fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  33635     const mod = sema.mod;
  33636     if (!ty.isIndexable(mod)) {
  33637         const msg = msg: {
  33638             const msg = try sema.errMsg(block, src, "type '{}' does not support indexing", .{ty.fmt(sema.mod)});
  33639             errdefer msg.destroy(sema.gpa);
  33640             try sema.errNote(block, src, msg, "operand must be an array, slice, tuple, or vector", .{});
  33641             break :msg msg;
  33642         };
  33643         return sema.failWithOwnedErrorMsg(msg);
  33644     }
  33645 }
  33646 
  33647 fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  33648     const mod = sema.mod;
  33649     if (ty.zigTypeTag(mod) == .Pointer) {
  33650         switch (ty.ptrSize(mod)) {
  33651             .Slice, .Many, .C => return,
  33652             .One => {
  33653                 const elem_ty = ty.childType(mod);
  33654                 if (elem_ty.zigTypeTag(mod) == .Array) return;
  33655                 // TODO https://github.com/ziglang/zig/issues/15479
  33656                 // if (elem_ty.isTuple()) return;
  33657             },
  33658         }
  33659     }
  33660     const msg = msg: {
  33661         const msg = try sema.errMsg(block, src, "type '{}' is not an indexable pointer", .{ty.fmt(sema.mod)});
  33662         errdefer msg.destroy(sema.gpa);
  33663         try sema.errNote(block, src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{});
  33664         break :msg msg;
  33665     };
  33666     return sema.failWithOwnedErrorMsg(msg);
  33667 }
  33668 
  33669 fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
  33670     const mod = sema.mod;
  33671     const resolved_ty = try sema.resolveTypeFields(ty);
  33672     const union_obj = mod.typeToUnion(resolved_ty).?;
  33673     switch (union_obj.status) {
  33674         .none, .have_field_types => {},
  33675         .field_types_wip, .layout_wip => {
  33676             const msg = try Module.ErrorMsg.create(
  33677                 sema.gpa,
  33678                 union_obj.srcLoc(sema.mod),
  33679                 "union '{}' depends on itself",
  33680                 .{ty.fmt(sema.mod)},
  33681             );
  33682             return sema.failWithOwnedErrorMsg(msg);
  33683         },
  33684         .have_layout, .fully_resolved_wip, .fully_resolved => return,
  33685     }
  33686     const prev_status = union_obj.status;
  33687     errdefer if (union_obj.status == .layout_wip) {
  33688         union_obj.status = prev_status;
  33689     };
  33690 
  33691     union_obj.status = .layout_wip;
  33692     for (union_obj.fields.values(), 0..) |field, i| {
  33693         sema.resolveTypeLayout(field.ty) catch |err| switch (err) {
  33694             error.AnalysisFail => {
  33695                 const msg = sema.err orelse return err;
  33696                 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{});
  33697                 return err;
  33698             },
  33699             else => return err,
  33700         };
  33701     }
  33702     union_obj.status = .have_layout;
  33703     _ = try sema.resolveTypeRequiresComptime(resolved_ty);
  33704 
  33705     if (union_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) {
  33706         const msg = try Module.ErrorMsg.create(
  33707             sema.gpa,
  33708             union_obj.srcLoc(sema.mod),
  33709             "union layout depends on it having runtime bits",
  33710             .{},
  33711         );
  33712         return sema.failWithOwnedErrorMsg(msg);
  33713     }
  33714 }
  33715 
  33716 // In case of querying the ABI alignment of this struct, we will ask
  33717 // for hasRuntimeBits() of each field, so we need "requires comptime"
  33718 // to be known already before this function returns.
  33719 pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
  33720     const mod = sema.mod;
  33721 
  33722     return switch (ty.toIntern()) {
  33723         .empty_struct_type => false,
  33724         else => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  33725             .int_type => false,
  33726             .ptr_type => |ptr_type| {
  33727                 const child_ty = ptr_type.child.toType();
  33728                 if (child_ty.zigTypeTag(mod) == .Fn) {
  33729                     return mod.typeToFunc(child_ty).?.is_generic;
  33730                 } else {
  33731                     return sema.resolveTypeRequiresComptime(child_ty);
  33732                 }
  33733             },
  33734             .anyframe_type => |child| {
  33735                 if (child == .none) return false;
  33736                 return sema.resolveTypeRequiresComptime(child.toType());
  33737             },
  33738             .array_type => |array_type| return sema.resolveTypeRequiresComptime(array_type.child.toType()),
  33739             .vector_type => |vector_type| return sema.resolveTypeRequiresComptime(vector_type.child.toType()),
  33740             .opt_type => |child| return sema.resolveTypeRequiresComptime(child.toType()),
  33741             .error_union_type => |error_union_type| return sema.resolveTypeRequiresComptime(error_union_type.payload_type.toType()),
  33742             .error_set_type, .inferred_error_set_type => false,
  33743 
  33744             .func_type => true,
  33745 
  33746             .simple_type => |t| switch (t) {
  33747                 .f16,
  33748                 .f32,
  33749                 .f64,
  33750                 .f80,
  33751                 .f128,
  33752                 .usize,
  33753                 .isize,
  33754                 .c_char,
  33755                 .c_short,
  33756                 .c_ushort,
  33757                 .c_int,
  33758                 .c_uint,
  33759                 .c_long,
  33760                 .c_ulong,
  33761                 .c_longlong,
  33762                 .c_ulonglong,
  33763                 .c_longdouble,
  33764                 .anyopaque,
  33765                 .bool,
  33766                 .void,
  33767                 .anyerror,
  33768                 .noreturn,
  33769                 .generic_poison,
  33770                 .atomic_order,
  33771                 .atomic_rmw_op,
  33772                 .calling_convention,
  33773                 .address_space,
  33774                 .float_mode,
  33775                 .reduce_op,
  33776                 .call_modifier,
  33777                 .prefetch_options,
  33778                 .export_options,
  33779                 .extern_options,
  33780                 => false,
  33781 
  33782                 .type,
  33783                 .comptime_int,
  33784                 .comptime_float,
  33785                 .null,
  33786                 .undefined,
  33787                 .enum_literal,
  33788                 .type_info,
  33789                 => true,
  33790             },
  33791             .struct_type => |struct_type| {
  33792                 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false;
  33793                 switch (struct_obj.requires_comptime) {
  33794                     .no, .wip => return false,
  33795                     .yes => return true,
  33796                     .unknown => {
  33797                         var requires_comptime = false;
  33798                         struct_obj.requires_comptime = .wip;
  33799                         for (struct_obj.fields.values()) |field| {
  33800                             if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
  33801                         }
  33802                         if (requires_comptime) {
  33803                             struct_obj.requires_comptime = .yes;
  33804                         } else {
  33805                             struct_obj.requires_comptime = .no;
  33806                         }
  33807                         return requires_comptime;
  33808                     },
  33809                 }
  33810             },
  33811 
  33812             .anon_struct_type => |tuple| {
  33813                 for (tuple.types, tuple.values) |field_ty, field_val| {
  33814                     const have_comptime_val = field_val != .none;
  33815                     if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty.toType())) {
  33816                         return true;
  33817                     }
  33818                 }
  33819                 return false;
  33820             },
  33821 
  33822             .union_type => |union_type| {
  33823                 const union_obj = mod.unionPtr(union_type.index);
  33824                 switch (union_obj.requires_comptime) {
  33825                     .no, .wip => return false,
  33826                     .yes => return true,
  33827                     .unknown => {
  33828                         var requires_comptime = false;
  33829                         union_obj.requires_comptime = .wip;
  33830                         for (union_obj.fields.values()) |field| {
  33831                             if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
  33832                         }
  33833                         if (requires_comptime) {
  33834                             union_obj.requires_comptime = .yes;
  33835                         } else {
  33836                             union_obj.requires_comptime = .no;
  33837                         }
  33838                         return requires_comptime;
  33839                     },
  33840                 }
  33841             },
  33842 
  33843             .opaque_type => false,
  33844 
  33845             .enum_type => |enum_type| try sema.resolveTypeRequiresComptime(enum_type.tag_ty.toType()),
  33846 
  33847             // values, not types
  33848             .undef,
  33849             .runtime_value,
  33850             .simple_value,
  33851             .variable,
  33852             .extern_func,
  33853             .func,
  33854             .int,
  33855             .err,
  33856             .error_union,
  33857             .enum_literal,
  33858             .enum_tag,
  33859             .empty_enum_value,
  33860             .float,
  33861             .ptr,
  33862             .opt,
  33863             .aggregate,
  33864             .un,
  33865             // memoization, not types
  33866             .memoized_call,
  33867             => unreachable,
  33868         },
  33869     };
  33870 }
  33871 
  33872 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to
  33873 /// be resolved.
  33874 pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void {
  33875     const mod = sema.mod;
  33876     switch (ty.zigTypeTag(mod)) {
  33877         .Pointer => {
  33878             const child_ty = try sema.resolveTypeFields(ty.childType(mod));
  33879             return sema.resolveTypeFully(child_ty);
  33880         },
  33881         .Struct => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  33882             .struct_type => return sema.resolveStructFully(ty),
  33883             .anon_struct_type => |tuple| {
  33884                 for (tuple.types) |field_ty| {
  33885                     try sema.resolveTypeFully(field_ty.toType());
  33886                 }
  33887             },
  33888             else => {},
  33889         },
  33890         .Union => return sema.resolveUnionFully(ty),
  33891         .Array => return sema.resolveTypeFully(ty.childType(mod)),
  33892         .Optional => {
  33893             return sema.resolveTypeFully(ty.optionalChild(mod));
  33894         },
  33895         .ErrorUnion => return sema.resolveTypeFully(ty.errorUnionPayload(mod)),
  33896         .Fn => {
  33897             const info = mod.typeToFunc(ty).?;
  33898             if (info.is_generic) {
  33899                 // Resolving of generic function types is deferred to when
  33900                 // the function is instantiated.
  33901                 return;
  33902             }
  33903             for (info.param_types) |param_ty| {
  33904                 try sema.resolveTypeFully(param_ty.toType());
  33905             }
  33906             try sema.resolveTypeFully(info.return_type.toType());
  33907         },
  33908         else => {},
  33909     }
  33910 }
  33911 
  33912 fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void {
  33913     try sema.resolveStructLayout(ty);
  33914 
  33915     const mod = sema.mod;
  33916     const resolved_ty = try sema.resolveTypeFields(ty);
  33917     const struct_obj = mod.typeToStruct(resolved_ty).?;
  33918 
  33919     switch (struct_obj.status) {
  33920         .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
  33921         .fully_resolved_wip, .fully_resolved => return,
  33922     }
  33923 
  33924     {
  33925         // After we have resolve struct layout we have to go over the fields again to
  33926         // make sure pointer fields get their child types resolved as well.
  33927         // See also similar code for unions.
  33928         const prev_status = struct_obj.status;
  33929         errdefer struct_obj.status = prev_status;
  33930 
  33931         struct_obj.status = .fully_resolved_wip;
  33932         for (struct_obj.fields.values()) |field| {
  33933             try sema.resolveTypeFully(field.ty);
  33934         }
  33935         struct_obj.status = .fully_resolved;
  33936     }
  33937 
  33938     // And let's not forget comptime-only status.
  33939     _ = try sema.typeRequiresComptime(ty);
  33940 }
  33941 
  33942 fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void {
  33943     try sema.resolveUnionLayout(ty);
  33944 
  33945     const mod = sema.mod;
  33946     const resolved_ty = try sema.resolveTypeFields(ty);
  33947     const union_obj = mod.typeToUnion(resolved_ty).?;
  33948     switch (union_obj.status) {
  33949         .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
  33950         .fully_resolved_wip, .fully_resolved => return,
  33951     }
  33952 
  33953     {
  33954         // After we have resolve union layout we have to go over the fields again to
  33955         // make sure pointer fields get their child types resolved as well.
  33956         // See also similar code for structs.
  33957         const prev_status = union_obj.status;
  33958         errdefer union_obj.status = prev_status;
  33959 
  33960         union_obj.status = .fully_resolved_wip;
  33961         for (union_obj.fields.values()) |field| {
  33962             try sema.resolveTypeFully(field.ty);
  33963         }
  33964         union_obj.status = .fully_resolved;
  33965     }
  33966 
  33967     // And let's not forget comptime-only status.
  33968     _ = try sema.typeRequiresComptime(ty);
  33969 }
  33970 
  33971 pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type {
  33972     const mod = sema.mod;
  33973 
  33974     switch (ty.toIntern()) {
  33975         .var_args_param_type => unreachable,
  33976 
  33977         .none => unreachable,
  33978 
  33979         .u0_type,
  33980         .i0_type,
  33981         .u1_type,
  33982         .u8_type,
  33983         .i8_type,
  33984         .u16_type,
  33985         .i16_type,
  33986         .u29_type,
  33987         .u32_type,
  33988         .i32_type,
  33989         .u64_type,
  33990         .i64_type,
  33991         .u80_type,
  33992         .u128_type,
  33993         .i128_type,
  33994         .usize_type,
  33995         .isize_type,
  33996         .c_char_type,
  33997         .c_short_type,
  33998         .c_ushort_type,
  33999         .c_int_type,
  34000         .c_uint_type,
  34001         .c_long_type,
  34002         .c_ulong_type,
  34003         .c_longlong_type,
  34004         .c_ulonglong_type,
  34005         .c_longdouble_type,
  34006         .f16_type,
  34007         .f32_type,
  34008         .f64_type,
  34009         .f80_type,
  34010         .f128_type,
  34011         .anyopaque_type,
  34012         .bool_type,
  34013         .void_type,
  34014         .type_type,
  34015         .anyerror_type,
  34016         .comptime_int_type,
  34017         .comptime_float_type,
  34018         .noreturn_type,
  34019         .anyframe_type,
  34020         .null_type,
  34021         .undefined_type,
  34022         .enum_literal_type,
  34023         .manyptr_u8_type,
  34024         .manyptr_const_u8_type,
  34025         .manyptr_const_u8_sentinel_0_type,
  34026         .single_const_pointer_to_comptime_int_type,
  34027         .slice_const_u8_type,
  34028         .slice_const_u8_sentinel_0_type,
  34029         .optional_noreturn_type,
  34030         .anyerror_void_error_union_type,
  34031         .generic_poison_type,
  34032         .empty_struct_type,
  34033         => return ty,
  34034 
  34035         .undef => unreachable,
  34036         .zero => unreachable,
  34037         .zero_usize => unreachable,
  34038         .zero_u8 => unreachable,
  34039         .one => unreachable,
  34040         .one_usize => unreachable,
  34041         .one_u8 => unreachable,
  34042         .four_u8 => unreachable,
  34043         .negative_one => unreachable,
  34044         .calling_convention_c => unreachable,
  34045         .calling_convention_inline => unreachable,
  34046         .void_value => unreachable,
  34047         .unreachable_value => unreachable,
  34048         .null_value => unreachable,
  34049         .bool_true => unreachable,
  34050         .bool_false => unreachable,
  34051         .empty_struct => unreachable,
  34052         .generic_poison => unreachable,
  34053 
  34054         .type_info_type => return sema.getBuiltinType("Type"),
  34055         .extern_options_type => return sema.getBuiltinType("ExternOptions"),
  34056         .export_options_type => return sema.getBuiltinType("ExportOptions"),
  34057         .atomic_order_type => return sema.getBuiltinType("AtomicOrder"),
  34058         .atomic_rmw_op_type => return sema.getBuiltinType("AtomicRmwOp"),
  34059         .calling_convention_type => return sema.getBuiltinType("CallingConvention"),
  34060         .address_space_type => return sema.getBuiltinType("AddressSpace"),
  34061         .float_mode_type => return sema.getBuiltinType("FloatMode"),
  34062         .reduce_op_type => return sema.getBuiltinType("ReduceOp"),
  34063         .call_modifier_type => return sema.getBuiltinType("CallModifier"),
  34064         .prefetch_options_type => return sema.getBuiltinType("PrefetchOptions"),
  34065 
  34066         _ => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) {
  34067             .type_struct,
  34068             .type_struct_ns,
  34069             .type_union_tagged,
  34070             .type_union_untagged,
  34071             .type_union_safety,
  34072             => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  34073                 .struct_type => |struct_type| {
  34074                     const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return ty;
  34075                     try sema.resolveTypeFieldsStruct(ty, struct_obj);
  34076                     return ty;
  34077                 },
  34078                 .union_type => |union_type| {
  34079                     const union_obj = mod.unionPtr(union_type.index);
  34080                     try sema.resolveTypeFieldsUnion(ty, union_obj);
  34081                     return ty;
  34082                 },
  34083                 else => unreachable,
  34084             },
  34085             else => return ty,
  34086         },
  34087     }
  34088 }
  34089 
  34090 fn resolveTypeFieldsStruct(
  34091     sema: *Sema,
  34092     ty: Type,
  34093     struct_obj: *Module.Struct,
  34094 ) CompileError!void {
  34095     switch (sema.mod.declPtr(struct_obj.owner_decl).analysis) {
  34096         .file_failure,
  34097         .dependency_failure,
  34098         .sema_failure,
  34099         .sema_failure_retryable,
  34100         => {
  34101             sema.owner_decl.analysis = .dependency_failure;
  34102             sema.owner_decl.generation = sema.mod.generation;
  34103             return error.AnalysisFail;
  34104         },
  34105         else => {},
  34106     }
  34107     switch (struct_obj.status) {
  34108         .none => {},
  34109         .field_types_wip => {
  34110             const msg = try Module.ErrorMsg.create(
  34111                 sema.gpa,
  34112                 struct_obj.srcLoc(sema.mod),
  34113                 "struct '{}' depends on itself",
  34114                 .{ty.fmt(sema.mod)},
  34115             );
  34116             return sema.failWithOwnedErrorMsg(msg);
  34117         },
  34118         .have_field_types,
  34119         .have_layout,
  34120         .layout_wip,
  34121         .fully_resolved_wip,
  34122         .fully_resolved,
  34123         => return,
  34124     }
  34125 
  34126     struct_obj.status = .field_types_wip;
  34127     errdefer struct_obj.status = .none;
  34128     try semaStructFields(sema.mod, struct_obj);
  34129 }
  34130 
  34131 fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_obj: *Module.Union) CompileError!void {
  34132     switch (sema.mod.declPtr(union_obj.owner_decl).analysis) {
  34133         .file_failure,
  34134         .dependency_failure,
  34135         .sema_failure,
  34136         .sema_failure_retryable,
  34137         => {
  34138             sema.owner_decl.analysis = .dependency_failure;
  34139             sema.owner_decl.generation = sema.mod.generation;
  34140             return error.AnalysisFail;
  34141         },
  34142         else => {},
  34143     }
  34144     switch (union_obj.status) {
  34145         .none => {},
  34146         .field_types_wip => {
  34147             const msg = try Module.ErrorMsg.create(
  34148                 sema.gpa,
  34149                 union_obj.srcLoc(sema.mod),
  34150                 "union '{}' depends on itself",
  34151                 .{ty.fmt(sema.mod)},
  34152             );
  34153             return sema.failWithOwnedErrorMsg(msg);
  34154         },
  34155         .have_field_types,
  34156         .have_layout,
  34157         .layout_wip,
  34158         .fully_resolved_wip,
  34159         .fully_resolved,
  34160         => return,
  34161     }
  34162 
  34163     union_obj.status = .field_types_wip;
  34164     errdefer union_obj.status = .none;
  34165     try semaUnionFields(sema.mod, union_obj);
  34166     union_obj.status = .have_field_types;
  34167 }
  34168 
  34169 fn resolveInferredErrorSet(
  34170     sema: *Sema,
  34171     block: *Block,
  34172     src: LazySrcLoc,
  34173     ies_index: Module.Fn.InferredErrorSet.Index,
  34174 ) CompileError!void {
  34175     const mod = sema.mod;
  34176     const ies = mod.inferredErrorSetPtr(ies_index);
  34177 
  34178     if (ies.is_resolved) return;
  34179 
  34180     const func = mod.funcPtr(ies.func);
  34181     if (func.state == .in_progress) {
  34182         return sema.fail(block, src, "unable to resolve inferred error set", .{});
  34183     }
  34184 
  34185     // In order to ensure that all dependencies are properly added to the set, we
  34186     // need to ensure the function body is analyzed of the inferred error set.
  34187     // However, in the case of comptime/inline function calls with inferred error sets,
  34188     // each call gets a new InferredErrorSet object, which contains the same
  34189     // `Module.Fn.Index`. Not only is the function not relevant to the inferred error set
  34190     // in this case, it may be a generic function which would cause an assertion failure
  34191     // if we called `ensureFuncBodyAnalyzed` on it here.
  34192     const ies_func_owner_decl = mod.declPtr(func.owner_decl);
  34193     const ies_func_info = mod.typeToFunc(ies_func_owner_decl.ty).?;
  34194     // if ies declared by a inline function with generic return type, the return_type should be generic_poison,
  34195     // because inline function does not create a new declaration, and the ies has been filled with analyzeCall,
  34196     // so here we can simply skip this case.
  34197     if (ies_func_info.return_type == .generic_poison_type) {
  34198         assert(ies_func_info.cc == .Inline);
  34199     } else if (mod.typeToInferredErrorSet(ies_func_info.return_type.toType().errorUnionSet(mod)).? == ies) {
  34200         if (ies_func_info.is_generic) {
  34201             const msg = msg: {
  34202                 const msg = try sema.errMsg(block, src, "unable to resolve inferred error set of generic function", .{});
  34203                 errdefer msg.destroy(sema.gpa);
  34204 
  34205                 try sema.mod.errNoteNonLazy(ies_func_owner_decl.srcLoc(mod), msg, "generic function declared here", .{});
  34206                 break :msg msg;
  34207             };
  34208             return sema.failWithOwnedErrorMsg(msg);
  34209         }
  34210         // In this case we are dealing with the actual InferredErrorSet object that
  34211         // corresponds to the function, not one created to track an inline/comptime call.
  34212         try sema.ensureFuncBodyAnalyzed(ies.func);
  34213     }
  34214 
  34215     ies.is_resolved = true;
  34216 
  34217     for (ies.inferred_error_sets.keys()) |other_ies_index| {
  34218         if (ies_index == other_ies_index) continue;
  34219         try sema.resolveInferredErrorSet(block, src, other_ies_index);
  34220 
  34221         const other_ies = mod.inferredErrorSetPtr(other_ies_index);
  34222         for (other_ies.errors.keys()) |key| {
  34223             try ies.errors.put(sema.gpa, key, {});
  34224         }
  34225         if (other_ies.is_anyerror)
  34226             ies.is_anyerror = true;
  34227     }
  34228 }
  34229 
  34230 fn resolveInferredErrorSetTy(
  34231     sema: *Sema,
  34232     block: *Block,
  34233     src: LazySrcLoc,
  34234     ty: Type,
  34235 ) CompileError!void {
  34236     const mod = sema.mod;
  34237     if (mod.typeToInferredErrorSetIndex(ty).unwrap()) |ies_index| {
  34238         try sema.resolveInferredErrorSet(block, src, ies_index);
  34239     }
  34240 }
  34241 
  34242 fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void {
  34243     const gpa = mod.gpa;
  34244     const ip = &mod.intern_pool;
  34245     const decl_index = struct_obj.owner_decl;
  34246     const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir;
  34247     const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
  34248     assert(extended.opcode == .struct_decl);
  34249     const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small));
  34250     var extra_index: usize = extended.operand;
  34251 
  34252     const src = LazySrcLoc.nodeOffset(0);
  34253     extra_index += @intFromBool(small.has_src_node);
  34254 
  34255     const fields_len = if (small.has_fields_len) blk: {
  34256         const fields_len = zir.extra[extra_index];
  34257         extra_index += 1;
  34258         break :blk fields_len;
  34259     } else 0;
  34260 
  34261     const decls_len = if (small.has_decls_len) decls_len: {
  34262         const decls_len = zir.extra[extra_index];
  34263         extra_index += 1;
  34264         break :decls_len decls_len;
  34265     } else 0;
  34266 
  34267     // The backing integer cannot be handled until `resolveStructLayout()`.
  34268     if (small.has_backing_int) {
  34269         const backing_int_body_len = zir.extra[extra_index];
  34270         extra_index += 1; // backing_int_body_len
  34271         if (backing_int_body_len == 0) {
  34272             extra_index += 1; // backing_int_ref
  34273         } else {
  34274             extra_index += backing_int_body_len; // backing_int_body_inst
  34275         }
  34276     }
  34277 
  34278     // Skip over decls.
  34279     var decls_it = zir.declIteratorInner(extra_index, decls_len);
  34280     while (decls_it.next()) |_| {}
  34281     extra_index = decls_it.extra_index;
  34282 
  34283     if (fields_len == 0) {
  34284         if (struct_obj.layout == .Packed) {
  34285             try semaBackingIntType(mod, struct_obj);
  34286         }
  34287         struct_obj.status = .have_layout;
  34288         return;
  34289     }
  34290 
  34291     const decl = mod.declPtr(decl_index);
  34292 
  34293     var analysis_arena = std.heap.ArenaAllocator.init(gpa);
  34294     defer analysis_arena.deinit();
  34295 
  34296     var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
  34297     defer comptime_mutable_decls.deinit();
  34298 
  34299     var sema: Sema = .{
  34300         .mod = mod,
  34301         .gpa = gpa,
  34302         .arena = analysis_arena.allocator(),
  34303         .code = zir,
  34304         .owner_decl = decl,
  34305         .owner_decl_index = decl_index,
  34306         .func = null,
  34307         .func_index = .none,
  34308         .fn_ret_ty = Type.void,
  34309         .owner_func = null,
  34310         .owner_func_index = .none,
  34311         .comptime_mutable_decls = &comptime_mutable_decls,
  34312     };
  34313     defer sema.deinit();
  34314 
  34315     var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope);
  34316     defer wip_captures.deinit();
  34317 
  34318     var block_scope: Block = .{
  34319         .parent = null,
  34320         .sema = &sema,
  34321         .src_decl = decl_index,
  34322         .namespace = struct_obj.namespace,
  34323         .wip_capture_scope = wip_captures.scope,
  34324         .instructions = .{},
  34325         .inlining = null,
  34326         .is_comptime = true,
  34327     };
  34328     defer {
  34329         assert(block_scope.instructions.items.len == 0);
  34330         block_scope.params.deinit(gpa);
  34331     }
  34332 
  34333     struct_obj.fields = .{};
  34334     try struct_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len);
  34335 
  34336     const Field = struct {
  34337         type_body_len: u32 = 0,
  34338         align_body_len: u32 = 0,
  34339         init_body_len: u32 = 0,
  34340         type_ref: Zir.Inst.Ref = .none,
  34341     };
  34342     const fields = try sema.arena.alloc(Field, fields_len);
  34343     var any_inits = false;
  34344 
  34345     {
  34346         const bits_per_field = 4;
  34347         const fields_per_u32 = 32 / bits_per_field;
  34348         const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  34349         const flags_index = extra_index;
  34350         var bit_bag_index: usize = flags_index;
  34351         extra_index += bit_bags_count;
  34352         var cur_bit_bag: u32 = undefined;
  34353         var field_i: u32 = 0;
  34354         while (field_i < fields_len) : (field_i += 1) {
  34355             if (field_i % fields_per_u32 == 0) {
  34356                 cur_bit_bag = zir.extra[bit_bag_index];
  34357                 bit_bag_index += 1;
  34358             }
  34359             const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  34360             cur_bit_bag >>= 1;
  34361             const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
  34362             cur_bit_bag >>= 1;
  34363             const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0;
  34364             cur_bit_bag >>= 1;
  34365             const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
  34366             cur_bit_bag >>= 1;
  34367 
  34368             var field_name_zir: ?[:0]const u8 = null;
  34369             if (!small.is_tuple) {
  34370                 field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
  34371                 extra_index += 1;
  34372             }
  34373             extra_index += 1; // doc_comment
  34374 
  34375             fields[field_i] = .{};
  34376 
  34377             if (has_type_body) {
  34378                 fields[field_i].type_body_len = zir.extra[extra_index];
  34379             } else {
  34380                 fields[field_i].type_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  34381             }
  34382             extra_index += 1;
  34383 
  34384             // This string needs to outlive the ZIR code.
  34385             const field_name = try ip.getOrPutString(gpa, if (field_name_zir) |s|
  34386                 s
  34387             else
  34388                 try std.fmt.allocPrint(sema.arena, "{d}", .{field_i}));
  34389 
  34390             const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
  34391             if (gop.found_existing) {
  34392                 const msg = msg: {
  34393                     const field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i }).lazy;
  34394                     const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{}'", .{field_name.fmt(ip)});
  34395                     errdefer msg.destroy(gpa);
  34396 
  34397                     const prev_field_index = struct_obj.fields.getIndex(field_name).?;
  34398                     const prev_field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = prev_field_index });
  34399                     try mod.errNoteNonLazy(prev_field_src, msg, "other field here", .{});
  34400                     try sema.errNote(&block_scope, src, msg, "struct declared here", .{});
  34401                     break :msg msg;
  34402                 };
  34403                 return sema.failWithOwnedErrorMsg(msg);
  34404             }
  34405             gop.value_ptr.* = .{
  34406                 .ty = Type.noreturn,
  34407                 .abi_align = .none,
  34408                 .default_val = .none,
  34409                 .is_comptime = is_comptime,
  34410                 .offset = undefined,
  34411             };
  34412 
  34413             if (has_align) {
  34414                 fields[field_i].align_body_len = zir.extra[extra_index];
  34415                 extra_index += 1;
  34416             }
  34417             if (has_init) {
  34418                 fields[field_i].init_body_len = zir.extra[extra_index];
  34419                 extra_index += 1;
  34420                 any_inits = true;
  34421             }
  34422         }
  34423     }
  34424 
  34425     // Next we do only types and alignments, saving the inits for a second pass,
  34426     // so that init values may depend on type layout.
  34427     const bodies_index = extra_index;
  34428 
  34429     for (fields, 0..) |zir_field, field_i| {
  34430         const field_ty: Type = ty: {
  34431             if (zir_field.type_ref != .none) {
  34432                 break :ty sema.resolveType(&block_scope, .unneeded, zir_field.type_ref) catch |err| switch (err) {
  34433                     error.NeededSourceLocation => {
  34434                         const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34435                             .index = field_i,
  34436                             .range = .type,
  34437                         }).lazy;
  34438                         _ = try sema.resolveType(&block_scope, ty_src, zir_field.type_ref);
  34439                         unreachable;
  34440                     },
  34441                     else => |e| return e,
  34442                 };
  34443             }
  34444             assert(zir_field.type_body_len != 0);
  34445             const body = zir.extra[extra_index..][0..zir_field.type_body_len];
  34446             extra_index += body.len;
  34447             const ty_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
  34448             break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) {
  34449                 error.NeededSourceLocation => {
  34450                     const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34451                         .index = field_i,
  34452                         .range = .type,
  34453                     }).lazy;
  34454                     _ = try sema.analyzeAsType(&block_scope, ty_src, ty_ref);
  34455                     unreachable;
  34456                 },
  34457                 else => |e| return e,
  34458             };
  34459         };
  34460         if (field_ty.isGenericPoison()) {
  34461             return error.GenericPoison;
  34462         }
  34463 
  34464         const field = &struct_obj.fields.values()[field_i];
  34465         field.ty = field_ty;
  34466 
  34467         if (field_ty.zigTypeTag(mod) == .Opaque) {
  34468             const msg = msg: {
  34469                 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34470                     .index = field_i,
  34471                     .range = .type,
  34472                 }).lazy;
  34473                 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  34474                 errdefer msg.destroy(sema.gpa);
  34475 
  34476                 try sema.addDeclaredHereNote(msg, field_ty);
  34477                 break :msg msg;
  34478             };
  34479             return sema.failWithOwnedErrorMsg(msg);
  34480         }
  34481         if (field_ty.zigTypeTag(mod) == .NoReturn) {
  34482             const msg = msg: {
  34483                 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34484                     .index = field_i,
  34485                     .range = .type,
  34486                 }).lazy;
  34487                 const msg = try sema.errMsg(&block_scope, ty_src, "struct fields cannot be 'noreturn'", .{});
  34488                 errdefer msg.destroy(sema.gpa);
  34489 
  34490                 try sema.addDeclaredHereNote(msg, field_ty);
  34491                 break :msg msg;
  34492             };
  34493             return sema.failWithOwnedErrorMsg(msg);
  34494         }
  34495         if (struct_obj.layout == .Extern and !try sema.validateExternType(field.ty, .struct_field)) {
  34496             const msg = msg: {
  34497                 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34498                     .index = field_i,
  34499                     .range = .type,
  34500                 });
  34501                 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(mod)});
  34502                 errdefer msg.destroy(sema.gpa);
  34503 
  34504                 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field.ty, .struct_field);
  34505 
  34506                 try sema.addDeclaredHereNote(msg, field.ty);
  34507                 break :msg msg;
  34508             };
  34509             return sema.failWithOwnedErrorMsg(msg);
  34510         } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty, mod))) {
  34511             const msg = msg: {
  34512                 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34513                     .index = field_i,
  34514                     .range = .type,
  34515                 });
  34516                 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(mod)});
  34517                 errdefer msg.destroy(sema.gpa);
  34518 
  34519                 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field.ty);
  34520 
  34521                 try sema.addDeclaredHereNote(msg, field.ty);
  34522                 break :msg msg;
  34523             };
  34524             return sema.failWithOwnedErrorMsg(msg);
  34525         }
  34526 
  34527         if (zir_field.align_body_len > 0) {
  34528             const body = zir.extra[extra_index..][0..zir_field.align_body_len];
  34529             extra_index += body.len;
  34530             const align_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
  34531             field.abi_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) {
  34532                 error.NeededSourceLocation => {
  34533                     const align_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34534                         .index = field_i,
  34535                         .range = .alignment,
  34536                     }).lazy;
  34537                     _ = try sema.analyzeAsAlign(&block_scope, align_src, align_ref);
  34538                     unreachable;
  34539                 },
  34540                 else => |e| return e,
  34541             };
  34542         }
  34543 
  34544         extra_index += zir_field.init_body_len;
  34545     }
  34546 
  34547     struct_obj.status = .have_field_types;
  34548 
  34549     if (any_inits) {
  34550         extra_index = bodies_index;
  34551         for (fields, 0..) |zir_field, field_i| {
  34552             extra_index += zir_field.type_body_len;
  34553             extra_index += zir_field.align_body_len;
  34554             if (zir_field.init_body_len > 0) {
  34555                 const body = zir.extra[extra_index..][0..zir_field.init_body_len];
  34556                 extra_index += body.len;
  34557                 const init = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
  34558                 const field = &struct_obj.fields.values()[field_i];
  34559                 const coerced = sema.coerce(&block_scope, field.ty, init, .unneeded) catch |err| switch (err) {
  34560                     error.NeededSourceLocation => {
  34561                         const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34562                             .index = field_i,
  34563                             .range = .value,
  34564                         }).lazy;
  34565                         _ = try sema.coerce(&block_scope, field.ty, init, init_src);
  34566                         unreachable;
  34567                     },
  34568                     else => |e| return e,
  34569                 };
  34570                 const default_val = (try sema.resolveMaybeUndefVal(coerced)) orelse {
  34571                     const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{
  34572                         .index = field_i,
  34573                         .range = .value,
  34574                     }).lazy;
  34575                     return sema.failWithNeededComptime(&block_scope, init_src, "struct field default value must be comptime-known");
  34576                 };
  34577                 field.default_val = try default_val.intern(field.ty, mod);
  34578             }
  34579         }
  34580     }
  34581     try wip_captures.finalize();
  34582     for (comptime_mutable_decls.items) |ct_decl_index| {
  34583         const ct_decl = mod.declPtr(ct_decl_index);
  34584         try ct_decl.intern(mod);
  34585     }
  34586 
  34587     struct_obj.have_field_inits = true;
  34588 }
  34589 
  34590 fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
  34591     const tracy = trace(@src());
  34592     defer tracy.end();
  34593 
  34594     const gpa = mod.gpa;
  34595     const ip = &mod.intern_pool;
  34596     const decl_index = union_obj.owner_decl;
  34597     const zir = mod.namespacePtr(union_obj.namespace).file_scope.zir;
  34598     const extended = zir.instructions.items(.data)[union_obj.zir_index].extended;
  34599     assert(extended.opcode == .union_decl);
  34600     const small = @as(Zir.Inst.UnionDecl.Small, @bitCast(extended.small));
  34601     var extra_index: usize = extended.operand;
  34602 
  34603     const src = LazySrcLoc.nodeOffset(0);
  34604     extra_index += @intFromBool(small.has_src_node);
  34605 
  34606     const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: {
  34607         const ty_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  34608         extra_index += 1;
  34609         break :blk ty_ref;
  34610     } else .none;
  34611 
  34612     const body_len = if (small.has_body_len) blk: {
  34613         const body_len = zir.extra[extra_index];
  34614         extra_index += 1;
  34615         break :blk body_len;
  34616     } else 0;
  34617 
  34618     const fields_len = if (small.has_fields_len) blk: {
  34619         const fields_len = zir.extra[extra_index];
  34620         extra_index += 1;
  34621         break :blk fields_len;
  34622     } else 0;
  34623 
  34624     const decls_len = if (small.has_decls_len) decls_len: {
  34625         const decls_len = zir.extra[extra_index];
  34626         extra_index += 1;
  34627         break :decls_len decls_len;
  34628     } else 0;
  34629 
  34630     // Skip over decls.
  34631     var decls_it = zir.declIteratorInner(extra_index, decls_len);
  34632     while (decls_it.next()) |_| {}
  34633     extra_index = decls_it.extra_index;
  34634 
  34635     const body = zir.extra[extra_index..][0..body_len];
  34636     extra_index += body.len;
  34637 
  34638     const decl = mod.declPtr(decl_index);
  34639 
  34640     var analysis_arena = std.heap.ArenaAllocator.init(gpa);
  34641     defer analysis_arena.deinit();
  34642 
  34643     var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
  34644     defer comptime_mutable_decls.deinit();
  34645 
  34646     var sema: Sema = .{
  34647         .mod = mod,
  34648         .gpa = gpa,
  34649         .arena = analysis_arena.allocator(),
  34650         .code = zir,
  34651         .owner_decl = decl,
  34652         .owner_decl_index = decl_index,
  34653         .func = null,
  34654         .func_index = .none,
  34655         .fn_ret_ty = Type.void,
  34656         .owner_func = null,
  34657         .owner_func_index = .none,
  34658         .comptime_mutable_decls = &comptime_mutable_decls,
  34659     };
  34660     defer sema.deinit();
  34661 
  34662     var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope);
  34663     defer wip_captures.deinit();
  34664 
  34665     var block_scope: Block = .{
  34666         .parent = null,
  34667         .sema = &sema,
  34668         .src_decl = decl_index,
  34669         .namespace = union_obj.namespace,
  34670         .wip_capture_scope = wip_captures.scope,
  34671         .instructions = .{},
  34672         .inlining = null,
  34673         .is_comptime = true,
  34674     };
  34675     defer {
  34676         assert(block_scope.instructions.items.len == 0);
  34677         block_scope.params.deinit(gpa);
  34678     }
  34679 
  34680     if (body.len != 0) {
  34681         try sema.analyzeBody(&block_scope, body);
  34682     }
  34683 
  34684     try wip_captures.finalize();
  34685     for (comptime_mutable_decls.items) |ct_decl_index| {
  34686         const ct_decl = mod.declPtr(ct_decl_index);
  34687         try ct_decl.intern(mod);
  34688     }
  34689 
  34690     try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len);
  34691 
  34692     var int_tag_ty: Type = undefined;
  34693     var enum_field_names: []InternPool.NullTerminatedString = &.{};
  34694     var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{};
  34695     var explicit_tags_seen: []bool = &.{};
  34696     if (tag_type_ref != .none) {
  34697         const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x };
  34698         const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref);
  34699         if (small.auto_enum_tag) {
  34700             // The provided type is an integer type and we must construct the enum tag type here.
  34701             int_tag_ty = provided_ty;
  34702             if (int_tag_ty.zigTypeTag(mod) != .Int and int_tag_ty.zigTypeTag(mod) != .ComptimeInt) {
  34703                 return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{}'", .{int_tag_ty.fmt(mod)});
  34704             }
  34705 
  34706             if (fields_len > 0) {
  34707                 const field_count_val = try mod.intValue(Type.comptime_int, fields_len - 1);
  34708                 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) {
  34709                     const msg = msg: {
  34710                         const msg = try sema.errMsg(&block_scope, tag_ty_src, "specified integer tag type cannot represent every field", .{});
  34711                         errdefer msg.destroy(sema.gpa);
  34712                         try sema.errNote(&block_scope, tag_ty_src, msg, "type '{}' cannot fit values in range 0...{d}", .{
  34713                             int_tag_ty.fmt(mod),
  34714                             fields_len - 1,
  34715                         });
  34716                         break :msg msg;
  34717                     };
  34718                     return sema.failWithOwnedErrorMsg(msg);
  34719                 }
  34720                 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  34721                 try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len);
  34722             }
  34723         } else {
  34724             // The provided type is the enum tag type.
  34725             union_obj.tag_ty = provided_ty;
  34726             const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) {
  34727                 .enum_type => |x| x,
  34728                 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(mod)}),
  34729             };
  34730             // The fields of the union must match the enum exactly.
  34731             // A flag per field is used to check for missing and extraneous fields.
  34732             explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
  34733             @memset(explicit_tags_seen, false);
  34734         }
  34735     } else {
  34736         // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis
  34737         // purposes, we still auto-generate an enum tag type the same way. That the union is
  34738         // untagged is represented by the Type tag (union vs union_tagged).
  34739         enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  34740     }
  34741 
  34742     const bits_per_field = 4;
  34743     const fields_per_u32 = 32 / bits_per_field;
  34744     const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  34745     var bit_bag_index: usize = extra_index;
  34746     extra_index += bit_bags_count;
  34747     var cur_bit_bag: u32 = undefined;
  34748     var field_i: u32 = 0;
  34749     var last_tag_val: ?Value = null;
  34750     while (field_i < fields_len) : (field_i += 1) {
  34751         if (field_i % fields_per_u32 == 0) {
  34752             cur_bit_bag = zir.extra[bit_bag_index];
  34753             bit_bag_index += 1;
  34754         }
  34755         const has_type = @as(u1, @truncate(cur_bit_bag)) != 0;
  34756         cur_bit_bag >>= 1;
  34757         const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  34758         cur_bit_bag >>= 1;
  34759         const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0;
  34760         cur_bit_bag >>= 1;
  34761         const unused = @as(u1, @truncate(cur_bit_bag)) != 0;
  34762         cur_bit_bag >>= 1;
  34763         _ = unused;
  34764 
  34765         const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
  34766         extra_index += 1;
  34767 
  34768         // doc_comment
  34769         extra_index += 1;
  34770 
  34771         const field_type_ref: Zir.Inst.Ref = if (has_type) blk: {
  34772             const field_type_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  34773             extra_index += 1;
  34774             break :blk field_type_ref;
  34775         } else .none;
  34776 
  34777         const align_ref: Zir.Inst.Ref = if (has_align) blk: {
  34778             const align_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  34779             extra_index += 1;
  34780             break :blk align_ref;
  34781         } else .none;
  34782 
  34783         const tag_ref: Air.Inst.Ref = if (has_tag) blk: {
  34784             const tag_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
  34785             extra_index += 1;
  34786             break :blk try sema.resolveInst(tag_ref);
  34787         } else .none;
  34788 
  34789         if (enum_field_vals.capacity() > 0) {
  34790             const enum_tag_val = if (tag_ref != .none) blk: {
  34791                 const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) {
  34792                     error.NeededSourceLocation => {
  34793                         const val_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34794                             .index = field_i,
  34795                             .range = .value,
  34796                         }).lazy;
  34797                         _ = try sema.semaUnionFieldVal(&block_scope, val_src, int_tag_ty, tag_ref);
  34798                         unreachable;
  34799                     },
  34800                     else => |e| return e,
  34801                 };
  34802                 last_tag_val = val;
  34803 
  34804                 break :blk val;
  34805             } else blk: {
  34806                 const val = if (last_tag_val) |val|
  34807                     try sema.intAdd(val, Value.one_comptime_int, int_tag_ty, undefined)
  34808                 else
  34809                     try mod.intValue(int_tag_ty, 0);
  34810                 last_tag_val = val;
  34811 
  34812                 break :blk val;
  34813             };
  34814             const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern());
  34815             if (gop.found_existing) {
  34816                 const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy;
  34817                 const other_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = gop.index }).lazy;
  34818                 const msg = msg: {
  34819                     const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{enum_tag_val.fmtValue(int_tag_ty, mod)});
  34820                     errdefer msg.destroy(gpa);
  34821                     try sema.errNote(&block_scope, other_field_src, msg, "other occurrence here", .{});
  34822                     break :msg msg;
  34823                 };
  34824                 return sema.failWithOwnedErrorMsg(msg);
  34825             }
  34826         }
  34827 
  34828         // This string needs to outlive the ZIR code.
  34829         const field_name = try ip.getOrPutString(gpa, field_name_zir);
  34830         if (enum_field_names.len != 0) {
  34831             enum_field_names[field_i] = field_name;
  34832         }
  34833 
  34834         const field_ty: Type = if (!has_type)
  34835             Type.void
  34836         else if (field_type_ref == .none)
  34837             Type.noreturn
  34838         else
  34839             sema.resolveType(&block_scope, .unneeded, field_type_ref) catch |err| switch (err) {
  34840                 error.NeededSourceLocation => {
  34841                     const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34842                         .index = field_i,
  34843                         .range = .type,
  34844                     }).lazy;
  34845                     _ = try sema.resolveType(&block_scope, ty_src, field_type_ref);
  34846                     unreachable;
  34847                 },
  34848                 else => |e| return e,
  34849             };
  34850 
  34851         if (field_ty.isGenericPoison()) {
  34852             return error.GenericPoison;
  34853         }
  34854 
  34855         const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
  34856         if (gop.found_existing) {
  34857             const msg = msg: {
  34858                 const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy;
  34859                 const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{}'", .{
  34860                     field_name.fmt(ip),
  34861                 });
  34862                 errdefer msg.destroy(gpa);
  34863 
  34864                 const prev_field_index = union_obj.fields.getIndex(field_name).?;
  34865                 const prev_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = prev_field_index }).lazy;
  34866                 try mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl, mod), msg, "other field here", .{});
  34867                 try sema.errNote(&block_scope, src, msg, "union declared here", .{});
  34868                 break :msg msg;
  34869             };
  34870             return sema.failWithOwnedErrorMsg(msg);
  34871         }
  34872 
  34873         if (explicit_tags_seen.len > 0) {
  34874             const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
  34875             const enum_index = tag_info.nameIndex(ip, field_name) orelse {
  34876                 const msg = msg: {
  34877                     const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34878                         .index = field_i,
  34879                         .range = .type,
  34880                     }).lazy;
  34881                     const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{}' in enum '{}'", .{
  34882                         field_name.fmt(ip), union_obj.tag_ty.fmt(mod),
  34883                     });
  34884                     errdefer msg.destroy(sema.gpa);
  34885                     try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
  34886                     break :msg msg;
  34887                 };
  34888                 return sema.failWithOwnedErrorMsg(msg);
  34889             };
  34890             // No check for duplicate because the check already happened in order
  34891             // to create the enum type in the first place.
  34892             assert(!explicit_tags_seen[enum_index]);
  34893             explicit_tags_seen[enum_index] = true;
  34894         }
  34895 
  34896         if (field_ty.zigTypeTag(mod) == .Opaque) {
  34897             const msg = msg: {
  34898                 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34899                     .index = field_i,
  34900                     .range = .type,
  34901                 }).lazy;
  34902                 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
  34903                 errdefer msg.destroy(sema.gpa);
  34904 
  34905                 try sema.addDeclaredHereNote(msg, field_ty);
  34906                 break :msg msg;
  34907             };
  34908             return sema.failWithOwnedErrorMsg(msg);
  34909         }
  34910         if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) {
  34911             const msg = msg: {
  34912                 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34913                     .index = field_i,
  34914                     .range = .type,
  34915                 });
  34916                 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  34917                 errdefer msg.destroy(sema.gpa);
  34918 
  34919                 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .union_field);
  34920 
  34921                 try sema.addDeclaredHereNote(msg, field_ty);
  34922                 break :msg msg;
  34923             };
  34924             return sema.failWithOwnedErrorMsg(msg);
  34925         } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) {
  34926             const msg = msg: {
  34927                 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34928                     .index = field_i,
  34929                     .range = .type,
  34930                 });
  34931                 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
  34932                 errdefer msg.destroy(sema.gpa);
  34933 
  34934                 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty);
  34935 
  34936                 try sema.addDeclaredHereNote(msg, field_ty);
  34937                 break :msg msg;
  34938             };
  34939             return sema.failWithOwnedErrorMsg(msg);
  34940         }
  34941 
  34942         gop.value_ptr.* = .{
  34943             .ty = field_ty,
  34944             .abi_align = .none,
  34945         };
  34946 
  34947         if (align_ref != .none) {
  34948             gop.value_ptr.abi_align = sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) {
  34949                 error.NeededSourceLocation => {
  34950                     const align_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
  34951                         .index = field_i,
  34952                         .range = .alignment,
  34953                     }).lazy;
  34954                     _ = try sema.resolveAlign(&block_scope, align_src, align_ref);
  34955                     unreachable;
  34956                 },
  34957                 else => |e| return e,
  34958             };
  34959         } else {
  34960             gop.value_ptr.abi_align = .none;
  34961         }
  34962     }
  34963 
  34964     if (explicit_tags_seen.len > 0) {
  34965         const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
  34966         if (tag_info.names.len > fields_len) {
  34967             const msg = msg: {
  34968                 const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{});
  34969                 errdefer msg.destroy(sema.gpa);
  34970 
  34971                 const enum_ty = union_obj.tag_ty;
  34972                 for (tag_info.names, 0..) |field_name, field_index| {
  34973                     if (explicit_tags_seen[field_index]) continue;
  34974                     try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{
  34975                         field_name.fmt(ip),
  34976                     });
  34977                 }
  34978                 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
  34979                 break :msg msg;
  34980             };
  34981             return sema.failWithOwnedErrorMsg(msg);
  34982         }
  34983     } else if (enum_field_vals.count() > 0) {
  34984         union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_obj);
  34985     } else {
  34986         union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_obj);
  34987     }
  34988 }
  34989 
  34990 fn semaUnionFieldVal(sema: *Sema, block: *Block, src: LazySrcLoc, int_tag_ty: Type, tag_ref: Air.Inst.Ref) CompileError!Value {
  34991     const coerced = try sema.coerce(block, int_tag_ty, tag_ref, src);
  34992     return sema.resolveConstValue(block, src, coerced, "enum tag value must be comptime-known");
  34993 }
  34994 
  34995 fn generateUnionTagTypeNumbered(
  34996     sema: *Sema,
  34997     block: *Block,
  34998     enum_field_names: []const InternPool.NullTerminatedString,
  34999     enum_field_vals: []const InternPool.Index,
  35000     union_obj: *Module.Union,
  35001 ) !Type {
  35002     const mod = sema.mod;
  35003     const gpa = sema.gpa;
  35004 
  35005     const src_decl = mod.declPtr(block.src_decl);
  35006     const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope);
  35007     errdefer mod.destroyDecl(new_decl_index);
  35008     const fqn = try union_obj.getFullyQualifiedName(mod);
  35009     const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)});
  35010     try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{
  35011         .ty = Type.noreturn,
  35012         .val = Value.@"unreachable",
  35013     }, name);
  35014     errdefer mod.abortAnonDecl(new_decl_index);
  35015 
  35016     const new_decl = mod.declPtr(new_decl_index);
  35017     new_decl.name_fully_qualified = true;
  35018     new_decl.owns_tv = true;
  35019     new_decl.name_fully_qualified = true;
  35020 
  35021     const enum_ty = try mod.intern(.{ .enum_type = .{
  35022         .decl = new_decl_index,
  35023         .namespace = .none,
  35024         .tag_ty = if (enum_field_vals.len == 0)
  35025             (try mod.intType(.unsigned, 0)).toIntern()
  35026         else
  35027             mod.intern_pool.typeOf(enum_field_vals[0]),
  35028         .names = enum_field_names,
  35029         .values = enum_field_vals,
  35030         .tag_mode = .explicit,
  35031     } });
  35032 
  35033     new_decl.ty = Type.type;
  35034     new_decl.val = enum_ty.toValue();
  35035 
  35036     try mod.finalizeAnonDecl(new_decl_index);
  35037     return enum_ty.toType();
  35038 }
  35039 
  35040 fn generateUnionTagTypeSimple(
  35041     sema: *Sema,
  35042     block: *Block,
  35043     enum_field_names: []const InternPool.NullTerminatedString,
  35044     maybe_union_obj: ?*Module.Union,
  35045 ) !Type {
  35046     const mod = sema.mod;
  35047     const gpa = sema.gpa;
  35048 
  35049     const new_decl_index = new_decl_index: {
  35050         const union_obj = maybe_union_obj orelse {
  35051             break :new_decl_index try mod.createAnonymousDecl(block, .{
  35052                 .ty = Type.noreturn,
  35053                 .val = Value.@"unreachable",
  35054             });
  35055         };
  35056         const src_decl = mod.declPtr(block.src_decl);
  35057         const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope);
  35058         errdefer mod.destroyDecl(new_decl_index);
  35059         const fqn = try union_obj.getFullyQualifiedName(mod);
  35060         const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)});
  35061         try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{
  35062             .ty = Type.noreturn,
  35063             .val = Value.@"unreachable",
  35064         }, name);
  35065         mod.declPtr(new_decl_index).name_fully_qualified = true;
  35066         break :new_decl_index new_decl_index;
  35067     };
  35068     errdefer mod.abortAnonDecl(new_decl_index);
  35069 
  35070     const enum_ty = try mod.intern(.{ .enum_type = .{
  35071         .decl = new_decl_index,
  35072         .namespace = .none,
  35073         .tag_ty = if (enum_field_names.len == 0)
  35074             (try mod.intType(.unsigned, 0)).toIntern()
  35075         else
  35076             (try mod.smallestUnsignedInt(enum_field_names.len - 1)).toIntern(),
  35077         .names = enum_field_names,
  35078         .values = &.{},
  35079         .tag_mode = .auto,
  35080     } });
  35081 
  35082     const new_decl = mod.declPtr(new_decl_index);
  35083     new_decl.owns_tv = true;
  35084     new_decl.ty = Type.type;
  35085     new_decl.val = enum_ty.toValue();
  35086 
  35087     try mod.finalizeAnonDecl(new_decl_index);
  35088     return enum_ty.toType();
  35089 }
  35090 
  35091 fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref {
  35092     const gpa = sema.gpa;
  35093     const src = LazySrcLoc.nodeOffset(0);
  35094 
  35095     var wip_captures = try WipCaptureScope.init(gpa, sema.owner_decl.src_scope);
  35096     defer wip_captures.deinit();
  35097 
  35098     var block: Block = .{
  35099         .parent = null,
  35100         .sema = sema,
  35101         .src_decl = sema.owner_decl_index,
  35102         .namespace = sema.owner_decl.src_namespace,
  35103         .wip_capture_scope = wip_captures.scope,
  35104         .instructions = .{},
  35105         .inlining = null,
  35106         .is_comptime = true,
  35107     };
  35108     defer {
  35109         block.instructions.deinit(gpa);
  35110         block.params.deinit(gpa);
  35111     }
  35112 
  35113     const decl_index = try getBuiltinDecl(sema, &block, name);
  35114     return sema.analyzeDeclVal(&block, src, decl_index);
  35115 }
  35116 
  35117 fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!Module.Decl.Index {
  35118     const gpa = sema.gpa;
  35119 
  35120     const src = LazySrcLoc.nodeOffset(0);
  35121 
  35122     const mod = sema.mod;
  35123     const ip = &mod.intern_pool;
  35124     const std_pkg = mod.main_pkg.table.get("std").?;
  35125     const std_file = (mod.importPkg(std_pkg) catch unreachable).file;
  35126     const opt_builtin_inst = (try sema.namespaceLookupRef(
  35127         block,
  35128         src,
  35129         mod.declPtr(std_file.root_decl.unwrap().?).src_namespace,
  35130         try ip.getOrPutString(gpa, "builtin"),
  35131     )) orelse @panic("lib/std.zig is corrupt and missing 'builtin'");
  35132     const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst, src);
  35133     const builtin_ty = sema.analyzeAsType(block, src, builtin_inst) catch |err| switch (err) {
  35134         error.AnalysisFail => std.debug.panic("std.builtin is corrupt", .{}),
  35135         else => |e| return e,
  35136     };
  35137     const decl_index = (try sema.namespaceLookup(
  35138         block,
  35139         src,
  35140         builtin_ty.getNamespaceIndex(mod).unwrap().?,
  35141         try ip.getOrPutString(gpa, name),
  35142     )) orelse std.debug.panic("lib/std/builtin.zig is corrupt and missing '{s}'", .{name});
  35143     return decl_index;
  35144 }
  35145 
  35146 fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type {
  35147     const ty_inst = try sema.getBuiltin(name);
  35148 
  35149     var wip_captures = try WipCaptureScope.init(sema.gpa, sema.owner_decl.src_scope);
  35150     defer wip_captures.deinit();
  35151 
  35152     var block: Block = .{
  35153         .parent = null,
  35154         .sema = sema,
  35155         .src_decl = sema.owner_decl_index,
  35156         .namespace = sema.owner_decl.src_namespace,
  35157         .wip_capture_scope = wip_captures.scope,
  35158         .instructions = .{},
  35159         .inlining = null,
  35160         .is_comptime = true,
  35161     };
  35162     defer {
  35163         block.instructions.deinit(sema.gpa);
  35164         block.params.deinit(sema.gpa);
  35165     }
  35166     const src = LazySrcLoc.nodeOffset(0);
  35167 
  35168     const result_ty = sema.analyzeAsType(&block, src, ty_inst) catch |err| switch (err) {
  35169         error.AnalysisFail => std.debug.panic("std.builtin.{s} is corrupt", .{name}),
  35170         else => |e| return e,
  35171     };
  35172     try sema.resolveTypeFully(result_ty); // Should not fail
  35173     return result_ty;
  35174 }
  35175 
  35176 /// There is another implementation of this in `Type.onePossibleValue`. This one
  35177 /// in `Sema` is for calling during semantic analysis, and performs field resolution
  35178 /// to get the answer. The one in `Type` is for calling during codegen and asserts
  35179 /// that the types are already resolved.
  35180 /// TODO assert the return value matches `ty.onePossibleValue`
  35181 pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
  35182     const mod = sema.mod;
  35183     return switch (ty.toIntern()) {
  35184         .u0_type,
  35185         .i0_type,
  35186         => try mod.intValue(ty, 0),
  35187         .u1_type,
  35188         .u8_type,
  35189         .i8_type,
  35190         .u16_type,
  35191         .i16_type,
  35192         .u29_type,
  35193         .u32_type,
  35194         .i32_type,
  35195         .u64_type,
  35196         .i64_type,
  35197         .u80_type,
  35198         .u128_type,
  35199         .i128_type,
  35200         .usize_type,
  35201         .isize_type,
  35202         .c_char_type,
  35203         .c_short_type,
  35204         .c_ushort_type,
  35205         .c_int_type,
  35206         .c_uint_type,
  35207         .c_long_type,
  35208         .c_ulong_type,
  35209         .c_longlong_type,
  35210         .c_ulonglong_type,
  35211         .c_longdouble_type,
  35212         .f16_type,
  35213         .f32_type,
  35214         .f64_type,
  35215         .f80_type,
  35216         .f128_type,
  35217         .anyopaque_type,
  35218         .bool_type,
  35219         .type_type,
  35220         .anyerror_type,
  35221         .comptime_int_type,
  35222         .comptime_float_type,
  35223         .enum_literal_type,
  35224         .atomic_order_type,
  35225         .atomic_rmw_op_type,
  35226         .calling_convention_type,
  35227         .address_space_type,
  35228         .float_mode_type,
  35229         .reduce_op_type,
  35230         .call_modifier_type,
  35231         .prefetch_options_type,
  35232         .export_options_type,
  35233         .extern_options_type,
  35234         .type_info_type,
  35235         .manyptr_u8_type,
  35236         .manyptr_const_u8_type,
  35237         .manyptr_const_u8_sentinel_0_type,
  35238         .single_const_pointer_to_comptime_int_type,
  35239         .slice_const_u8_type,
  35240         .slice_const_u8_sentinel_0_type,
  35241         .anyerror_void_error_union_type,
  35242         => null,
  35243         .void_type => Value.void,
  35244         .noreturn_type => Value.@"unreachable",
  35245         .anyframe_type => unreachable,
  35246         .null_type => Value.null,
  35247         .undefined_type => Value.undef,
  35248         .optional_noreturn_type => try mod.nullValue(ty),
  35249         .generic_poison_type => error.GenericPoison,
  35250         .empty_struct_type => Value.empty_struct,
  35251         // values, not types
  35252         .undef,
  35253         .zero,
  35254         .zero_usize,
  35255         .zero_u8,
  35256         .one,
  35257         .one_usize,
  35258         .one_u8,
  35259         .four_u8,
  35260         .negative_one,
  35261         .calling_convention_c,
  35262         .calling_convention_inline,
  35263         .void_value,
  35264         .unreachable_value,
  35265         .null_value,
  35266         .bool_true,
  35267         .bool_false,
  35268         .empty_struct,
  35269         .generic_poison,
  35270         // invalid
  35271         .var_args_param_type,
  35272         .none,
  35273         => unreachable,
  35274         _ => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) {
  35275             .type_int_signed, // i0 handled above
  35276             .type_int_unsigned, // u0 handled above
  35277             .type_pointer,
  35278             .type_slice,
  35279             .type_optional, // ?noreturn handled above
  35280             .type_anyframe,
  35281             .type_error_union,
  35282             .type_error_set,
  35283             .type_inferred_error_set,
  35284             .type_opaque,
  35285             .type_function,
  35286             => null,
  35287             .simple_type, // handled above
  35288             // values, not types
  35289             .undef,
  35290             .runtime_value,
  35291             .simple_value,
  35292             .ptr_decl,
  35293             .ptr_mut_decl,
  35294             .ptr_comptime_field,
  35295             .ptr_int,
  35296             .ptr_eu_payload,
  35297             .ptr_opt_payload,
  35298             .ptr_elem,
  35299             .ptr_field,
  35300             .ptr_slice,
  35301             .opt_payload,
  35302             .opt_null,
  35303             .int_u8,
  35304             .int_u16,
  35305             .int_u32,
  35306             .int_i32,
  35307             .int_usize,
  35308             .int_comptime_int_u32,
  35309             .int_comptime_int_i32,
  35310             .int_small,
  35311             .int_positive,
  35312             .int_negative,
  35313             .int_lazy_align,
  35314             .int_lazy_size,
  35315             .error_set_error,
  35316             .error_union_error,
  35317             .error_union_payload,
  35318             .enum_literal,
  35319             .enum_tag,
  35320             .float_f16,
  35321             .float_f32,
  35322             .float_f64,
  35323             .float_f80,
  35324             .float_f128,
  35325             .float_c_longdouble_f80,
  35326             .float_c_longdouble_f128,
  35327             .float_comptime_float,
  35328             .variable,
  35329             .extern_func,
  35330             .func,
  35331             .only_possible_value,
  35332             .union_value,
  35333             .bytes,
  35334             .aggregate,
  35335             .repeated,
  35336             // memoized value, not types
  35337             .memoized_call,
  35338             => unreachable,
  35339             .type_array_big,
  35340             .type_array_small,
  35341             .type_vector,
  35342             .type_enum_auto,
  35343             .type_enum_explicit,
  35344             .type_enum_nonexhaustive,
  35345             .type_struct,
  35346             .type_struct_ns,
  35347             .type_struct_anon,
  35348             .type_tuple_anon,
  35349             .type_union_tagged,
  35350             .type_union_untagged,
  35351             .type_union_safety,
  35352             => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  35353                 inline .array_type, .vector_type => |seq_type, seq_tag| {
  35354                     const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none;
  35355                     if (seq_type.len + @intFromBool(has_sentinel) == 0) return (try mod.intern(.{ .aggregate = .{
  35356                         .ty = ty.toIntern(),
  35357                         .storage = .{ .elems = &.{} },
  35358                     } })).toValue();
  35359 
  35360                     if (try sema.typeHasOnePossibleValue(seq_type.child.toType())) |opv| {
  35361                         return (try mod.intern(.{ .aggregate = .{
  35362                             .ty = ty.toIntern(),
  35363                             .storage = .{ .repeated_elem = opv.toIntern() },
  35364                         } })).toValue();
  35365                     }
  35366                     return null;
  35367                 },
  35368 
  35369                 .struct_type => |struct_type| {
  35370                     const resolved_ty = try sema.resolveTypeFields(ty);
  35371                     if (mod.structPtrUnwrap(struct_type.index)) |s| {
  35372                         const field_vals = try sema.arena.alloc(InternPool.Index, s.fields.count());
  35373                         for (field_vals, s.fields.values(), 0..) |*field_val, field, i| {
  35374                             if (field.is_comptime) {
  35375                                 field_val.* = field.default_val;
  35376                                 continue;
  35377                             }
  35378                             if (field.ty.eql(resolved_ty, sema.mod)) {
  35379                                 const msg = try Module.ErrorMsg.create(
  35380                                     sema.gpa,
  35381                                     s.srcLoc(sema.mod),
  35382                                     "struct '{}' depends on itself",
  35383                                     .{ty.fmt(sema.mod)},
  35384                                 );
  35385                                 try sema.addFieldErrNote(resolved_ty, i, msg, "while checking this field", .{});
  35386                                 return sema.failWithOwnedErrorMsg(msg);
  35387                             }
  35388                             if (try sema.typeHasOnePossibleValue(field.ty)) |field_opv| {
  35389                                 field_val.* = try field_opv.intern(field.ty, mod);
  35390                             } else return null;
  35391                         }
  35392 
  35393                         // In this case the struct has no runtime-known fields and
  35394                         // therefore has one possible value.
  35395                         return (try mod.intern(.{ .aggregate = .{
  35396                             .ty = ty.toIntern(),
  35397                             .storage = .{ .elems = field_vals },
  35398                         } })).toValue();
  35399                     }
  35400 
  35401                     // In this case the struct has no fields at all and
  35402                     // therefore has one possible value.
  35403                     return (try mod.intern(.{ .aggregate = .{
  35404                         .ty = ty.toIntern(),
  35405                         .storage = .{ .elems = &.{} },
  35406                     } })).toValue();
  35407                 },
  35408 
  35409                 .anon_struct_type => |tuple| {
  35410                     for (tuple.values) |val| {
  35411                         if (val == .none) return null;
  35412                     }
  35413                     // In this case the struct has all comptime-known fields and
  35414                     // therefore has one possible value.
  35415                     // TODO: write something like getCoercedInts to avoid needing to dupe
  35416                     return (try mod.intern(.{ .aggregate = .{
  35417                         .ty = ty.toIntern(),
  35418                         .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values) },
  35419                     } })).toValue();
  35420                 },
  35421 
  35422                 .union_type => |union_type| {
  35423                     const resolved_ty = try sema.resolveTypeFields(ty);
  35424                     const union_obj = mod.unionPtr(union_type.index);
  35425                     const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse
  35426                         return null;
  35427                     const fields = union_obj.fields.values();
  35428                     if (fields.len == 0) {
  35429                         const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
  35430                         return only.toValue();
  35431                     }
  35432                     const only_field = fields[0];
  35433                     if (only_field.ty.eql(resolved_ty, sema.mod)) {
  35434                         const msg = try Module.ErrorMsg.create(
  35435                             sema.gpa,
  35436                             union_obj.srcLoc(sema.mod),
  35437                             "union '{}' depends on itself",
  35438                             .{ty.fmt(sema.mod)},
  35439                         );
  35440                         try sema.addFieldErrNote(resolved_ty, 0, msg, "while checking this field", .{});
  35441                         return sema.failWithOwnedErrorMsg(msg);
  35442                     }
  35443                     const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse
  35444                         return null;
  35445                     const only = try mod.intern(.{ .un = .{
  35446                         .ty = resolved_ty.toIntern(),
  35447                         .tag = tag_val.toIntern(),
  35448                         .val = val_val.toIntern(),
  35449                     } });
  35450                     return only.toValue();
  35451                 },
  35452 
  35453                 .enum_type => |enum_type| switch (enum_type.tag_mode) {
  35454                     .nonexhaustive => {
  35455                         if (enum_type.tag_ty == .comptime_int_type) return null;
  35456 
  35457                         if (try sema.typeHasOnePossibleValue(enum_type.tag_ty.toType())) |int_opv| {
  35458                             const only = try mod.intern(.{ .enum_tag = .{
  35459                                 .ty = ty.toIntern(),
  35460                                 .int = int_opv.toIntern(),
  35461                             } });
  35462                             return only.toValue();
  35463                         }
  35464 
  35465                         return null;
  35466                     },
  35467                     .auto, .explicit => {
  35468                         if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null;
  35469 
  35470                         switch (enum_type.names.len) {
  35471                             0 => {
  35472                                 const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
  35473                                 return only.toValue();
  35474                             },
  35475                             1 => return try mod.getCoerced((if (enum_type.values.len == 0)
  35476                                 try mod.intern(.{ .int = .{
  35477                                     .ty = enum_type.tag_ty,
  35478                                     .storage = .{ .u64 = 0 },
  35479                                 } })
  35480                             else
  35481                                 enum_type.values[0]).toValue(), ty),
  35482                             else => return null,
  35483                         }
  35484                     },
  35485                 },
  35486 
  35487                 else => unreachable,
  35488             },
  35489         },
  35490     };
  35491 }
  35492 
  35493 /// Returns the type of the AIR instruction.
  35494 fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
  35495     return sema.getTmpAir().typeOf(inst, &sema.mod.intern_pool);
  35496 }
  35497 
  35498 pub fn getTmpAir(sema: Sema) Air {
  35499     return .{
  35500         .instructions = sema.air_instructions.slice(),
  35501         .extra = sema.air_extra.items,
  35502     };
  35503 }
  35504 
  35505 // TODO: make this non-fallible or remove it entirely
  35506 pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
  35507     _ = sema;
  35508     return Air.internedToRef(ty.toIntern());
  35509 }
  35510 
  35511 fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
  35512     const mod = sema.mod;
  35513     return sema.addConstant(try mod.intValue(ty, int));
  35514 }
  35515 
  35516 fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
  35517     return sema.addConstant((try sema.mod.intern(.{ .undef = ty.toIntern() })).toValue());
  35518 }
  35519 
  35520 // TODO: make this non-fallible or remove it entirely
  35521 pub fn addConstant(sema: *Sema, val: Value) !Air.Inst.Ref {
  35522     _ = sema;
  35523     return Air.internedToRef(val.toIntern());
  35524 }
  35525 
  35526 pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {
  35527     const fields = std.meta.fields(@TypeOf(extra));
  35528     try sema.air_extra.ensureUnusedCapacity(sema.gpa, fields.len);
  35529     return sema.addExtraAssumeCapacity(extra);
  35530 }
  35531 
  35532 pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {
  35533     const fields = std.meta.fields(@TypeOf(extra));
  35534     const result = @as(u32, @intCast(sema.air_extra.items.len));
  35535     inline for (fields) |field| {
  35536         sema.air_extra.appendAssumeCapacity(switch (field.type) {
  35537             u32 => @field(extra, field.name),
  35538             Air.Inst.Ref => @intFromEnum(@field(extra, field.name)),
  35539             i32 => @as(u32, @bitCast(@field(extra, field.name))),
  35540             InternPool.Index => @intFromEnum(@field(extra, field.name)),
  35541             else => @compileError("bad field type: " ++ @typeName(field.type)),
  35542         });
  35543     }
  35544     return result;
  35545 }
  35546 
  35547 fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void {
  35548     const coerced = @as([]const u32, @ptrCast(refs));
  35549     sema.air_extra.appendSliceAssumeCapacity(coerced);
  35550 }
  35551 
  35552 fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index {
  35553     const air_datas = sema.air_instructions.items(.data);
  35554     const air_tags = sema.air_instructions.items(.tag);
  35555     switch (air_tags[inst_index]) {
  35556         .br => return air_datas[inst_index].br.block_inst,
  35557         else => return null,
  35558     }
  35559 }
  35560 
  35561 fn isComptimeKnown(
  35562     sema: *Sema,
  35563     inst: Air.Inst.Ref,
  35564 ) !bool {
  35565     return (try sema.resolveMaybeUndefVal(inst)) != null;
  35566 }
  35567 
  35568 fn analyzeComptimeAlloc(
  35569     sema: *Sema,
  35570     block: *Block,
  35571     var_type: Type,
  35572     alignment: Alignment,
  35573 ) CompileError!Air.Inst.Ref {
  35574     const mod = sema.mod;
  35575 
  35576     // Needed to make an anon decl with type `var_type` (the `finish()` call below).
  35577     _ = try sema.typeHasOnePossibleValue(var_type);
  35578 
  35579     const ptr_type = try mod.ptrType(.{
  35580         .child = var_type.toIntern(),
  35581         .flags = .{
  35582             .alignment = alignment,
  35583             .address_space = target_util.defaultAddressSpace(mod.getTarget(), .global_constant),
  35584         },
  35585     });
  35586 
  35587     var anon_decl = try block.startAnonDecl();
  35588     defer anon_decl.deinit();
  35589 
  35590     const decl_index = try anon_decl.finish(
  35591         var_type,
  35592         // There will be stores before the first load, but they may be to sub-elements or
  35593         // sub-fields. So we need to initialize with undef to allow the mechanism to expand
  35594         // into fields/elements and have those overridden with stored values.
  35595         (try mod.intern(.{ .undef = var_type.toIntern() })).toValue(),
  35596         alignment,
  35597     );
  35598     const decl = mod.declPtr(decl_index);
  35599     decl.alignment = alignment;
  35600 
  35601     try sema.comptime_mutable_decls.append(decl_index);
  35602     try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
  35603     return sema.addConstant((try mod.intern(.{ .ptr = .{
  35604         .ty = ptr_type.toIntern(),
  35605         .addr = .{ .mut_decl = .{
  35606             .decl = decl_index,
  35607             .runtime_index = block.runtime_index,
  35608         } },
  35609     } })).toValue());
  35610 }
  35611 
  35612 /// The places where a user can specify an address space attribute
  35613 pub const AddressSpaceContext = enum {
  35614     /// A function is specified to be placed in a certain address space.
  35615     function,
  35616 
  35617     /// A (global) variable is specified to be placed in a certain address space.
  35618     /// In contrast to .constant, these values (and thus the address space they will be
  35619     /// placed in) are required to be mutable.
  35620     variable,
  35621 
  35622     /// A (global) constant value is specified to be placed in a certain address space.
  35623     /// In contrast to .variable, values placed in this address space are not required to be mutable.
  35624     constant,
  35625 
  35626     /// A pointer is ascripted to point into a certain address space.
  35627     pointer,
  35628 };
  35629 
  35630 pub fn analyzeAddressSpace(
  35631     sema: *Sema,
  35632     block: *Block,
  35633     src: LazySrcLoc,
  35634     zir_ref: Zir.Inst.Ref,
  35635     ctx: AddressSpaceContext,
  35636 ) !std.builtin.AddressSpace {
  35637     const mod = sema.mod;
  35638     const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref, "addresspace must be comptime-known");
  35639     const address_space = mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val);
  35640     const target = sema.mod.getTarget();
  35641     const arch = target.cpu.arch;
  35642 
  35643     const is_nv = arch == .nvptx or arch == .nvptx64;
  35644     const is_amd = arch == .amdgcn;
  35645     const is_spirv = arch == .spirv32 or arch == .spirv64;
  35646     const is_gpu = is_nv or is_amd or is_spirv;
  35647 
  35648     const supported = switch (address_space) {
  35649         // TODO: on spir-v only when os is opencl.
  35650         .generic => true,
  35651         .gs, .fs, .ss => (arch == .x86 or arch == .x86_64) and ctx == .pointer,
  35652         // TODO: check that .shared and .local are left uninitialized
  35653         .param => is_nv,
  35654         .global, .shared, .local => is_gpu,
  35655         .constant => is_gpu and (ctx == .constant),
  35656         // TODO this should also check how many flash banks the cpu has
  35657         .flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr,
  35658     };
  35659 
  35660     if (!supported) {
  35661         // TODO error messages could be made more elaborate here
  35662         const entity = switch (ctx) {
  35663             .function => "functions",
  35664             .variable => "mutable values",
  35665             .constant => "constant values",
  35666             .pointer => "pointers",
  35667         };
  35668         return sema.fail(
  35669             block,
  35670             src,
  35671             "{s} with address space '{s}' are not supported on {s}",
  35672             .{ entity, @tagName(address_space), arch.genericName() },
  35673         );
  35674     }
  35675 
  35676     return address_space;
  35677 }
  35678 
  35679 /// Asserts the value is a pointer and dereferences it.
  35680 /// Returns `null` if the pointer contents cannot be loaded at comptime.
  35681 fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
  35682     const mod = sema.mod;
  35683     const load_ty = ptr_ty.childType(mod);
  35684     const res = try sema.pointerDerefExtra(block, src, ptr_val, load_ty);
  35685     switch (res) {
  35686         .runtime_load => return null,
  35687         .val => |v| return v,
  35688         .needed_well_defined => |ty| return sema.fail(
  35689             block,
  35690             src,
  35691             "comptime dereference requires '{}' to have a well-defined layout, but it does not.",
  35692             .{ty.fmt(sema.mod)},
  35693         ),
  35694         .out_of_bounds => |ty| return sema.fail(
  35695             block,
  35696             src,
  35697             "dereference of '{}' exceeds bounds of containing decl of type '{}'",
  35698             .{ ptr_ty.fmt(sema.mod), ty.fmt(sema.mod) },
  35699         ),
  35700     }
  35701 }
  35702 
  35703 const DerefResult = union(enum) {
  35704     runtime_load,
  35705     val: Value,
  35706     needed_well_defined: Type,
  35707     out_of_bounds: Type,
  35708 };
  35709 
  35710 fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, load_ty: Type) CompileError!DerefResult {
  35711     const mod = sema.mod;
  35712     const target = mod.getTarget();
  35713     const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) {
  35714         error.RuntimeLoad => return DerefResult{ .runtime_load = {} },
  35715         else => |e| return e,
  35716     };
  35717 
  35718     if (deref.pointee) |tv| {
  35719         const coerce_in_mem_ok =
  35720             (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or
  35721             (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok;
  35722         if (coerce_in_mem_ok) {
  35723             // We have a Value that lines up in virtual memory exactly with what we want to load,
  35724             // and it is in-memory coercible to load_ty. It may be returned without modifications.
  35725             // Move mutable decl values to the InternPool and assert other decls are already in
  35726             // the InternPool.
  35727             const uncoerced_val = if (deref.is_mutable) try tv.val.intern(tv.ty, mod) else tv.val.toIntern();
  35728             const coerced_val = try mod.getCoerced(uncoerced_val.toValue(), load_ty);
  35729             return .{ .val = coerced_val };
  35730         }
  35731     }
  35732 
  35733     // The type is not in-memory coercible or the direct dereference failed, so it must
  35734     // be bitcast according to the pointer type we are performing the load through.
  35735     if (!load_ty.hasWellDefinedLayout(mod)) {
  35736         return DerefResult{ .needed_well_defined = load_ty };
  35737     }
  35738 
  35739     const load_sz = try sema.typeAbiSize(load_ty);
  35740 
  35741     // Try the smaller bit-cast first, since that's more efficient than using the larger `parent`
  35742     if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(tv.ty))
  35743         return DerefResult{ .val = (try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0)) orelse return .runtime_load };
  35744 
  35745     // If that fails, try to bit-cast from the largest parent value with a well-defined layout
  35746     if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(parent.tv.ty))
  35747         return DerefResult{ .val = (try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset)) orelse return .runtime_load };
  35748 
  35749     if (deref.ty_without_well_defined_layout) |bad_ty| {
  35750         // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
  35751         // is that some type we encountered when de-referencing does not have a well-defined layout.
  35752         return DerefResult{ .needed_well_defined = bad_ty };
  35753     } else {
  35754         // If all encountered types had well-defined layouts, the parent is the root decl and it just
  35755         // wasn't big enough for the load.
  35756         return DerefResult{ .out_of_bounds = deref.parent.?.tv.ty };
  35757     }
  35758 }
  35759 
  35760 /// Used to convert a u64 value to a usize value, emitting a compile error if the number
  35761 /// is too big to fit.
  35762 fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize {
  35763     if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int;
  35764     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});
  35765 }
  35766 
  35767 /// For pointer-like optionals, it returns the pointer type. For pointers,
  35768 /// the type is returned unmodified.
  35769 /// This can return `error.AnalysisFail` because it sometimes requires resolving whether
  35770 /// a type has zero bits, which can cause a "foo depends on itself" compile error.
  35771 /// This logic must be kept in sync with `Type.isPtrLikeOptional`.
  35772 fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type {
  35773     const mod = sema.mod;
  35774     return switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  35775         .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  35776             .One, .Many, .C => ty,
  35777             .Slice => null,
  35778         },
  35779         .opt_type => |opt_child| switch (mod.intern_pool.indexToKey(opt_child)) {
  35780             .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  35781                 .Slice, .C => null,
  35782                 .Many, .One => {
  35783                     if (ptr_type.flags.is_allowzero) return null;
  35784 
  35785                     // optionals of zero sized types behave like bools, not pointers
  35786                     const payload_ty = opt_child.toType();
  35787                     if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) {
  35788                         return null;
  35789                     }
  35790 
  35791                     return payload_ty;
  35792                 },
  35793             },
  35794             else => null,
  35795         },
  35796         else => null,
  35797     };
  35798 }
  35799 
  35800 /// `generic_poison` will return false.
  35801 /// This function returns false negatives when structs and unions are having their
  35802 /// field types resolved.
  35803 /// TODO assert the return value matches `ty.comptimeOnly`
  35804 /// TODO merge these implementations together with the "advanced"/opt_sema pattern seen
  35805 /// elsewhere in value.zig
  35806 pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
  35807     const mod = sema.mod;
  35808     return switch (ty.toIntern()) {
  35809         .empty_struct_type => false,
  35810 
  35811         else => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
  35812             .int_type => return false,
  35813             .ptr_type => |ptr_type| {
  35814                 const child_ty = ptr_type.child.toType();
  35815                 if (child_ty.zigTypeTag(mod) == .Fn) {
  35816                     return mod.typeToFunc(child_ty).?.is_generic;
  35817                 } else {
  35818                     return sema.typeRequiresComptime(child_ty);
  35819                 }
  35820             },
  35821             .anyframe_type => |child| {
  35822                 if (child == .none) return false;
  35823                 return sema.typeRequiresComptime(child.toType());
  35824             },
  35825             .array_type => |array_type| return sema.typeRequiresComptime(array_type.child.toType()),
  35826             .vector_type => |vector_type| return sema.typeRequiresComptime(vector_type.child.toType()),
  35827             .opt_type => |child| return sema.typeRequiresComptime(child.toType()),
  35828 
  35829             .error_union_type => |error_union_type| {
  35830                 return sema.typeRequiresComptime(error_union_type.payload_type.toType());
  35831             },
  35832 
  35833             .error_set_type, .inferred_error_set_type => false,
  35834 
  35835             .func_type => true,
  35836 
  35837             .simple_type => |t| return switch (t) {
  35838                 .f16,
  35839                 .f32,
  35840                 .f64,
  35841                 .f80,
  35842                 .f128,
  35843                 .usize,
  35844                 .isize,
  35845                 .c_char,
  35846                 .c_short,
  35847                 .c_ushort,
  35848                 .c_int,
  35849                 .c_uint,
  35850                 .c_long,
  35851                 .c_ulong,
  35852                 .c_longlong,
  35853                 .c_ulonglong,
  35854                 .c_longdouble,
  35855                 .anyopaque,
  35856                 .bool,
  35857                 .void,
  35858                 .anyerror,
  35859                 .noreturn,
  35860                 .generic_poison,
  35861                 .atomic_order,
  35862                 .atomic_rmw_op,
  35863                 .calling_convention,
  35864                 .address_space,
  35865                 .float_mode,
  35866                 .reduce_op,
  35867                 .call_modifier,
  35868                 .prefetch_options,
  35869                 .export_options,
  35870                 .extern_options,
  35871                 => false,
  35872 
  35873                 .type,
  35874                 .comptime_int,
  35875                 .comptime_float,
  35876                 .null,
  35877                 .undefined,
  35878                 .enum_literal,
  35879                 .type_info,
  35880                 => true,
  35881             },
  35882             .struct_type => |struct_type| {
  35883                 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false;
  35884                 switch (struct_obj.requires_comptime) {
  35885                     .no, .wip => return false,
  35886                     .yes => return true,
  35887                     .unknown => {
  35888                         if (struct_obj.status == .field_types_wip)
  35889                             return false;
  35890 
  35891                         try sema.resolveTypeFieldsStruct(ty, struct_obj);
  35892 
  35893                         struct_obj.requires_comptime = .wip;
  35894                         for (struct_obj.fields.values()) |field| {
  35895                             if (field.is_comptime) continue;
  35896                             if (try sema.typeRequiresComptime(field.ty)) {
  35897                                 struct_obj.requires_comptime = .yes;
  35898                                 return true;
  35899                             }
  35900                         }
  35901                         struct_obj.requires_comptime = .no;
  35902                         return false;
  35903                     },
  35904                 }
  35905             },
  35906             .anon_struct_type => |tuple| {
  35907                 for (tuple.types, tuple.values) |field_ty, val| {
  35908                     const have_comptime_val = val != .none;
  35909                     if (!have_comptime_val and try sema.typeRequiresComptime(field_ty.toType())) {
  35910                         return true;
  35911                     }
  35912                 }
  35913                 return false;
  35914             },
  35915 
  35916             .union_type => |union_type| {
  35917                 const union_obj = mod.unionPtr(union_type.index);
  35918                 switch (union_obj.requires_comptime) {
  35919                     .no, .wip => return false,
  35920                     .yes => return true,
  35921                     .unknown => {
  35922                         if (union_obj.status == .field_types_wip)
  35923                             return false;
  35924 
  35925                         try sema.resolveTypeFieldsUnion(ty, union_obj);
  35926 
  35927                         union_obj.requires_comptime = .wip;
  35928                         for (union_obj.fields.values()) |field| {
  35929                             if (try sema.typeRequiresComptime(field.ty)) {
  35930                                 union_obj.requires_comptime = .yes;
  35931                                 return true;
  35932                             }
  35933                         }
  35934                         union_obj.requires_comptime = .no;
  35935                         return false;
  35936                     },
  35937                 }
  35938             },
  35939 
  35940             .opaque_type => false,
  35941             .enum_type => |enum_type| try sema.typeRequiresComptime(enum_type.tag_ty.toType()),
  35942 
  35943             // values, not types
  35944             .undef,
  35945             .runtime_value,
  35946             .simple_value,
  35947             .variable,
  35948             .extern_func,
  35949             .func,
  35950             .int,
  35951             .err,
  35952             .error_union,
  35953             .enum_literal,
  35954             .enum_tag,
  35955             .empty_enum_value,
  35956             .float,
  35957             .ptr,
  35958             .opt,
  35959             .aggregate,
  35960             .un,
  35961             // memoization, not types
  35962             .memoized_call,
  35963             => unreachable,
  35964         },
  35965     };
  35966 }
  35967 
  35968 pub fn typeHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {
  35969     const mod = sema.mod;
  35970     return ty.hasRuntimeBitsAdvanced(mod, false, .{ .sema = sema }) catch |err| switch (err) {
  35971         error.NeedLazy => unreachable,
  35972         else => |e| return e,
  35973     };
  35974 }
  35975 
  35976 fn typeAbiSize(sema: *Sema, ty: Type) !u64 {
  35977     try sema.resolveTypeLayout(ty);
  35978     return ty.abiSize(sema.mod);
  35979 }
  35980 
  35981 fn typeAbiAlignment(sema: *Sema, ty: Type) CompileError!u32 {
  35982     return (try ty.abiAlignmentAdvanced(sema.mod, .{ .sema = sema })).scalar;
  35983 }
  35984 
  35985 /// Not valid to call for packed unions.
  35986 /// Keep implementation in sync with `Module.Union.Field.normalAlignment`.
  35987 fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 {
  35988     return @as(u32, @intCast(if (field.ty.isNoReturn(sema.mod))
  35989         0
  35990     else
  35991         field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty)));
  35992 }
  35993 
  35994 /// Keep implementation in sync with `Module.Struct.Field.alignment`.
  35995 fn structFieldAlignment(sema: *Sema, field: Module.Struct.Field, layout: std.builtin.Type.ContainerLayout) !u32 {
  35996     const mod = sema.mod;
  35997     if (field.abi_align.toByteUnitsOptional()) |a| {
  35998         assert(layout != .Packed);
  35999         return @as(u32, @intCast(a));
  36000     }
  36001     switch (layout) {
  36002         .Packed => return 0,
  36003         .Auto => if (mod.getTarget().ofmt != .c) {
  36004             return sema.typeAbiAlignment(field.ty);
  36005         },
  36006         .Extern => {},
  36007     }
  36008     // extern
  36009     const ty_abi_align = try sema.typeAbiAlignment(field.ty);
  36010     if (field.ty.isAbiInt(mod) and field.ty.intInfo(mod).bits >= 128) {
  36011         return @max(ty_abi_align, 16);
  36012     }
  36013     return ty_abi_align;
  36014 }
  36015 
  36016 /// Synchronize logic with `Type.isFnOrHasRuntimeBits`.
  36017 pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {
  36018     const mod = sema.mod;
  36019     const fn_info = mod.typeToFunc(ty).?;
  36020     if (fn_info.is_generic) return false;
  36021     if (fn_info.is_var_args) return true;
  36022     switch (fn_info.cc) {
  36023         // If there was a comptime calling convention, it should also return false here.
  36024         .Inline => return false,
  36025         else => {},
  36026     }
  36027     if (try sema.typeRequiresComptime(fn_info.return_type.toType())) {
  36028         return false;
  36029     }
  36030     return true;
  36031 }
  36032 
  36033 fn unionFieldIndex(
  36034     sema: *Sema,
  36035     block: *Block,
  36036     unresolved_union_ty: Type,
  36037     field_name: InternPool.NullTerminatedString,
  36038     field_src: LazySrcLoc,
  36039 ) !u32 {
  36040     const mod = sema.mod;
  36041     const union_ty = try sema.resolveTypeFields(unresolved_union_ty);
  36042     const union_obj = mod.typeToUnion(union_ty).?;
  36043     const field_index_usize = union_obj.fields.getIndex(field_name) orelse
  36044         return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
  36045     return @as(u32, @intCast(field_index_usize));
  36046 }
  36047 
  36048 fn structFieldIndex(
  36049     sema: *Sema,
  36050     block: *Block,
  36051     unresolved_struct_ty: Type,
  36052     field_name: InternPool.NullTerminatedString,
  36053     field_src: LazySrcLoc,
  36054 ) !u32 {
  36055     const mod = sema.mod;
  36056     const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty);
  36057     if (struct_ty.isAnonStruct(mod)) {
  36058         return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src);
  36059     } else {
  36060         const struct_obj = mod.typeToStruct(struct_ty).?;
  36061         const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
  36062             return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
  36063         return @as(u32, @intCast(field_index_usize));
  36064     }
  36065 }
  36066 
  36067 fn anonStructFieldIndex(
  36068     sema: *Sema,
  36069     block: *Block,
  36070     struct_ty: Type,
  36071     field_name: InternPool.NullTerminatedString,
  36072     field_src: LazySrcLoc,
  36073 ) !u32 {
  36074     const mod = sema.mod;
  36075     switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) {
  36076         .anon_struct_type => |anon_struct_type| for (anon_struct_type.names, 0..) |name, i| {
  36077             if (name == field_name) return @as(u32, @intCast(i));
  36078         },
  36079         .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| {
  36080             for (struct_obj.fields.keys(), 0..) |name, i| {
  36081                 if (name == field_name) {
  36082                     return @as(u32, @intCast(i));
  36083                 }
  36084             }
  36085         },
  36086         else => unreachable,
  36087     }
  36088     return sema.fail(block, field_src, "no field named '{}' in anonymous struct '{}'", .{
  36089         field_name.fmt(&mod.intern_pool), struct_ty.fmt(sema.mod),
  36090     });
  36091 }
  36092 
  36093 fn queueFullTypeResolution(sema: *Sema, ty: Type) !void {
  36094     try sema.types_to_resolve.put(sema.gpa, ty.toIntern(), {});
  36095 }
  36096 
  36097 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
  36098 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
  36099 fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value {
  36100     var overflow: usize = undefined;
  36101     return sema.intAddInner(lhs, rhs, ty, &overflow) catch |err| switch (err) {
  36102         error.Overflow => {
  36103             const is_vec = ty.isVector(sema.mod);
  36104             overflow_idx.* = if (is_vec) overflow else 0;
  36105             const safe_ty = if (is_vec) try sema.mod.vectorType(.{
  36106                 .len = ty.vectorLen(sema.mod),
  36107                 .child = .comptime_int_type,
  36108             }) else Type.comptime_int;
  36109             return sema.intAddInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) {
  36110                 error.Overflow => unreachable,
  36111                 else => |e| return e,
  36112             };
  36113         },
  36114         else => |e| return e,
  36115     };
  36116 }
  36117 
  36118 fn intAddInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value {
  36119     const mod = sema.mod;
  36120     if (ty.zigTypeTag(mod) == .Vector) {
  36121         const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
  36122         const scalar_ty = ty.scalarType(mod);
  36123         for (result_data, 0..) |*scalar, i| {
  36124             const lhs_elem = try lhs.elemValue(mod, i);
  36125             const rhs_elem = try rhs.elemValue(mod, i);
  36126             const val = sema.intAddScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) {
  36127                 error.Overflow => {
  36128                     overflow_idx.* = i;
  36129                     return error.Overflow;
  36130                 },
  36131                 else => |e| return e,
  36132             };
  36133             scalar.* = try val.intern(scalar_ty, mod);
  36134         }
  36135         return (try mod.intern(.{ .aggregate = .{
  36136             .ty = ty.toIntern(),
  36137             .storage = .{ .elems = result_data },
  36138         } })).toValue();
  36139     }
  36140     return sema.intAddScalar(lhs, rhs, ty);
  36141 }
  36142 
  36143 fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
  36144     const mod = sema.mod;
  36145     if (scalar_ty.toIntern() != .comptime_int_type) {
  36146         const res = try sema.intAddWithOverflowScalar(lhs, rhs, scalar_ty);
  36147         if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
  36148         return res.wrapped_result;
  36149     }
  36150     // TODO is this a performance issue? maybe we should try the operation without
  36151     // resorting to BigInt first.
  36152     var lhs_space: Value.BigIntSpace = undefined;
  36153     var rhs_space: Value.BigIntSpace = undefined;
  36154     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  36155     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  36156     const limbs = try sema.arena.alloc(
  36157         std.math.big.Limb,
  36158         @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
  36159     );
  36160     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  36161     result_bigint.add(lhs_bigint, rhs_bigint);
  36162     return mod.intValue_big(scalar_ty, result_bigint.toConst());
  36163 }
  36164 
  36165 /// Supports both floats and ints; handles undefined.
  36166 fn numberAddWrapScalar(
  36167     sema: *Sema,
  36168     lhs: Value,
  36169     rhs: Value,
  36170     ty: Type,
  36171 ) !Value {
  36172     const mod = sema.mod;
  36173     if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
  36174 
  36175     if (ty.zigTypeTag(mod) == .ComptimeInt) {
  36176         return sema.intAdd(lhs, rhs, ty, undefined);
  36177     }
  36178 
  36179     if (ty.isAnyFloat()) {
  36180         return Value.floatAdd(lhs, rhs, ty, sema.arena, mod);
  36181     }
  36182 
  36183     const overflow_result = try sema.intAddWithOverflow(lhs, rhs, ty);
  36184     return overflow_result.wrapped_result;
  36185 }
  36186 
  36187 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
  36188 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
  36189 fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value {
  36190     var overflow: usize = undefined;
  36191     return sema.intSubInner(lhs, rhs, ty, &overflow) catch |err| switch (err) {
  36192         error.Overflow => {
  36193             const is_vec = ty.isVector(sema.mod);
  36194             overflow_idx.* = if (is_vec) overflow else 0;
  36195             const safe_ty = if (is_vec) try sema.mod.vectorType(.{
  36196                 .len = ty.vectorLen(sema.mod),
  36197                 .child = .comptime_int_type,
  36198             }) else Type.comptime_int;
  36199             return sema.intSubInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) {
  36200                 error.Overflow => unreachable,
  36201                 else => |e| return e,
  36202             };
  36203         },
  36204         else => |e| return e,
  36205     };
  36206 }
  36207 
  36208 fn intSubInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value {
  36209     const mod = sema.mod;
  36210     if (ty.zigTypeTag(mod) == .Vector) {
  36211         const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
  36212         const scalar_ty = ty.scalarType(mod);
  36213         for (result_data, 0..) |*scalar, i| {
  36214             const lhs_elem = try lhs.elemValue(sema.mod, i);
  36215             const rhs_elem = try rhs.elemValue(sema.mod, i);
  36216             const val = sema.intSubScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) {
  36217                 error.Overflow => {
  36218                     overflow_idx.* = i;
  36219                     return error.Overflow;
  36220                 },
  36221                 else => |e| return e,
  36222             };
  36223             scalar.* = try val.intern(scalar_ty, mod);
  36224         }
  36225         return (try mod.intern(.{ .aggregate = .{
  36226             .ty = ty.toIntern(),
  36227             .storage = .{ .elems = result_data },
  36228         } })).toValue();
  36229     }
  36230     return sema.intSubScalar(lhs, rhs, ty);
  36231 }
  36232 
  36233 fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
  36234     const mod = sema.mod;
  36235     if (scalar_ty.toIntern() != .comptime_int_type) {
  36236         const res = try sema.intSubWithOverflowScalar(lhs, rhs, scalar_ty);
  36237         if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
  36238         return res.wrapped_result;
  36239     }
  36240     // TODO is this a performance issue? maybe we should try the operation without
  36241     // resorting to BigInt first.
  36242     var lhs_space: Value.BigIntSpace = undefined;
  36243     var rhs_space: Value.BigIntSpace = undefined;
  36244     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  36245     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  36246     const limbs = try sema.arena.alloc(
  36247         std.math.big.Limb,
  36248         @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
  36249     );
  36250     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  36251     result_bigint.sub(lhs_bigint, rhs_bigint);
  36252     return mod.intValue_big(scalar_ty, result_bigint.toConst());
  36253 }
  36254 
  36255 /// Supports both floats and ints; handles undefined.
  36256 fn numberSubWrapScalar(
  36257     sema: *Sema,
  36258     lhs: Value,
  36259     rhs: Value,
  36260     ty: Type,
  36261 ) !Value {
  36262     const mod = sema.mod;
  36263     if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
  36264 
  36265     if (ty.zigTypeTag(mod) == .ComptimeInt) {
  36266         return sema.intSub(lhs, rhs, ty, undefined);
  36267     }
  36268 
  36269     if (ty.isAnyFloat()) {
  36270         return Value.floatSub(lhs, rhs, ty, sema.arena, mod);
  36271     }
  36272 
  36273     const overflow_result = try sema.intSubWithOverflow(lhs, rhs, ty);
  36274     return overflow_result.wrapped_result;
  36275 }
  36276 
  36277 fn intSubWithOverflow(
  36278     sema: *Sema,
  36279     lhs: Value,
  36280     rhs: Value,
  36281     ty: Type,
  36282 ) !Value.OverflowArithmeticResult {
  36283     const mod = sema.mod;
  36284     if (ty.zigTypeTag(mod) == .Vector) {
  36285         const vec_len = ty.vectorLen(mod);
  36286         const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len);
  36287         const result_data = try sema.arena.alloc(InternPool.Index, vec_len);
  36288         const scalar_ty = ty.scalarType(mod);
  36289         for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
  36290             const lhs_elem = try lhs.elemValue(sema.mod, i);
  36291             const rhs_elem = try rhs.elemValue(sema.mod, i);
  36292             const of_math_result = try sema.intSubWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty);
  36293             of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
  36294             scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
  36295         }
  36296         return Value.OverflowArithmeticResult{
  36297             .overflow_bit = (try mod.intern(.{ .aggregate = .{
  36298                 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
  36299                 .storage = .{ .elems = overflowed_data },
  36300             } })).toValue(),
  36301             .wrapped_result = (try mod.intern(.{ .aggregate = .{
  36302                 .ty = ty.toIntern(),
  36303                 .storage = .{ .elems = result_data },
  36304             } })).toValue(),
  36305         };
  36306     }
  36307     return sema.intSubWithOverflowScalar(lhs, rhs, ty);
  36308 }
  36309 
  36310 fn intSubWithOverflowScalar(
  36311     sema: *Sema,
  36312     lhs: Value,
  36313     rhs: Value,
  36314     ty: Type,
  36315 ) !Value.OverflowArithmeticResult {
  36316     const mod = sema.mod;
  36317     const info = ty.intInfo(mod);
  36318 
  36319     var lhs_space: Value.BigIntSpace = undefined;
  36320     var rhs_space: Value.BigIntSpace = undefined;
  36321     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  36322     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  36323     const limbs = try sema.arena.alloc(
  36324         std.math.big.Limb,
  36325         std.math.big.int.calcTwosCompLimbCount(info.bits),
  36326     );
  36327     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  36328     const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
  36329     const wrapped_result = try mod.intValue_big(ty, result_bigint.toConst());
  36330     return Value.OverflowArithmeticResult{
  36331         .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
  36332         .wrapped_result = wrapped_result,
  36333     };
  36334 }
  36335 
  36336 fn intFromFloat(
  36337     sema: *Sema,
  36338     block: *Block,
  36339     src: LazySrcLoc,
  36340     val: Value,
  36341     float_ty: Type,
  36342     int_ty: Type,
  36343 ) CompileError!Value {
  36344     const mod = sema.mod;
  36345     if (float_ty.zigTypeTag(mod) == .Vector) {
  36346         const elem_ty = float_ty.scalarType(mod);
  36347         const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(mod));
  36348         const scalar_ty = int_ty.scalarType(mod);
  36349         for (result_data, 0..) |*scalar, i| {
  36350             const elem_val = try val.elemValue(sema.mod, i);
  36351             scalar.* = try (try sema.intFromFloatScalar(block, src, elem_val, elem_ty, int_ty.scalarType(mod))).intern(scalar_ty, mod);
  36352         }
  36353         return (try mod.intern(.{ .aggregate = .{
  36354             .ty = int_ty.toIntern(),
  36355             .storage = .{ .elems = result_data },
  36356         } })).toValue();
  36357     }
  36358     return sema.intFromFloatScalar(block, src, val, float_ty, int_ty);
  36359 }
  36360 
  36361 // float is expected to be finite and non-NaN
  36362 fn float128IntPartToBigInt(
  36363     arena: Allocator,
  36364     float: f128,
  36365 ) !std.math.big.int.Managed {
  36366     const is_negative = std.math.signbit(float);
  36367     const floored = @floor(@fabs(float));
  36368 
  36369     var rational = try std.math.big.Rational.init(arena);
  36370     defer rational.q.deinit();
  36371     rational.setFloat(f128, floored) catch |err| switch (err) {
  36372         error.NonFiniteFloat => unreachable,
  36373         error.OutOfMemory => return error.OutOfMemory,
  36374     };
  36375 
  36376     // The float is reduced in rational.setFloat, so we assert that denominator is equal to one
  36377     const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
  36378     assert(rational.q.toConst().eqAbs(big_one));
  36379 
  36380     if (is_negative) {
  36381         rational.negate();
  36382     }
  36383     return rational.p;
  36384 }
  36385 
  36386 fn intFromFloatScalar(
  36387     sema: *Sema,
  36388     block: *Block,
  36389     src: LazySrcLoc,
  36390     val: Value,
  36391     float_ty: Type,
  36392     int_ty: Type,
  36393 ) CompileError!Value {
  36394     const mod = sema.mod;
  36395 
  36396     const float = val.toFloat(f128, mod);
  36397     if (std.math.isNan(float)) {
  36398         return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{
  36399             int_ty.fmt(sema.mod),
  36400         });
  36401     }
  36402     if (std.math.isInf(float)) {
  36403         return sema.fail(block, src, "float value Inf cannot be stored in integer type '{}'", .{
  36404             int_ty.fmt(sema.mod),
  36405         });
  36406     }
  36407 
  36408     var big_int = try float128IntPartToBigInt(sema.arena, float);
  36409     defer big_int.deinit();
  36410 
  36411     const cti_result = try mod.intValue_big(Type.comptime_int, big_int.toConst());
  36412 
  36413     if (!(try sema.intFitsInType(cti_result, int_ty, null))) {
  36414         return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{
  36415             val.fmtValue(float_ty, sema.mod), int_ty.fmt(sema.mod),
  36416         });
  36417     }
  36418     return mod.getCoerced(cti_result, int_ty);
  36419 }
  36420 
  36421 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
  36422 /// Vectors are also accepted. Vector results are reduced with AND.
  36423 ///
  36424 /// If provided, `vector_index` reports the first element that failed the range check.
  36425 fn intFitsInType(
  36426     sema: *Sema,
  36427     val: Value,
  36428     ty: Type,
  36429     vector_index: ?*usize,
  36430 ) CompileError!bool {
  36431     const mod = sema.mod;
  36432     if (ty.toIntern() == .comptime_int_type) return true;
  36433     const info = ty.intInfo(mod);
  36434     switch (val.toIntern()) {
  36435         .zero_usize, .zero_u8 => return true,
  36436         else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
  36437             .undef => return true,
  36438             .variable, .extern_func, .func, .ptr => {
  36439                 const target = mod.getTarget();
  36440                 const ptr_bits = target.ptrBitWidth();
  36441                 return switch (info.signedness) {
  36442                     .signed => info.bits > ptr_bits,
  36443                     .unsigned => info.bits >= ptr_bits,
  36444                 };
  36445             },
  36446             .int => |int| switch (int.storage) {
  36447                 .u64, .i64, .big_int => {
  36448                     var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
  36449                     const big_int = int.storage.toBigInt(&buffer);
  36450                     return big_int.fitsInTwosComp(info.signedness, info.bits);
  36451                 },
  36452                 .lazy_align => |lazy_ty| {
  36453                     const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed);
  36454                     // If it is u16 or bigger we know the alignment fits without resolving it.
  36455                     if (info.bits >= max_needed_bits) return true;
  36456                     const x = try sema.typeAbiAlignment(lazy_ty.toType());
  36457                     if (x == 0) return true;
  36458                     const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed);
  36459                     return info.bits >= actual_needed_bits;
  36460                 },
  36461                 .lazy_size => |lazy_ty| {
  36462                     const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed);
  36463                     // If it is u64 or bigger we know the size fits without resolving it.
  36464                     if (info.bits >= max_needed_bits) return true;
  36465                     const x = try sema.typeAbiSize(lazy_ty.toType());
  36466                     if (x == 0) return true;
  36467                     const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed);
  36468                     return info.bits >= actual_needed_bits;
  36469                 },
  36470             },
  36471             .aggregate => |aggregate| {
  36472                 assert(ty.zigTypeTag(mod) == .Vector);
  36473                 return switch (aggregate.storage) {
  36474                     .bytes => |bytes| for (bytes, 0..) |byte, i| {
  36475                         if (byte == 0) continue;
  36476                         const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed);
  36477                         if (info.bits >= actual_needed_bits) continue;
  36478                         if (vector_index) |vi| vi.* = i;
  36479                         break false;
  36480                     } else true,
  36481                     .elems, .repeated_elem => for (switch (aggregate.storage) {
  36482                         .bytes => unreachable,
  36483                         .elems => |elems| elems,
  36484                         .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem),
  36485                     }, 0..) |elem, i| {
  36486                         if (try sema.intFitsInType(elem.toValue(), ty.scalarType(mod), null)) continue;
  36487                         if (vector_index) |vi| vi.* = i;
  36488                         break false;
  36489                     } else true,
  36490                 };
  36491             },
  36492             else => unreachable,
  36493         },
  36494     }
  36495 }
  36496 
  36497 fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool {
  36498     const mod = sema.mod;
  36499     if (!(try int_val.compareAllWithZeroAdvanced(.gte, sema))) return false;
  36500     const end_val = try mod.intValue(tag_ty, end);
  36501     if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false;
  36502     return true;
  36503 }
  36504 
  36505 /// Asserts the type is an enum.
  36506 fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
  36507     const mod = sema.mod;
  36508     const enum_type = mod.intern_pool.indexToKey(ty.toIntern()).enum_type;
  36509     assert(enum_type.tag_mode != .nonexhaustive);
  36510     // The `tagValueIndex` function call below relies on the type being the integer tag type.
  36511     // `getCoerced` assumes the value will fit the new type.
  36512     if (!(try sema.intFitsInType(int, enum_type.tag_ty.toType(), null))) return false;
  36513     const int_coerced = try mod.getCoerced(int, enum_type.tag_ty.toType());
  36514 
  36515     return enum_type.tagValueIndex(&mod.intern_pool, int_coerced.toIntern()) != null;
  36516 }
  36517 
  36518 fn intAddWithOverflow(
  36519     sema: *Sema,
  36520     lhs: Value,
  36521     rhs: Value,
  36522     ty: Type,
  36523 ) !Value.OverflowArithmeticResult {
  36524     const mod = sema.mod;
  36525     if (ty.zigTypeTag(mod) == .Vector) {
  36526         const vec_len = ty.vectorLen(mod);
  36527         const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len);
  36528         const result_data = try sema.arena.alloc(InternPool.Index, vec_len);
  36529         const scalar_ty = ty.scalarType(mod);
  36530         for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
  36531             const lhs_elem = try lhs.elemValue(sema.mod, i);
  36532             const rhs_elem = try rhs.elemValue(sema.mod, i);
  36533             const of_math_result = try sema.intAddWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty);
  36534             of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
  36535             scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
  36536         }
  36537         return Value.OverflowArithmeticResult{
  36538             .overflow_bit = (try mod.intern(.{ .aggregate = .{
  36539                 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
  36540                 .storage = .{ .elems = overflowed_data },
  36541             } })).toValue(),
  36542             .wrapped_result = (try mod.intern(.{ .aggregate = .{
  36543                 .ty = ty.toIntern(),
  36544                 .storage = .{ .elems = result_data },
  36545             } })).toValue(),
  36546         };
  36547     }
  36548     return sema.intAddWithOverflowScalar(lhs, rhs, ty);
  36549 }
  36550 
  36551 fn intAddWithOverflowScalar(
  36552     sema: *Sema,
  36553     lhs: Value,
  36554     rhs: Value,
  36555     ty: Type,
  36556 ) !Value.OverflowArithmeticResult {
  36557     const mod = sema.mod;
  36558     const info = ty.intInfo(mod);
  36559 
  36560     var lhs_space: Value.BigIntSpace = undefined;
  36561     var rhs_space: Value.BigIntSpace = undefined;
  36562     const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema);
  36563     const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema);
  36564     const limbs = try sema.arena.alloc(
  36565         std.math.big.Limb,
  36566         std.math.big.int.calcTwosCompLimbCount(info.bits),
  36567     );
  36568     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
  36569     const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
  36570     const result = try mod.intValue_big(ty, result_bigint.toConst());
  36571     return Value.OverflowArithmeticResult{
  36572         .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
  36573         .wrapped_result = result,
  36574     };
  36575 }
  36576 
  36577 /// Asserts the values are comparable. Both operands have type `ty`.
  36578 /// For vectors, returns true if the comparison is true for ALL elements.
  36579 ///
  36580 /// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)`
  36581 fn compareAll(
  36582     sema: *Sema,
  36583     lhs: Value,
  36584     op: std.math.CompareOperator,
  36585     rhs: Value,
  36586     ty: Type,
  36587 ) CompileError!bool {
  36588     const mod = sema.mod;
  36589     if (ty.zigTypeTag(mod) == .Vector) {
  36590         var i: usize = 0;
  36591         while (i < ty.vectorLen(mod)) : (i += 1) {
  36592             const lhs_elem = try lhs.elemValue(sema.mod, i);
  36593             const rhs_elem = try rhs.elemValue(sema.mod, i);
  36594             if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)))) {
  36595                 return false;
  36596             }
  36597         }
  36598         return true;
  36599     }
  36600     return sema.compareScalar(lhs, op, rhs, ty);
  36601 }
  36602 
  36603 /// Asserts the values are comparable. Both operands have type `ty`.
  36604 fn compareScalar(
  36605     sema: *Sema,
  36606     lhs: Value,
  36607     op: std.math.CompareOperator,
  36608     rhs: Value,
  36609     ty: Type,
  36610 ) CompileError!bool {
  36611     const mod = sema.mod;
  36612     const coerced_lhs = try mod.getCoerced(lhs, ty);
  36613     const coerced_rhs = try mod.getCoerced(rhs, ty);
  36614     switch (op) {
  36615         .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty),
  36616         .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)),
  36617         else => return Value.compareHeteroAdvanced(coerced_lhs, op, coerced_rhs, mod, sema),
  36618     }
  36619 }
  36620 
  36621 fn valuesEqual(
  36622     sema: *Sema,
  36623     lhs: Value,
  36624     rhs: Value,
  36625     ty: Type,
  36626 ) CompileError!bool {
  36627     return lhs.eql(rhs, ty, sema.mod);
  36628 }
  36629 
  36630 /// Asserts the values are comparable vectors of type `ty`.
  36631 fn compareVector(
  36632     sema: *Sema,
  36633     lhs: Value,
  36634     op: std.math.CompareOperator,
  36635     rhs: Value,
  36636     ty: Type,
  36637 ) !Value {
  36638     const mod = sema.mod;
  36639     assert(ty.zigTypeTag(mod) == .Vector);
  36640     const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
  36641     for (result_data, 0..) |*scalar, i| {
  36642         const lhs_elem = try lhs.elemValue(sema.mod, i);
  36643         const rhs_elem = try rhs.elemValue(sema.mod, i);
  36644         const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod));
  36645         scalar.* = try Value.makeBool(res_bool).intern(Type.bool, mod);
  36646     }
  36647     return (try mod.intern(.{ .aggregate = .{
  36648         .ty = (try mod.vectorType(.{ .len = ty.vectorLen(mod), .child = .bool_type })).toIntern(),
  36649         .storage = .{ .elems = result_data },
  36650     } })).toValue();
  36651 }
  36652 
  36653 /// Returns the type of a pointer to an element.
  36654 /// Asserts that the type is a pointer, and that the element type is indexable.
  36655 /// For *[N]T, return *T
  36656 /// For [*]T, returns *T
  36657 /// For []T, returns *T
  36658 /// Handles const-ness and address spaces in particular.
  36659 /// This code is duplicated in `analyzePtrArithmetic`.
  36660 fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type {
  36661     const mod = sema.mod;
  36662     const ptr_info = ptr_ty.ptrInfo(mod);
  36663     const elem_ty = ptr_ty.elemType2(mod);
  36664     const is_allowzero = ptr_info.flags.is_allowzero and (offset orelse 0) == 0;
  36665     const parent_ty = ptr_ty.childType(mod);
  36666 
  36667     const VI = InternPool.Key.PtrType.VectorIndex;
  36668 
  36669     const vector_info: struct {
  36670         host_size: u16 = 0,
  36671         alignment: u32 = 0,
  36672         vector_index: VI = .none,
  36673     } = if (parent_ty.isVector(mod) and ptr_info.flags.size == .One) blk: {
  36674         const elem_bits = elem_ty.bitSize(mod);
  36675         if (elem_bits == 0) break :blk .{};
  36676         const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits);
  36677         if (!is_packed) break :blk .{};
  36678 
  36679         break :blk .{
  36680             .host_size = @as(u16, @intCast(parent_ty.arrayLen(mod))),
  36681             .alignment = @as(u32, @intCast(parent_ty.abiAlignment(mod))),
  36682             .vector_index = if (offset) |some| @as(VI, @enumFromInt(some)) else .runtime,
  36683         };
  36684     } else .{};
  36685 
  36686     const alignment: Alignment = a: {
  36687         // Calculate the new pointer alignment.
  36688         if (ptr_info.flags.alignment == .none) {
  36689             if (vector_info.alignment != 0) break :a Alignment.fromNonzeroByteUnits(vector_info.alignment);
  36690             // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
  36691             break :a .none;
  36692         }
  36693         // If the addend is not a comptime-known value we can still count on
  36694         // it being a multiple of the type size.
  36695         const elem_size = try sema.typeAbiSize(elem_ty);
  36696         const addend = if (offset) |off| elem_size * off else elem_size;
  36697 
  36698         // The resulting pointer is aligned to the lcd between the offset (an
  36699         // arbitrary number) and the alignment factor (always a power of two,
  36700         // non zero).
  36701         const new_align = @as(Alignment, @enumFromInt(@min(
  36702             @ctz(addend),
  36703             @intFromEnum(ptr_info.flags.alignment),
  36704         )));
  36705         assert(new_align != .none);
  36706         break :a new_align;
  36707     };
  36708     return mod.ptrType(.{
  36709         .child = elem_ty.toIntern(),
  36710         .flags = .{
  36711             .alignment = alignment,
  36712             .is_const = ptr_info.flags.is_const,
  36713             .is_volatile = ptr_info.flags.is_volatile,
  36714             .is_allowzero = is_allowzero,
  36715             .address_space = ptr_info.flags.address_space,
  36716             .vector_index = vector_info.vector_index,
  36717         },
  36718         .packed_offset = .{
  36719             .host_size = vector_info.host_size,
  36720             .bit_offset = 0,
  36721         },
  36722     });
  36723 }
  36724 
  36725 /// Merge lhs with rhs.
  36726 /// Asserts that lhs and rhs are both error sets and are resolved.
  36727 fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type {
  36728     const mod = sema.mod;
  36729     const arena = sema.arena;
  36730     const lhs_names = lhs.errorSetNames(mod);
  36731     const rhs_names = rhs.errorSetNames(mod);
  36732     var names: Module.Fn.InferredErrorSet.NameMap = .{};
  36733     try names.ensureUnusedCapacity(arena, lhs_names.len);
  36734 
  36735     for (lhs_names) |name| {
  36736         names.putAssumeCapacityNoClobber(name, {});
  36737     }
  36738     for (rhs_names) |name| {
  36739         try names.put(arena, name, {});
  36740     }
  36741 
  36742     return mod.errorSetFromUnsortedNames(names.keys());
  36743 }
  36744 
  36745 /// Avoids crashing the compiler when asking if inferred allocations are noreturn.
  36746 fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool {
  36747     if (ref == .unreachable_value) return true;
  36748     if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) {
  36749         .inferred_alloc, .inferred_alloc_comptime => return false,
  36750         else => {},
  36751     };
  36752     return sema.typeOf(ref).isNoReturn(sema.mod);
  36753 }
  36754 
  36755 /// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type.
  36756 fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool {
  36757     if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) {
  36758         .inferred_alloc, .inferred_alloc_comptime => return false,
  36759         else => {},
  36760     };
  36761     return sema.typeOf(ref).zigTypeTag(sema.mod) == tag;
  36762 }